@live-change/stripe-service 0.8.109 → 0.8.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -4,6 +4,7 @@ const app = App.app()
4
4
  import definition from './definition.js'
5
5
 
6
6
  import "./payment.js"
7
+ import "./stripeEvents.js"
7
8
  import "./webhook.js"
8
9
 
9
10
  export default definition
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/stripe-service",
3
- "version": "0.8.109",
3
+ "version": "0.8.111",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,14 +21,14 @@
21
21
  "url": "https://www.viamage.com/"
22
22
  },
23
23
  "dependencies": {
24
- "@live-change/framework": "^0.8.109",
25
- "@live-change/relations-plugin": "^0.8.109",
24
+ "@live-change/framework": "^0.8.111",
25
+ "@live-change/relations-plugin": "^0.8.111",
26
26
  "lru-cache": "^7.12.0",
27
27
  "pluralize": "^8.0.0",
28
28
  "progress-stream": "^2.0.0",
29
29
  "prosemirror-model": "^1.18.1",
30
30
  "stripe": "^17.2.1"
31
31
  },
32
- "gitHead": "075bf3759fae6c40fb8e44d77d9f12c5c175e098",
32
+ "gitHead": "9b96afb2fc61ab3d2a5d143924e2c56d411280b4",
33
33
  "type": "module"
34
34
  }
package/payment.js CHANGED
@@ -4,38 +4,38 @@ const app = App.app()
4
4
  import definition from './definition.js'
5
5
  import config from './config.js'
6
6
 
7
- import stripe from 'stripe'
7
+ import stripe from "./stripeClient.js"
8
8
 
