@sails-pay/bachs 0.0.6 → 0.0.8
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/helpers/parameters.js +2 -1
- package/helpers/payloads.js +5 -28
- package/machines/checkout.js +19 -23
- package/package.json +1 -1
- package/test/checkout.test.js +34 -5
- package/test/payloads.test.js +0 -72
package/helpers/parameters.js
CHANGED
|
@@ -37,7 +37,8 @@ module.exports = {
|
|
|
37
37
|
BACHS_SUCCESS_URL: {
|
|
38
38
|
type: 'string',
|
|
39
39
|
friendlyName: 'Success URL',
|
|
40
|
-
description:
|
|
40
|
+
description:
|
|
41
|
+
'Default URL Bachs redirects to after payment. Alias for returnUrl.'
|
|
41
42
|
},
|
|
42
43
|
BACHS_CANCEL_URL: {
|
|
43
44
|
type: 'string',
|
package/helpers/payloads.js
CHANGED
|
@@ -27,7 +27,7 @@ function buildCustomerPayload(inputs) {
|
|
|
27
27
|
})
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
const details = withoutUndefined({
|
|
31
31
|
email:
|
|
32
32
|
customer.email ||
|
|
33
33
|
inputs.customerEmail ||
|
|
@@ -37,6 +37,9 @@ function buildCustomerPayload(inputs) {
|
|
|
37
37
|
customer.name || inputs.customerName || inputs.name || checkoutData.name,
|
|
38
38
|
phone_number: customer.phoneNumber || inputs.phoneNumber
|
|
39
39
|
})
|
|
40
|
+
|
|
41
|
+
// Omit an absent identity so hosted checkout can collect guest details.
|
|
42
|
+
return Object.keys(details).length ? details : undefined
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
function buildProductCart(items) {
|
|
@@ -88,6 +91,7 @@ function buildCheckoutSessionPayload(inputs, adapterConfig = {}) {
|
|
|
88
91
|
cancel_url: inputs.cancelUrl || adapterConfig.cancelUrl,
|
|
89
92
|
reference: inputs.reference,
|
|
90
93
|
metadata: inputs.metadata || checkoutData.custom,
|
|
94
|
+
expires_in_minutes: inputs.expiresInMinutes,
|
|
91
95
|
transfer_data: inputs.connect && {
|
|
92
96
|
destination: inputs.connect.destination
|
|
93
97
|
},
|
|
@@ -135,32 +139,6 @@ function buildPricingPayload(inputs) {
|
|
|
135
139
|
})
|
|
136
140
|
}
|
|
137
141
|
|
|
138
|
-
function buildPureCheckoutPayload(inputs, adapterConfig = {}) {
|
|
139
|
-
const checkoutData = inputs.checkoutData || {}
|
|
140
|
-
const customer = inputs.customer || {}
|
|
141
|
-
|
|
142
|
-
return withoutUndefined({
|
|
143
|
-
pricing: buildPricingPayload(inputs),
|
|
144
|
-
customer_email:
|
|
145
|
-
inputs.customerEmail ||
|
|
146
|
-
inputs.email ||
|
|
147
|
-
customer.email ||
|
|
148
|
-
checkoutData.email,
|
|
149
|
-
customer_name:
|
|
150
|
-
inputs.customerName || inputs.name || customer.name || checkoutData.name,
|
|
151
|
-
success_url:
|
|
152
|
-
inputs.successUrl ||
|
|
153
|
-
inputs.returnUrl ||
|
|
154
|
-
adapterConfig.successUrl ||
|
|
155
|
-
adapterConfig.returnUrl,
|
|
156
|
-
cancel_url: inputs.cancelUrl || adapterConfig.cancelUrl,
|
|
157
|
-
reference: inputs.reference,
|
|
158
|
-
metadata: inputs.metadata || checkoutData.custom,
|
|
159
|
-
expires_in_minutes: inputs.expiresInMinutes,
|
|
160
|
-
simulated_outcome: inputs.simulatedOutcome
|
|
161
|
-
})
|
|
162
|
-
}
|
|
163
|
-
|
|
164
142
|
function buildRefundPayload(inputs) {
|
|
165
143
|
return withoutUndefined({
|
|
166
144
|
charge_id: inputs.chargeId,
|
|
@@ -176,7 +154,6 @@ function buildRefundPayload(inputs) {
|
|
|
176
154
|
|
|
177
155
|
module.exports = {
|
|
178
156
|
buildCheckoutSessionPayload,
|
|
179
|
-
buildPureCheckoutPayload,
|
|
180
157
|
buildRefundPayload,
|
|
181
158
|
buildProductCart,
|
|
182
159
|
buildCustomerPayload,
|
package/machines/checkout.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
const fetch = require('../helpers/fetch')
|
|
2
|
-
const {
|
|
3
|
-
buildCheckoutSessionPayload,
|
|
4
|
-
buildPureCheckoutPayload
|
|
5
|
-
} = require('../helpers/payloads')
|
|
2
|
+
const { buildCheckoutSessionPayload } = require('../helpers/payloads')
|
|
6
3
|
const validateCheckoutItems = require('../helpers/validate-checkout-items')
|
|
7
4
|
const validateCheckoutConnect = require('../helpers/validate-checkout-connect')
|
|
8
5
|
const parameters = require('../helpers/parameters')
|
|
@@ -46,8 +43,7 @@ module.exports = require('machine').build({
|
|
|
46
43
|
},
|
|
47
44
|
successUrl: {
|
|
48
45
|
type: 'string',
|
|
49
|
-
description:
|
|
50
|
-
'URL Bachs redirects to after a pure checkout payment. Maps to success_url.'
|
|
46
|
+
description: 'Alias for returnUrl.'
|
|
51
47
|
},
|
|
52
48
|
cancelUrl: parameters.BACHS_CANCEL_URL,
|
|
53
49
|
customer: {
|
|
@@ -96,23 +92,27 @@ module.exports = require('machine').build({
|
|
|
96
92
|
},
|
|
97
93
|
pricing: {
|
|
98
94
|
type: 'ref',
|
|
99
|
-
description:
|
|
95
|
+
description:
|
|
96
|
+
'Pricing for a checkout that sets its own price, instead of products.'
|
|
100
97
|
},
|
|
101
98
|
amount: {
|
|
102
99
|
type: 'string',
|
|
103
|
-
description:
|
|
100
|
+
description:
|
|
101
|
+
'Amount for a checkout that sets its own price, e.g. "50.00".'
|
|
104
102
|
},
|
|
105
103
|
currency: {
|
|
106
104
|
type: 'string',
|
|
107
|
-
description:
|
|
105
|
+
description:
|
|
106
|
+
'Currency for a checkout that sets its own price, e.g. "USD" or "NGN".'
|
|
108
107
|
},
|
|
109
108
|
currencyOptions: {
|
|
110
109
|
type: 'ref',
|
|
111
|
-
description:
|
|
110
|
+
description:
|
|
111
|
+
'Per-currency amount overrides for a checkout that sets its own price.'
|
|
112
112
|
},
|
|
113
113
|
expiresInMinutes: {
|
|
114
114
|
type: 'number',
|
|
115
|
-
description: '
|
|
115
|
+
description: 'Checkout expiry in minutes.'
|
|
116
116
|
},
|
|
117
117
|
simulatedOutcome: {
|
|
118
118
|
type: 'string',
|
|
@@ -150,13 +150,12 @@ module.exports = require('machine').build({
|
|
|
150
150
|
const hasPricing = Boolean(
|
|
151
151
|
inputs.pricing || inputs.amount !== undefined || inputs.currency
|
|
152
152
|
)
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
// states its own amount and currency
|
|
153
|
+
// Bachs checks out through sessions. A session takes products or a raw
|
|
154
|
+
// price, and it permits a split rather than requiring one, so a checkout
|
|
155
|
+
// that states its own amount and currency belongs here whether or not it
|
|
156
|
+
// pays a connected account.
|
|
156
157
|
const shouldUseCheckoutSession =
|
|
157
|
-
hasItems ||
|
|
158
|
-
hasProductCollection ||
|
|
159
|
-
(hasPricing && inputs.connect !== undefined)
|
|
158
|
+
hasItems || hasProductCollection || hasPricing
|
|
160
159
|
|
|
161
160
|
if (hasItems && hasProductCollection) {
|
|
162
161
|
return exits.invalidRequest({
|
|
@@ -183,10 +182,7 @@ module.exports = require('machine').build({
|
|
|
183
182
|
}
|
|
184
183
|
}
|
|
185
184
|
|
|
186
|
-
const
|
|
187
|
-
const payload = shouldUseCheckoutSession
|
|
188
|
-
? buildCheckoutSessionPayload(inputs, adapterConfig)
|
|
189
|
-
: buildPureCheckoutPayload(inputs, adapterConfig)
|
|
185
|
+
const payload = buildCheckoutSessionPayload(inputs, adapterConfig)
|
|
190
186
|
|
|
191
187
|
if (
|
|
192
188
|
!payload.product_cart &&
|
|
@@ -195,12 +191,12 @@ module.exports = require('machine').build({
|
|
|
195
191
|
) {
|
|
196
192
|
return exits.invalidRequest({
|
|
197
193
|
message:
|
|
198
|
-
'Provide
|
|
194
|
+
'Provide items, productCollectionId, or pricing for a Bachs checkout.'
|
|
199
195
|
})
|
|
200
196
|
}
|
|
201
197
|
|
|
202
198
|
try {
|
|
203
|
-
const checkout = await fetch(
|
|
199
|
+
const checkout = await fetch('/checkout-sessions', {
|
|
204
200
|
method: 'POST',
|
|
205
201
|
apiKey: inputs.apiKey || adapterConfig.apiKey,
|
|
206
202
|
baseUrl: inputs.baseUrl || adapterConfig.baseUrl,
|
package/package.json
CHANGED
package/test/checkout.test.js
CHANGED
|
@@ -96,7 +96,6 @@ test('checkout sends custom pricing and the buyer-selected amount', async () =>
|
|
|
96
96
|
|
|
97
97
|
assert.equal(checkoutUrl, 'https://pay.bachs.io/c/custom')
|
|
98
98
|
assert.deepEqual(JSON.parse(calls[0].options.body), {
|
|
99
|
-
customer: {},
|
|
100
99
|
product_cart: [
|
|
101
100
|
{
|
|
102
101
|
product_id: 'prod_custom',
|
|
@@ -112,7 +111,7 @@ test('checkout sends custom pricing and the buyer-selected amount', async () =>
|
|
|
112
111
|
})
|
|
113
112
|
})
|
|
114
113
|
|
|
115
|
-
test('checkout
|
|
114
|
+
test('checkout prices itself with amount and currency', async () => {
|
|
116
115
|
const calls = []
|
|
117
116
|
|
|
118
117
|
fetch.setFetchImplementation(async (url, options) => {
|
|
@@ -137,7 +136,10 @@ test('checkout preserves pure checkout behavior', async () => {
|
|
|
137
136
|
})
|
|
138
137
|
|
|
139
138
|
assert.equal(checkoutUrl, 'https://pay.bachs.io/c/pure')
|
|
140
|
-
assert.equal(
|
|
139
|
+
assert.equal(
|
|
140
|
+
calls[0].url,
|
|
141
|
+
'https://sandbox-api.bachs.io/v1/checkout-sessions'
|
|
142
|
+
)
|
|
141
143
|
assert.equal(calls[0].options.headers['Idempotency-Key'], 'pure_123')
|
|
142
144
|
assert.deepEqual(JSON.parse(calls[0].options.body), {
|
|
143
145
|
pricing: {
|
|
@@ -444,7 +446,7 @@ test('checkout splits a product-less checkout session', async () => {
|
|
|
444
446
|
assert.equal('product_cart' in body, false)
|
|
445
447
|
})
|
|
446
448
|
|
|
447
|
-
test('a
|
|
449
|
+
test('a self-priced checkout without connect still uses a checkout session', async () => {
|
|
448
450
|
const calls = []
|
|
449
451
|
|
|
450
452
|
fetch.setFetchImplementation(async (url, options) => {
|
|
@@ -466,7 +468,10 @@ test('a pure checkout without connect still posts to /checkouts', async () => {
|
|
|
466
468
|
customer: { email: 'sponsor@example.com' }
|
|
467
469
|
})
|
|
468
470
|
|
|
469
|
-
assert.equal(
|
|
471
|
+
assert.equal(
|
|
472
|
+
calls[0].url,
|
|
473
|
+
'https://sandbox-api.bachs.io/v1/checkout-sessions'
|
|
474
|
+
)
|
|
470
475
|
})
|
|
471
476
|
|
|
472
477
|
test('checkout without connect sends no split fields', async () => {
|
|
@@ -573,3 +578,27 @@ for (const invalidCase of invalidConnectCases) {
|
|
|
573
578
|
assert.equal(fetchCalls, 0)
|
|
574
579
|
})
|
|
575
580
|
}
|
|
581
|
+
|
|
582
|
+
for (const customer of [undefined, null, {}]) {
|
|
583
|
+
test(`guest checkout omits an empty customer (${JSON.stringify(
|
|
584
|
+
customer
|
|
585
|
+
)})`, async () => {
|
|
586
|
+
let payload
|
|
587
|
+
fetch.setFetchImplementation(async (url, options) => {
|
|
588
|
+
payload = JSON.parse(options.body)
|
|
589
|
+
return {
|
|
590
|
+
ok: true,
|
|
591
|
+
text: async () =>
|
|
592
|
+
JSON.stringify({ checkout_url: 'https://pay.bachs.io/c/guest' })
|
|
593
|
+
}
|
|
594
|
+
})
|
|
595
|
+
await checkout({
|
|
596
|
+
apiKey: 'sk_sandbox_test',
|
|
597
|
+
amount: '10.00',
|
|
598
|
+
currency: 'USD',
|
|
599
|
+
customer
|
|
600
|
+
})
|
|
601
|
+
assert.equal(Object.hasOwn(payload, 'customer'), false)
|
|
602
|
+
assert.equal(Object.hasOwn(payload, 'customer_creation'), false)
|
|
603
|
+
})
|
|
604
|
+
}
|
package/test/payloads.test.js
CHANGED
|
@@ -2,7 +2,6 @@ const test = require('node:test')
|
|
|
2
2
|
const assert = require('node:assert/strict')
|
|
3
3
|
const {
|
|
4
4
|
buildCheckoutSessionPayload,
|
|
5
|
-
buildPureCheckoutPayload,
|
|
6
5
|
buildRefundPayload,
|
|
7
6
|
buildProductCart,
|
|
8
7
|
normalizePricing
|
|
@@ -216,77 +215,6 @@ test('buildCheckoutSessionPayload maps productCollectionId and configured return
|
|
|
216
215
|
})
|
|
217
216
|
})
|
|
218
217
|
|
|
219
|
-
test('buildPureCheckoutPayload maps amount checkout inputs to Bachs snake case', () => {
|
|
220
|
-
const payload = buildPureCheckoutPayload(
|
|
221
|
-
{
|
|
222
|
-
amount: '50.00',
|
|
223
|
-
currency: 'USD',
|
|
224
|
-
currencyOptions: {
|
|
225
|
-
NGN: '75000.00'
|
|
226
|
-
},
|
|
227
|
-
email: 'customer@example.com',
|
|
228
|
-
name: 'Jane Doe',
|
|
229
|
-
successUrl: 'https://example.com/success',
|
|
230
|
-
cancelUrl: 'https://example.com/cancel',
|
|
231
|
-
reference: 'order_9876',
|
|
232
|
-
metadata: {
|
|
233
|
-
orderId: '9876'
|
|
234
|
-
},
|
|
235
|
-
expiresInMinutes: 30,
|
|
236
|
-
simulatedOutcome: 'success'
|
|
237
|
-
},
|
|
238
|
-
{}
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
assert.deepEqual(payload, {
|
|
242
|
-
pricing: {
|
|
243
|
-
currency: 'USD',
|
|
244
|
-
amount: '50.00',
|
|
245
|
-
currency_options: {
|
|
246
|
-
NGN: '75000.00'
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
customer_email: 'customer@example.com',
|
|
250
|
-
customer_name: 'Jane Doe',
|
|
251
|
-
success_url: 'https://example.com/success',
|
|
252
|
-
cancel_url: 'https://example.com/cancel',
|
|
253
|
-
reference: 'order_9876',
|
|
254
|
-
metadata: {
|
|
255
|
-
orderId: '9876'
|
|
256
|
-
},
|
|
257
|
-
expires_in_minutes: 30,
|
|
258
|
-
simulated_outcome: 'success'
|
|
259
|
-
})
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
test('buildPureCheckoutPayload reuses advanced pricing normalization', () => {
|
|
263
|
-
const payload = buildPureCheckoutPayload({
|
|
264
|
-
pricing: {
|
|
265
|
-
type: 'custom',
|
|
266
|
-
currency: 'USD',
|
|
267
|
-
presetAmount: '10.00',
|
|
268
|
-
minimumAmount: '5.00',
|
|
269
|
-
maximumAmount: '100.00'
|
|
270
|
-
},
|
|
271
|
-
currencyOptions: {
|
|
272
|
-
NGN: '15000.00'
|
|
273
|
-
}
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
assert.deepEqual(payload, {
|
|
277
|
-
pricing: {
|
|
278
|
-
currency: 'USD',
|
|
279
|
-
price_type: 'custom',
|
|
280
|
-
preset_amount: '10.00',
|
|
281
|
-
minimum_amount: '5.00',
|
|
282
|
-
maximum_amount: '100.00',
|
|
283
|
-
currency_options: {
|
|
284
|
-
NGN: '15000.00'
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
})
|
|
288
|
-
})
|
|
289
|
-
|
|
290
218
|
test('buildRefundPayload maps refund inputs to Bachs snake case', () => {
|
|
291
219
|
const payload = buildRefundPayload({
|
|
292
220
|
chargeId: 'chr_123',
|