@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.
@@ -37,7 +37,8 @@ module.exports = {
37
37
  BACHS_SUCCESS_URL: {
38
38
  type: 'string',
39
39
  friendlyName: 'Success URL',
40
- description: 'Default URL Bachs redirects to after pure checkout payment.'
40
+ description:
41
+ 'Default URL Bachs redirects to after payment. Alias for returnUrl.'
41
42
  },
42
43
  BACHS_CANCEL_URL: {
43
44
  type: 'string',
@@ -27,7 +27,7 @@ function buildCustomerPayload(inputs) {
27
27
  })
28
28
  }
29
29
 
30
- return withoutUndefined({
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,
@@ -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: 'Pure Checkout pricing object.'
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: 'Pure Checkout amount, e.g. "50.00".'
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: 'Pure Checkout currency, e.g. "USD" or "NGN".'
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: 'Pure Checkout per-currency amount overrides.'
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: 'Pure Checkout expiry in minutes.'
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
- // A split belongs to the platform, and Bachs only splits checkout
154
- // sessions. A session takes products or a raw price, so a checkout that
155
- // states its own amount and currency can be split too.
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 path = shouldUseCheckoutSession ? '/checkout-sessions' : '/checkouts'
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 product items/productCollectionId or pure checkout pricing.'
194
+ 'Provide items, productCollectionId, or pricing for a Bachs checkout.'
199
195
  })
200
196
  }
201
197
 
202
198
  try {
203
- const checkout = await fetch(path, {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sails-pay/bachs",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Bachs adapter for Sails Pay",
5
5
  "main": "adapter.js",
6
6
  "scripts": {
@@ -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 preserves pure checkout behavior', async () => {
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(calls[0].url, 'https://sandbox-api.bachs.io/v1/checkouts')
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 pure checkout without connect still posts to /checkouts', async () => {
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(calls[0].url, 'https://sandbox-api.bachs.io/v1/checkouts')
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
+ }
@@ -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',