-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
155 lines (136 loc) · 5.11 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Be sure to add these ENV variables!
const {
MOONCLERK_API_KEY,
KEYGEN_PRODUCT_TOKEN,
KEYGEN_ACCOUNT_ID,
KEYGEN_POLICY_ID,
PORT = 8080
} = process.env
const fetch = require('node-fetch')
const crypto = require('crypto')
const express = require('express')
const bodyParser = require('body-parser')
const morgan = require('morgan')
const app = express()
app.use(bodyParser.json({ type: 'application/vnd.moonclerk+json' }))
app.use(bodyParser.json({ type: 'application/vnd.api+json' }))
app.use(bodyParser.json({ type: 'application/json' }))
app.use(morgan('combined'))
app.set('view engine', 'ejs')
// 1. Our MoonClerk checkout form will redirect here after a successful purchase. Inside
// this route, we'll verify that the passed customer/payment is valid within MoonClerk
// and then create a Keygen license resource. After that has successfully been done,
// we'll render a 'success' page containing our user's license key which they can
// use inside of our software product, e.g.:
//
// curl -X POST https://api.keygen.sh/v1/accounts/$KEYGEN_ACCOUNT_ID/licenses/actions/validate-key \
// -H 'Content-Type: application/vnd.api+json' \
// -H 'Accept: application/vnd.api+json' \
// -d '{
// "meta": {
// "key": "$KEYGEN_LICENSE_KEY"
// }
// }'
app.get('/success', async (req, res) => {
const { query } = req
// If we aren't supplied with a customer ID or a payment ID, the request is invalid.
if (!query.customer_id && !query.payment_id) {
res.render('error', {
error: 'Missing payment details'
})
return
}
// 2. Fetch the MoonClerk resource to make sure our request is valid. We'll get back
// a customer resource if the licensee was charged for a subscription, otherwise
// we'll get back a payment resource for a one-time purchase.
let customer
let payment
if (query.customer_id) {
const mres = await fetch(`https://api.moonclerk.com/customers/${query.customer_id}`, {
method: 'GET',
headers: {
'Authorization': `Token token=${MOONCLERK_API_KEY}`,
'Accept': 'application/vnd.moonclerk+json;version=1'
}
})
if (mres.status !== 200) { // Invalid! Bail early before we create a license.
res.render('error', {
error: 'Invalid customer ID'
})
return
}
customer = await mres.json()
} else {
const mres = await fetch(`https://api.moonclerk.com/payments/${query.payment_id}`, {
method: 'GET',
headers: {
'Authorization': `Token token=${MOONCLERK_API_KEY}`,
'Accept': 'application/vnd.moonclerk+json;version=1'
}
})
if (mres.status !== 200) { // Invalid! Bail early before we create a license.
res.render('error', {
error: 'Invalid payment ID'
})
return
}
payment = await mres.json()
}
// 3. Create a user-less Keygen license for our new MoonClerk customer.
const kreq = await fetch(`https://api.keygen.sh/v1/accounts/${KEYGEN_ACCOUNT_ID}/licenses`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${KEYGEN_PRODUCT_TOKEN}`,
'Content-Type': 'application/vnd.api+json',
'Accept': 'application/vnd.api+json'
},
body: JSON.stringify({
data: {
type: 'licenses',
attributes: {
// Generate a short license key in the form of 'XXXX-XXXX-XXXX-XXXX' that we can
// send to our customer via email and display on the success page.
key: crypto.randomBytes(8).toString('hex').split(/(.{4})/).filter(Boolean).join('-'),
metadata: {
// One of these fields will be populated depending on if this was a one-time
// charge or a subscription. We're storing both to be sure.
moonclerkCustomerId: query.customer_id,
moonclerkPaymentId: query.payment_id
}
},
relationships: {
policy: {
data: { type: 'policies', id: KEYGEN_POLICY_ID }
}
}
}
})
})
const { data: license, errors } = await kreq.json()
if (errors) {
const error = errors.map(e => e.detail).toString()
// If you receive an error here, then you may want to handle the fact the customer
// may have been charged for a license that they didn't receive e.g. easiest way
// would be to create the license manually, or refund their payment.
console.error(`Received error while creating license for ${JSON.stringify(query)}:\n ${error}`)
res.render('error', { error })
return
}
// 4. All is good! License was successfully created for the new MoonClerk customer.
// Next up would be for us to email the license key to our customer's email
// using `customer.email` or `payment.email`.
// 5. Render our success page with the new license resource.
res.render('success', {
license
})
})
app.get('/', async (req, res) => {
res.render('index')
})
process.on('unhandledRejection', err => {
console.error(`Unhandled rejection: ${err}`, err.stack)
})
const server = app.listen(PORT, 'localhost', () => {
const { address, port } = server.address()
console.log(`Listening at http://${address}:${port}`)
})