9
9
  const itemsArray = {
10
- type: Array,
11
- of: {
12
- type: Object,
13
- properties: {
14
- name: {
15
- type: String,
16
- validation: ['nonEmpty']
17
- },
18
- price: {
19
- type: Number,
20
- validation: ['nonEmpty', 'number']
21
- },
22
- currency: {
23
- type: String,
24
- validation: ['nonEmpty']
25
- },
26
- quantity: {
27
- type: Number,
28
- validation: ['nonEmpty', 'number']
29
- },
30
- description: {
31
- type: String
32
- },
33
- taxCode: {
34
- type: String
35
- }
10
+ type: Array,
11
+ of: {
12
+ type: Object,
13
+ properties: {
14
+ name: {
15
+ type: String,
16
+ validation: ['nonEmpty']
17
+ },
18
+ price: {
19
+ type: Number,
20
+ validation: ['nonEmpty', 'number']
21
+ },
22
+ currency: {
23
+ type: String,
24
+ validation: ['nonEmpty']
25
+ },
26
+ quantity: {
27
+ type: Number,
28
+ validation: ['nonEmpty', 'number']
29
+ },
30
+ description: {
31
+ type: String
32
+ },
33
+ taxCode: {
34
+ type: String
36
35
  }
37
36
  }
38
37
  }
38
+ }
39
39
 
40
40
  const paymentParameters = {
41
41
  items: itemsArray,
@@ -54,10 +54,18 @@ const paymentParameters = {
54
54
  cancelUrl: {
55
55
  type: String,
56
56
  validation: ['nonEmpty']
57
+ },
58
+ payerType: {
59
+ type: String,
60
+ validation: ['nonEmpty']
61
+ },
62
+ payer: {
63
+ type: String,
64
+ validation: ['nonEmpty']
57
65
  }
58
66
  }
59
67
 
60
- definition.model({
68
+ const Payment = definition.model({
61
69
  name: "Payment",
62
70
  entity:{
63
71
  },
@@ -65,7 +73,7 @@ definition.model({
65
73
  ...paymentParameters,
66
74
  state: {
67
75
  type: String,
68
- options: ['created', 'succeeded', 'failed', 'refunded']
76
+ options: ['created', 'pending', 'succeeded', 'failed', 'refunded']
69
77
  },
70
78
  stripeSession: {
71
79
  type: String
@@ -75,21 +83,33 @@ definition.model({
75
83
  }
76
84
  })
77
85
 
86
+ export { Payment}
87
+
78
88
  definition.trigger({
79
89
  name: 'startPayment',
80
90
  properties: {
81
91
  ...paymentParameters
82
92
  },
83
- async execute({ items, causeType, cause, successUrl, cancelUrl }, { client, service }, emit) {
84
- console.log("STRIPE SECRET KEY", config.secretKey)
93
+ waitForEvents: true,
94
+ async execute({ items, causeType, cause, successUrl, cancelUrl, payerType, payer }, { service }, emit) {
85
95
  const payment = app.generateUid()
86
- const stripeClient = stripe(config.secretKey)
96
+ if(!successUrl) successUrl = config.checkoutSuccessUrl + '/' + payment.replace(/\[/g, '(').replace(/]/g, ')')
97
+ if(!cancelUrl) cancelUrl = config.checkoutCancelUrl + '/' + payment.replace(/\[/g, '(').replace(/]/g, ')')
87
98
  console.log("URLS", {
88
- success_url: successUrl ?? (config.checkoutSuccessUrl + '/' + payment),
89
- cancel_url: cancelUrl ?? (config.checkoutCancelUrl + '/' + payment)
99
+ success_url: successUrl,
100
+ cancel_url: cancelUrl,
90
101
  })
91
- const session = await stripeClient.checkout.sessions.create({
102
+ const metadata = {
103
+ type: 'payment',
104
+ payment
105
+ }
106
+ const session = await stripe.checkout.sessions.create({
92
107
  payment_method_types: ['card'],
108
+ client_reference_id: 'payment:'+payment,
109
+ // customer: // store stripe customer id ?
110
+ // customer_creation: 'always'
111
+ /// TODO:
112
+ // customer_email: /// get from contacts
93
113
  line_items: items.map(item => ({
94
114
  price_data: {
95
115
  currency: item.currency,
@@ -102,15 +122,23 @@ definition.trigger({
102
122
  },
103
123
  quantity: item.quantity
104
124
  })),
125
+ invoice_creation: {
126
+ enabled: true
127
+ },
128
+ metadata,
129
+ payment_intent_data: {
130
+ metadata
131
+ },
105
132
  mode: 'payment',
106
- success_url: successUrl ?? (config.checkoutSuccessUrl + '/' + payment),
107
- cancel_url: cancelUrl ?? (config.checkoutCancelUrl + '/' + payment)
133
+ success_url: successUrl,
134
+ cancel_url: cancelUrl,
108
135
  })
109
136
  emit({
110
137
  type: 'PaymentCreated',
111
138
  payment,
112
139
  data: {
113
- items, causeType, cause,
140
+ items, causeType, cause, payerType, payer,
141
+ successUrl, cancelUrl,
114
142
  stripeSession: session.id,
115
143
  state: 'created'
116
144
  }
@@ -0,0 +1,8 @@
1
+ import stripe from 'stripe'
2
+
3
+ import config from './config.js'
4
+
5
+ console.log("STRIPE SECRET KEY", config.secretKey)
6
+ const stripeClient = stripe(config.secretKey)
7
+
8
+ export default stripeClient
@@ -0,0 +1,323 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+
4
+ import definition from './definition.js'
5
+ import config from './config.js'
6
+
7
+ import stripe from "./stripeClient.js"
8
+
9
+ import { Payment } from './payment.js'
10
+
11
+ definition.trigger({
12
+ name: 'stripeEvent.checkout.session.completed',
13
+ properties: {
14
+ data: {
15
+ type: Object,
16
+ properties: {
17
+ object: {
18
+ type: Object,
19
+ properties: {
20
+ client_reference_id: {
21
+ type: String,
22
+ validation: ['nonEmpty']
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ },
29
+ async execute({ data: { object } }, { triggerService, trigger }, emit) {
30
+ //console.log('stripeEvent.checkout.session.completed', object)
31
+ const id = object.client_reference_id
32
+ const referenceType = id.slice(0, id.indexOf(':'))
33
+ const referenceId = id.slice(id.indexOf(':') + 1)
34
+ if(referenceType === 'payment') {
35
+ const payment = await Payment.get(referenceId)
36
+ if(!payment) throw new Error('Payment '+referenceId+' not found')
37
+ if(object.payment_status === 'paid') {
38
+ console.log("PAYMENT TO UPDATE", payment)
39
+ await triggerService({
40
+ service: definition.name,
41
+ type: 'stripe_updatePayment'
42
+ }, {
43
+ payment: referenceId,
44
+ state: 'succeeded'
45
+ })
46
+ const triggerData = {
47
+ paymentType: 'stripe_Payment',
48
+ payment: referenceId,
49
+ causeType: payment.causeType,
50
+ cause: payment.cause,
51
+ payerType: payment.payerType,
52
+ payer: payment.payer,
53
+ items: payment.items
54
+ }
55
+ await trigger({
56
+ type: 'paymentSucceeded_'+payment.causeType,
57
+ }, triggerData)
58
+ await trigger({
59
+ type: 'paymentSucceeded',
60
+ }, triggerData)
61
+ } else if(object.payment_status === 'unpaid') {
62
+ await triggerService({
63
+ service: definition.name,
64
+ type: 'stripe_updatePayment'
65
+ }, {
66
+ payment: referenceId,
67
+ state: 'pending'
68
+ })
69
+ }
70
+ } else {
71
+ // Ignore
72
+ }
73
+
74
+ }
75
+ })
76
+
77
+
78
+ definition.trigger({
79
+ name: 'stripeEvent.checkout.session.expired',
80
+ properties: {
81
+ data: {
82
+ type: Object,
83
+ properties: {
84
+ object: {
85
+ type: Object,
86
+ properties: {
87
+ client_reference_id: {
88
+ type: String,
89
+ validation: ['nonEmpty']
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ },
96
+ async execute({ data: { object } }, { triggerService, trigger }, emit) {
97
+ ///console.log('stripeEvent.checkout.session.expired', object)
98
+ const id = object.client_reference_id
99
+ const referenceType = id.slice(0, id.indexOf(':'))
100
+ const referenceId = id.slice(id.indexOf(':') + 1)
101
+ if(referenceType === 'payment') {
102
+ const payment = await Payment.get(referenceId)
103
+ if(!payment) throw new Error('Payment '+referenceId+' not found')
104
+ await triggerService({
105
+ type: 'stripe_updatePayment'
106
+ }, {
107
+ payment: referenceId,
108
+ state: 'failed'
109
+ })
110
+ const triggerData = {
111
+ paymentType: 'stripe_Payment',
112
+ payment: referenceId,
113
+ causeType: payment.causeType,
114
+ cause: payment.cause,
115
+ payerType: payment.payerType,
116
+ payer: payment.payer,
117
+ items: payment.items
118
+ }
119
+ await trigger({
120
+ type: 'paymentFailed_'+payment.causeType,
121
+ }, triggerData)
122
+ await trigger({
123
+ type: 'paymentFailed',
124
+ }, triggerData)
125
+ } else {
126
+ // Ignore
127
+ }
128
+
129
+ }
130
+ })
131
+
132
+
133
+ definition.trigger({
134
+ name: 'stripeEvent.checkout.session.async_payment_succeeded',
135
+ properties: {
136
+ data: {
137
+ type: Object,
138
+ properties: {
139
+ object: {
140
+ type: Object,
141
+ properties: {
142
+ client_reference_id: {
143
+ type: String,
144
+ validation: ['nonEmpty']
145
+ }
146
+ }
147
+ }
148
+ }
149
+ }
150
+ },
151
+ async execute({ data: { object } }, { triggerService, trigger }, emit) {
152
+ //console.log('stripeEvent.checkout.session.async_payment_succeeded', object)
153
+ const id = object.client_reference_id
154
+ const referenceType = id.slice(0, id.indexOf(':'))
155
+ const referenceId = id.slice(id.indexOf(':') + 1)
156
+ if(referenceType === 'payment') {
157
+ const payment = await Payment.get(referenceId)
158
+ if(!payment) throw new Error('Payment '+referenceId+' not found')
159
+ if(object.payment_status === 'paid') {
160
+ await triggerService({
161
+ type: 'stripe_updatePayment'
162
+ }, {
163
+ payment: referenceId,
164
+ state: 'succeeded'
165
+ })
166
+ const triggerData = {
167
+ paymentType: 'stripe_Payment',
168
+ payment: referenceId,
169
+ causeType: payment.causeType,
170
+ cause: payment.cause,
171
+ payerType: payment.payerType,
172
+ payer: payment.payer,
173
+ items: payment.items
174
+ }
175
+ await trigger({
176
+ type: 'paymentSucceeded_'+payment.causeType,
177
+ }, triggerData)
178
+ await trigger({
179
+ type: 'paymentSucceeded',
180
+ }, triggerData)
181
+ } else if(object.payment_status === 'unpaid') {
182
+ await triggerService({
183
+ type: 'stripe_updatePayment'
184
+ }, {
185
+ payment: referenceId,
186
+ state: 'pending'
187
+ })
188
+ }
189
+ } else {
190
+ // Ignore
191
+ }
192
+
193
+ }
194
+ })
195
+
196
+
197
+ definition.trigger({
198
+ name: 'stripeEvent.checkout.session.async_payment_failed',
199
+ properties: {
200
+ data: {
201
+ type: Object,
202
+ properties: {
203
+ object: {
204
+ type: Object,
205
+ properties: {
206
+ client_reference_id: {
207
+ type: String,
208
+ validation: ['nonEmpty']
209
+ }
210
+ }
211
+ }
212
+ }
213
+ }
214
+ },
215
+ async execute({ data: { object } }, { triggerService, trigger }, emit) {
216
+ //console.log('stripeEvent.checkout.session.async_payment_failed', object)
217
+ const id = object.client_reference_id
218
+ const referenceType = id.slice(0, id.indexOf(':'))
219
+ const referenceId = id.slice(id.indexOf(':') + 1)
220
+ if(referenceType === 'payment') {
221
+ const payment = await Payment.get(referenceId)
222
+ if(!payment) throw new Error('Payment '+referenceId+' not found')
223
+ await triggerService({
224
+ type: 'stripe_updatePayment'
225
+ }, {
226
+ payment: referenceId,
227
+ state: 'failed'
228
+ })
229
+ const triggerData = {
230
+ paymentType: 'stripe_Payment',
231
+ payment: referenceId,
232
+ causeType: payment.causeType,
233
+ cause: payment.cause,
234
+ payerType: payment.payerType,
235
+ payer: payment.payer,
236
+ items: payment.items
237
+ }
238
+ await trigger({
239
+ type: 'paymentFailed_'+payment.causeType,
240
+ }, triggerData)
241
+ await trigger({
242
+ type: 'paymentFailed',
243
+ }, triggerData)
244
+ } else {
245
+ // Ignore
246
+ }
247
+
248
+ }
249
+ })
250
+
251
+ definition.trigger({
252
+ name: 'stripeEvent.charge.succeeded',
253
+ properties: {
254
+ data: {
255
+ type: Object,
256
+ properties: {
257
+ object: {
258
+ type: Object
259
+ }
260
+ }
261
+ }
262
+ },
263
+ async execute({ data: { object } }, { triggerService, trigger }, emit) {
264
+ console.log('charge.succeeded', JSON.stringify(object, null, 2))
265
+ if(object.metadata?.type === 'payment') {
266
+ const payment = await Payment.get(object.metadata.payment)
267
+ const triggerData = {
268
+ paymentType: 'stripe_Payment',
269
+ payment: payment.id,
270
+ causeType: payment.causeType,
271
+ cause: payment.cause,
272
+ payerType: payment.payerType,
273
+ payer: payment.payer,
274
+ items: payment.items
275
+ }
276
+ await trigger({
277
+ type: 'chargeCollected_'+payment.causeType
278
+ }, triggerData)
279
+ await trigger({
280
+ type: 'chargeCollected'
281
+ }, triggerData)
282
+ } else {
283
+ // Ignore
284
+ }
285
+ }
286
+ })
287
+
288
+ definition.trigger({
289
+ name: 'stripeEvent.charge.refunded',
290
+ properties: {
291
+ data: {
292
+ type: Object,
293
+ properties: {
294
+ object: {
295
+ type: Object
296
+ }
297
+ }
298
+ }
299
+ },
300
+ async execute({ data: { object } }, { triggerService, trigger }, emit) {
301
+ console.log('charge.refunded', JSON.stringify(object, null, 2))
302
+ if(object.metadata?.type === 'payment') {
303
+ const payment = await Payment.get(object.metadata.payment)
304
+ const triggerData = {
305
+ paymentType: 'stripe_Payment',
306
+ payment: payment.id,
307
+ causeType: payment.causeType,
308
+ cause: payment.cause,
309
+ payerType: payment.payerType,
310
+ payer: payment.payer,
311
+ items: payment.items
312
+ }
313
+ await trigger({
314
+ type: 'chargeRefunded_'+payment.causeType,
315
+ }, triggerData)
316
+ await trigger({
317
+ type: 'chargeRefunded',
318
+ }, triggerData)
319
+ } else {
320
+ // Ignore
321
+ }
322
+ }
323
+ })
package/webhook.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import App from '@live-change/framework'
2
2
  const app = App.app()
3
3
  import definition from './definition.js'
4
+ import config from './config.js'
5
+
6
+ import { encodeDate } from "@live-change/uid"
4
7
 
5
8
  import crypto from "crypto"
6
9
  import express from "express"
@@ -18,11 +21,17 @@ definition.endpoint({
18
21
  })
19
22
  expressApp.post('/', express.raw({type: 'application/json'}), async (request, response) => {
20
23
  try {
21
- const event = stripe.webhooks.constructEvent(request.body, sig, config.webhookSecret)
22
- console.log("STRIPE WEBHOOK", event)
23
- await app.triggerService({
24
- service: 'stripe',
25
- type: `stripe.${event.type}`
24
+ const signature = request.headers['stripe-signature']
25
+ const event = stripe.webhooks.constructEvent(request.body, signature, config.webhookSecret)
26
+ console.log("STRIPE WEBHOOK", event.type)//, JSON.stringify(event))
27
+ const { id, created } = event
28
+ const eventIdEncoded = `[${encodeDate(new Date(created*1000))}.${id}@stripe]`
29
+ await app.trigger({
30
+ // service: 'stripe',
31
+ type: `stripeEvent.${event.type}`,
32
+ id: eventIdEncoded // needed for deduplication
33
+ }, {
34
+ ...event
26
35
  })
27
36
  response.json({ received: true })
28
37
  } catch (err) {