tf-checkout-react-native 2.0.0-beta.14 → 2.0.0-beta.15
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/dist/index.d.mts +38 -37
- package/dist/index.d.ts +38 -37
- package/dist/index.js +723 -342
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +677 -293
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/src/confirmer.ts +338 -99
- package/src/index.tsx +10 -2
- package/src/instance.ts +1 -3
- package/src/paymentSheet.ts +170 -0
- package/src/sections/Payment.tsx +41 -24
- package/src/ui/CheckoutProvider.tsx +14 -12
- package/src/ui/Select.tsx +30 -6
- package/src/useCheckout.ts +78 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tf-checkout-react-native",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.15",
|
|
4
4
|
"description": "TheTicketFairy ReactNative Checkout library",
|
|
5
5
|
"homepage": "https://github.com/theticketfairy/tf-shared-libraries#readme",
|
|
6
6
|
"author": "The Ticket Fairy",
|
|
@@ -37,10 +37,12 @@
|
|
|
37
37
|
"!**/.*",
|
|
38
38
|
"!scripts/verify-wallet-context.cjs",
|
|
39
39
|
"!scripts/verify-provider-uri.cjs",
|
|
40
|
-
"!scripts/verify-native-textarea.cjs"
|
|
40
|
+
"!scripts/verify-native-textarea.cjs",
|
|
41
|
+
"!scripts/verify-frozen-select.cjs",
|
|
42
|
+
"!scripts/verify-payment-sheet.cjs"
|
|
41
43
|
],
|
|
42
44
|
"scripts": {
|
|
43
|
-
"test": "node scripts/verify-discussion-support.cjs && node scripts/verify-wallet-context.cjs && node scripts/verify-provider-uri.cjs && node scripts/verify-native-textarea.cjs && jest --passWithNoTests",
|
|
45
|
+
"test": "node scripts/verify-discussion-support.cjs && node scripts/verify-wallet-context.cjs && node scripts/verify-provider-uri.cjs && node scripts/verify-native-textarea.cjs && node scripts/verify-frozen-select.cjs && node scripts/verify-payment-sheet.cjs && jest --passWithNoTests",
|
|
44
46
|
"typecheck": "tsc -p tsconfig.build.json",
|
|
45
47
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
46
48
|
"lint:fix": "eslint \"**/*.{js,ts,tsx}\" --fix",
|
package/src/confirmer.ts
CHANGED
|
@@ -21,6 +21,9 @@ import {
|
|
|
21
21
|
createPaymentMethod,
|
|
22
22
|
handleNextAction,
|
|
23
23
|
initStripe,
|
|
24
|
+
initPaymentSheet,
|
|
25
|
+
presentPaymentSheet,
|
|
26
|
+
LinkDisplay,
|
|
24
27
|
PlatformPay,
|
|
25
28
|
retrievePaymentIntent,
|
|
26
29
|
type BillingDetails,
|
|
@@ -35,7 +38,30 @@ import {
|
|
|
35
38
|
type CheckoutPorts,
|
|
36
39
|
type NativeStripeErrorLike,
|
|
37
40
|
type PaymentConfirmer,
|
|
41
|
+
type CardClaimStep,
|
|
42
|
+
type StripeCardPaymentContext,
|
|
43
|
+
stripeElementsPaymentContext,
|
|
38
44
|
} from 'tf-checkout-shared'
|
|
45
|
+
import {
|
|
46
|
+
createPaymentSheetConfirmation,
|
|
47
|
+
NATIVE_SHEET_CARD_FIELD_REQUIRED,
|
|
48
|
+
NATIVE_SHEET_REVIEW_REQUIRED,
|
|
49
|
+
} from './paymentSheet'
|
|
50
|
+
|
|
51
|
+
export interface NativePaymentSheetOptions {
|
|
52
|
+
merchantDisplayName: string
|
|
53
|
+
/** Must match the host's registered scheme and Stripe callback handler. */
|
|
54
|
+
returnURL: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface NativeStripeConfirmer extends PaymentConfirmer {
|
|
58
|
+
createPaymentSheetSession(options: NativePaymentSheetOptions): {
|
|
59
|
+
confirmer: PaymentConfirmer
|
|
60
|
+
cardFieldOperation<T>(operation: () => Promise<T>): Promise<T>
|
|
61
|
+
requiresCardField(): boolean
|
|
62
|
+
run<T>(operation: (claim: CardClaimStep) => Promise<T>): Promise<T>
|
|
63
|
+
}
|
|
64
|
+
}
|
|
39
65
|
|
|
40
66
|
/** `CheckoutBilling` → the native SDK's `BillingDetails`. Mirrors the web
|
|
41
67
|
* `stripeBillingDetails`; the SDK collects card data from `<CardField/>`. */
|
|
@@ -74,24 +100,190 @@ const stripeFailure = (
|
|
|
74
100
|
* idempotent per key so an event on a connected Stripe account re-initializes
|
|
75
101
|
* only when the key/account actually changes.
|
|
76
102
|
*/
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
103
|
+
// Stripe React Native has one native SDK instance, including when a host creates
|
|
104
|
+
// several confirmer adapters. Its account and presentation lease must be global.
|
|
105
|
+
let initKey: string | null = null
|
|
106
|
+
let accountKey: string | null = null
|
|
107
|
+
const listeners = new Set<() => void>()
|
|
108
|
+
let initializedScheme: string | undefined
|
|
109
|
+
let initializing = false
|
|
110
|
+
let sheetPresenting = false
|
|
111
|
+
let nativeOperationActive = false
|
|
112
|
+
let sheetOwner: { key: string; options: NativePaymentSheetOptions } | null = null
|
|
81
113
|
|
|
82
|
-
|
|
114
|
+
const assertNativeLeaseFree = () => {
|
|
115
|
+
if (sheetOwner || initializing || nativeOperationActive)
|
|
116
|
+
throw paymentProviderError(
|
|
117
|
+
'The payment SDK is owned by another operation.',
|
|
118
|
+
'notStarted'
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const nativeSdkCall = async <T>(operation: () => Promise<T>): Promise<T> => {
|
|
123
|
+
assertNativeLeaseFree()
|
|
124
|
+
nativeOperationActive = true
|
|
125
|
+
try {
|
|
126
|
+
return await operation()
|
|
127
|
+
} finally {
|
|
128
|
+
nativeOperationActive = false
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const createRnStripeConfirmer = (): NativeStripeConfirmer => {
|
|
133
|
+
const adapter: NativeStripeConfirmer = {
|
|
83
134
|
init: async ({ publishableKey, accountId, merchantIdentifier, urlScheme }) => {
|
|
84
|
-
const key =
|
|
85
|
-
if (initKey === key) return
|
|
86
|
-
await initStripe({
|
|
135
|
+
const key = JSON.stringify([
|
|
87
136
|
publishableKey,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
137
|
+
accountId ?? null,
|
|
138
|
+
merchantIdentifier ?? null,
|
|
139
|
+
urlScheme ?? null,
|
|
140
|
+
])
|
|
141
|
+
if (initializing || (sheetOwner && sheetOwner.key !== key))
|
|
142
|
+
throw paymentProviderError(
|
|
143
|
+
'The payment SDK is owned by another operation.',
|
|
144
|
+
'notStarted'
|
|
145
|
+
)
|
|
146
|
+
if (initKey === key) return
|
|
147
|
+
if (nativeOperationActive)
|
|
148
|
+
throw paymentProviderError(
|
|
149
|
+
'The payment SDK is owned by another operation.',
|
|
150
|
+
'notStarted'
|
|
151
|
+
)
|
|
152
|
+
initializing = true
|
|
153
|
+
initKey = null
|
|
154
|
+
accountKey = null
|
|
155
|
+
initializedScheme = undefined
|
|
156
|
+
try {
|
|
157
|
+
await initStripe({
|
|
158
|
+
publishableKey,
|
|
159
|
+
...(accountId ? { stripeAccountId: accountId } : {}),
|
|
160
|
+
...(merchantIdentifier ? { merchantIdentifier } : {}),
|
|
161
|
+
...(urlScheme ? { urlScheme } : {}),
|
|
162
|
+
})
|
|
163
|
+
initKey = key
|
|
164
|
+
initializedScheme = urlScheme
|
|
165
|
+
accountKey = `${publishableKey}:${accountId ?? ''}`
|
|
166
|
+
listeners.forEach((listener) => listener())
|
|
167
|
+
} finally {
|
|
168
|
+
initializing = false
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
createPaymentSheetSession: (options) => {
|
|
173
|
+
let running = false
|
|
174
|
+
let owner: typeof sheetOwner = null
|
|
175
|
+
let requiresCardField = false
|
|
176
|
+
let reviewed: StripeCardPaymentContext | undefined
|
|
177
|
+
return {
|
|
178
|
+
requiresCardField: () => requiresCardField,
|
|
179
|
+
cardFieldOperation: async (operation) => {
|
|
180
|
+
if (running) {
|
|
181
|
+
// The engine's authoritative review requested its MSI/CardField
|
|
182
|
+
// branch. It catches unavailable offers; never let that catch
|
|
183
|
+
// silently fall through to PaymentSheet instead.
|
|
184
|
+
requiresCardField = true
|
|
185
|
+
throw paymentProviderError('Use the card form for this order.', 'notStarted')
|
|
186
|
+
}
|
|
187
|
+
return operation()
|
|
188
|
+
},
|
|
189
|
+
confirmer: {
|
|
190
|
+
...adapter,
|
|
191
|
+
prepareCardPayment: async (context) => {
|
|
192
|
+
if (!running) return
|
|
193
|
+
reviewed = stripeElementsPaymentContext(
|
|
194
|
+
context?.amountMinor,
|
|
195
|
+
context?.currency
|
|
196
|
+
)
|
|
197
|
+
if (!reviewed || !owner || sheetOwner !== owner) {
|
|
198
|
+
throw paymentProviderError(NATIVE_SHEET_REVIEW_REQUIRED, 'notStarted')
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
confirmCard: async (clientSecret, billing) => {
|
|
202
|
+
if (!running) return adapter.confirmCard(clientSecret, billing)
|
|
203
|
+
if (requiresCardField)
|
|
204
|
+
throw paymentProviderError(
|
|
205
|
+
'Use the card form for this order.',
|
|
206
|
+
'notStarted'
|
|
207
|
+
)
|
|
208
|
+
if (!owner || sheetOwner !== owner)
|
|
209
|
+
throw paymentProviderError(
|
|
210
|
+
'The payment sheet was not claimed.',
|
|
211
|
+
'notStarted'
|
|
212
|
+
)
|
|
213
|
+
{
|
|
214
|
+
if (sheetPresenting)
|
|
215
|
+
throw paymentProviderError(
|
|
216
|
+
'Another payment sheet is active.',
|
|
217
|
+
'notStarted'
|
|
218
|
+
)
|
|
219
|
+
const confirm = createPaymentSheetConfirmation(
|
|
220
|
+
{
|
|
221
|
+
initPaymentSheet,
|
|
222
|
+
presentPaymentSheet,
|
|
223
|
+
retrievePaymentIntent,
|
|
224
|
+
LinkDisplay,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
...owner.options,
|
|
228
|
+
reviewed,
|
|
229
|
+
assertCurrent: () => {
|
|
230
|
+
if (sheetOwner !== owner || initKey !== owner.key || initializing)
|
|
231
|
+
throw paymentProviderError(
|
|
232
|
+
'The original payment owner changed.',
|
|
233
|
+
'notStarted'
|
|
234
|
+
)
|
|
235
|
+
},
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
sheetPresenting = true
|
|
239
|
+
try {
|
|
240
|
+
return await confirm(clientSecret, toBillingDetails(billing))
|
|
241
|
+
} catch (error) {
|
|
242
|
+
if (
|
|
243
|
+
(error as { providerCode?: string })?.providerCode ===
|
|
244
|
+
NATIVE_SHEET_CARD_FIELD_REQUIRED
|
|
245
|
+
)
|
|
246
|
+
requiresCardField = true
|
|
247
|
+
throw error
|
|
248
|
+
} finally {
|
|
249
|
+
sheetPresenting = false
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
run: async (operation) => {
|
|
255
|
+
if (running)
|
|
256
|
+
throw paymentProviderError('This payment is already active.', 'notStarted')
|
|
257
|
+
running = true
|
|
258
|
+
reviewed = undefined
|
|
259
|
+
let admitted = true
|
|
260
|
+
const claim: CardClaimStep = () => {
|
|
261
|
+
if (
|
|
262
|
+
!admitted ||
|
|
263
|
+
nativeOperationActive ||
|
|
264
|
+
initializing ||
|
|
265
|
+
!initKey ||
|
|
266
|
+
sheetOwner ||
|
|
267
|
+
!initializedScheme ||
|
|
268
|
+
options.returnURL !== `${initializedScheme}://stripe-redirect` ||
|
|
269
|
+
!options.merchantDisplayName.trim()
|
|
270
|
+
)
|
|
271
|
+
return 'unavailable'
|
|
272
|
+
owner = { key: initKey, options: { ...options } }
|
|
273
|
+
sheetOwner = owner
|
|
274
|
+
return 'attached'
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
return await operation(claim)
|
|
278
|
+
} finally {
|
|
279
|
+
admitted = false
|
|
280
|
+
if (owner && sheetOwner === owner) sheetOwner = null
|
|
281
|
+
owner = null
|
|
282
|
+
reviewed = undefined
|
|
283
|
+
running = false
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
}
|
|
95
287
|
},
|
|
96
288
|
|
|
97
289
|
// `publishableKey:accountId`, the shape `bundleConfirmerKey` expects.
|
|
@@ -106,20 +298,27 @@ export const createRnStripeConfirmer = (): PaymentConfirmer => {
|
|
|
106
298
|
|
|
107
299
|
// Native statuses are already the port's PascalCase vocabulary — no normalize.
|
|
108
300
|
getIntentStatus: async (clientSecret) => {
|
|
109
|
-
|
|
301
|
+
assertNativeLeaseFree()
|
|
302
|
+
const { paymentIntent } = await nativeSdkCall(() =>
|
|
303
|
+
retrievePaymentIntent(clientSecret)
|
|
304
|
+
)
|
|
110
305
|
return paymentIntent?.status
|
|
111
306
|
},
|
|
112
307
|
|
|
113
308
|
confirmCard: async (clientSecret, billing) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
309
|
+
assertNativeLeaseFree()
|
|
310
|
+
const { error, paymentIntent } = await nativeSdkCall(() =>
|
|
311
|
+
confirmPayment(clientSecret, {
|
|
312
|
+
paymentMethodType: 'Card',
|
|
313
|
+
paymentMethodData: { billingDetails: toBillingDetails(billing) },
|
|
314
|
+
})
|
|
315
|
+
)
|
|
118
316
|
if (error) throw stripeFailure(error, 'Payment failed.')
|
|
119
317
|
return confirmedStripeIntentStatus(paymentIntent)
|
|
120
318
|
},
|
|
121
319
|
|
|
122
320
|
confirmApplePay: async (clientSecret, total, currencyCode, merchant) => {
|
|
321
|
+
assertNativeLeaseFree()
|
|
123
322
|
const context = stripeWalletPaymentContext(
|
|
124
323
|
total,
|
|
125
324
|
currencyCode,
|
|
@@ -130,24 +329,27 @@ export const createRnStripeConfirmer = (): PaymentConfirmer => {
|
|
|
130
329
|
'The order wallet details are unavailable.',
|
|
131
330
|
'notStarted'
|
|
132
331
|
)
|
|
133
|
-
const { error, paymentIntent } = await
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
332
|
+
const { error, paymentIntent } = await nativeSdkCall(() =>
|
|
333
|
+
confirmPlatformPayPayment(clientSecret, {
|
|
334
|
+
applePay: {
|
|
335
|
+
merchantCountryCode: context.merchantCountry,
|
|
336
|
+
currencyCode: context.currency,
|
|
337
|
+
cartItems: [
|
|
338
|
+
{
|
|
339
|
+
label: merchant.merchantName ?? 'Order',
|
|
340
|
+
amount: context.amount,
|
|
341
|
+
paymentType: PlatformPay.PaymentType.Immediate,
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
})
|
|
346
|
+
)
|
|
146
347
|
if (error) throw stripeFailure(error, 'Apple Pay failed.')
|
|
147
348
|
return confirmedStripeIntentStatus(paymentIntent)
|
|
148
349
|
},
|
|
149
350
|
|
|
150
351
|
confirmGooglePay: async (clientSecret, total, currencyCode, merchant) => {
|
|
352
|
+
assertNativeLeaseFree()
|
|
151
353
|
const context = stripeWalletPaymentContext(
|
|
152
354
|
total,
|
|
153
355
|
currencyCode,
|
|
@@ -158,18 +360,21 @@ export const createRnStripeConfirmer = (): PaymentConfirmer => {
|
|
|
158
360
|
'The order wallet details are unavailable.',
|
|
159
361
|
'notStarted'
|
|
160
362
|
)
|
|
161
|
-
const { error, paymentIntent } = await
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
363
|
+
const { error, paymentIntent } = await nativeSdkCall(() =>
|
|
364
|
+
confirmPlatformPayPayment(clientSecret, {
|
|
365
|
+
googlePay: {
|
|
366
|
+
testEnv: merchant.testEnv === true,
|
|
367
|
+
merchantCountryCode: context.merchantCountry,
|
|
368
|
+
currencyCode: context.currency,
|
|
369
|
+
merchantName: merchant.merchantName ?? 'Order',
|
|
370
|
+
},
|
|
371
|
+
})
|
|
372
|
+
)
|
|
169
373
|
if (error) throw stripeFailure(error, 'Google Pay failed.')
|
|
170
374
|
return confirmedStripeIntentStatus(paymentIntent)
|
|
171
375
|
},
|
|
172
376
|
}
|
|
377
|
+
return adapter
|
|
173
378
|
}
|
|
174
379
|
|
|
175
380
|
/**
|
|
@@ -183,70 +388,94 @@ export const createRnPorts = (): Omit<CheckoutPorts, 'confirmer'> => ({
|
|
|
183
388
|
openUrl: (url: string) => Linking.openURL(url).then(() => undefined),
|
|
184
389
|
|
|
185
390
|
createCardPaymentMethod: async (billing) => {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
391
|
+
assertNativeLeaseFree()
|
|
392
|
+
const { paymentMethod, error } = await nativeSdkCall(() =>
|
|
393
|
+
createPaymentMethod({
|
|
394
|
+
paymentMethodType: 'Card',
|
|
395
|
+
paymentMethodData: { billingDetails: toBillingDetails(billing) },
|
|
396
|
+
})
|
|
397
|
+
)
|
|
190
398
|
if (error || !paymentMethod?.id) throw failed(error?.message, 'Payment failed')
|
|
191
399
|
return paymentMethod.id
|
|
192
400
|
},
|
|
193
401
|
|
|
194
402
|
createRedirectPaymentMethod: async (methodType, billing) => {
|
|
403
|
+
assertNativeLeaseFree()
|
|
195
404
|
const details = toBillingDetails(billing)
|
|
196
405
|
const result = await (() => {
|
|
197
406
|
switch (methodType) {
|
|
198
407
|
case 'klarna':
|
|
199
|
-
return
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
408
|
+
return nativeSdkCall(() =>
|
|
409
|
+
createPaymentMethod({
|
|
410
|
+
paymentMethodType: 'Klarna',
|
|
411
|
+
paymentMethodData: { billingDetails: details },
|
|
412
|
+
})
|
|
413
|
+
)
|
|
203
414
|
case 'afterpay_clearpay':
|
|
204
|
-
return
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
415
|
+
return nativeSdkCall(() =>
|
|
416
|
+
createPaymentMethod({
|
|
417
|
+
paymentMethodType: 'AfterpayClearpay',
|
|
418
|
+
paymentMethodData: { billingDetails: details, shippingDetails: details },
|
|
419
|
+
})
|
|
420
|
+
)
|
|
208
421
|
case 'affirm':
|
|
209
|
-
return
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
422
|
+
return nativeSdkCall(() =>
|
|
423
|
+
createPaymentMethod({
|
|
424
|
+
paymentMethodType: 'Affirm',
|
|
425
|
+
paymentMethodData: { billingDetails: details },
|
|
426
|
+
})
|
|
427
|
+
)
|
|
213
428
|
case 'ideal':
|
|
214
|
-
return
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
429
|
+
return nativeSdkCall(() =>
|
|
430
|
+
createPaymentMethod({
|
|
431
|
+
paymentMethodType: 'Ideal',
|
|
432
|
+
paymentMethodData: { billingDetails: details },
|
|
433
|
+
})
|
|
434
|
+
)
|
|
218
435
|
case 'bancontact':
|
|
219
|
-
return
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
436
|
+
return nativeSdkCall(() =>
|
|
437
|
+
createPaymentMethod({
|
|
438
|
+
paymentMethodType: 'Bancontact',
|
|
439
|
+
paymentMethodData: { billingDetails: details },
|
|
440
|
+
})
|
|
441
|
+
)
|
|
223
442
|
case 'eps':
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
443
|
+
return nativeSdkCall(() =>
|
|
444
|
+
createPaymentMethod({
|
|
445
|
+
paymentMethodType: 'Eps',
|
|
446
|
+
paymentMethodData: { billingDetails: details },
|
|
447
|
+
})
|
|
448
|
+
)
|
|
228
449
|
case 'p24':
|
|
229
|
-
return
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
450
|
+
return nativeSdkCall(() =>
|
|
451
|
+
createPaymentMethod({
|
|
452
|
+
paymentMethodType: 'P24',
|
|
453
|
+
paymentMethodData: { billingDetails: details },
|
|
454
|
+
})
|
|
455
|
+
)
|
|
233
456
|
case 'alipay':
|
|
234
|
-
return createPaymentMethod({ paymentMethodType: 'Alipay' })
|
|
457
|
+
return nativeSdkCall(() => createPaymentMethod({ paymentMethodType: 'Alipay' }))
|
|
235
458
|
case 'grabpay':
|
|
236
|
-
return
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
459
|
+
return nativeSdkCall(() =>
|
|
460
|
+
createPaymentMethod({
|
|
461
|
+
paymentMethodType: 'GrabPay',
|
|
462
|
+
paymentMethodData: { billingDetails: details },
|
|
463
|
+
})
|
|
464
|
+
)
|
|
240
465
|
case 'cashapp':
|
|
241
|
-
return
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
466
|
+
return nativeSdkCall(() =>
|
|
467
|
+
createPaymentMethod({
|
|
468
|
+
paymentMethodType: 'CashApp',
|
|
469
|
+
paymentMethodData: { billingDetails: details },
|
|
470
|
+
})
|
|
471
|
+
)
|
|
245
472
|
case 'paypal':
|
|
246
|
-
return
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
473
|
+
return nativeSdkCall(() =>
|
|
474
|
+
createPaymentMethod({
|
|
475
|
+
paymentMethodType: 'PayPal',
|
|
476
|
+
paymentMethodData: { billingDetails: details },
|
|
477
|
+
})
|
|
478
|
+
)
|
|
250
479
|
default:
|
|
251
480
|
throw new Error('This payment method is not available on this device.')
|
|
252
481
|
}
|
|
@@ -257,25 +486,32 @@ export const createRnPorts = (): Omit<CheckoutPorts, 'confirmer'> => ({
|
|
|
257
486
|
},
|
|
258
487
|
|
|
259
488
|
handleNextAction: async (clientSecret) => {
|
|
260
|
-
|
|
489
|
+
assertNativeLeaseFree()
|
|
490
|
+
const { error } = await nativeSdkCall(() => handleNextAction(clientSecret))
|
|
261
491
|
if (error) throw stripeFailure(error, 'Payment failed')
|
|
262
492
|
},
|
|
263
493
|
|
|
264
494
|
confirmOxxo: async (clientSecret, billing) => {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
495
|
+
assertNativeLeaseFree()
|
|
496
|
+
const { error, paymentIntent } = await nativeSdkCall(() =>
|
|
497
|
+
confirmPayment(clientSecret, {
|
|
498
|
+
paymentMethodType: 'Oxxo',
|
|
499
|
+
paymentMethodData: { billingDetails: toBillingDetails(billing) },
|
|
500
|
+
})
|
|
501
|
+
)
|
|
269
502
|
return { error: error?.message, nextAction: paymentIntent?.nextAction as unknown }
|
|
270
503
|
},
|
|
271
504
|
|
|
272
505
|
// Payment plan (new card): confirm the SetupIntent to SAVE the card (nothing
|
|
273
506
|
// charged) and return its PaymentMethod id for the server-side plan.
|
|
274
507
|
confirmPaymentPlanSetup: async (clientSecret, billing) => {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
508
|
+
assertNativeLeaseFree()
|
|
509
|
+
const { setupIntent, error } = await nativeSdkCall(() =>
|
|
510
|
+
confirmSetupIntent(clientSecret, {
|
|
511
|
+
paymentMethodType: 'Card',
|
|
512
|
+
paymentMethodData: { billingDetails: toBillingDetails(billing) },
|
|
513
|
+
})
|
|
514
|
+
)
|
|
279
515
|
const paymentMethodId = setupIntent?.paymentMethod?.id
|
|
280
516
|
if (error || !paymentMethodId) throw failed(error?.message, 'Card setup failed')
|
|
281
517
|
return paymentMethodId
|
|
@@ -284,10 +520,13 @@ export const createRnPorts = (): Omit<CheckoutPorts, 'confirmer'> => ({
|
|
|
284
520
|
// Payment plan (saved-card 3DS): confirm the pending charge against the
|
|
285
521
|
// existing PaymentMethod when the plan comes back requires_action.
|
|
286
522
|
confirmSavedCardPayment: async (clientSecret, paymentMethodId) => {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
523
|
+
assertNativeLeaseFree()
|
|
524
|
+
const { error } = await nativeSdkCall(() =>
|
|
525
|
+
confirmPayment(clientSecret, {
|
|
526
|
+
paymentMethodType: 'Card',
|
|
527
|
+
paymentMethodData: { paymentMethodId },
|
|
528
|
+
})
|
|
529
|
+
)
|
|
291
530
|
if (error) throw stripeFailure(error, 'Payment failed')
|
|
292
531
|
},
|
|
293
532
|
})
|
package/src/index.tsx
CHANGED
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
export * from 'tf-checkout-shared'
|
|
14
14
|
|
|
15
15
|
// Native platform layer
|
|
16
|
-
export {
|
|
16
|
+
export {
|
|
17
|
+
useCheckout,
|
|
18
|
+
type UseCheckoutOptions,
|
|
19
|
+
type NativeUseCheckout,
|
|
20
|
+
} from './useCheckout'
|
|
17
21
|
export { configureCheckout, mmkvStorageAdapter } from './configure'
|
|
18
22
|
export { createMmkvStorage } from './storage'
|
|
19
23
|
export { CardInput, claimCardField, type CardInputProps } from './CardInput'
|
|
@@ -26,4 +30,8 @@ export { EmbeddedCheckout, type EmbeddedCheckoutProps } from './EmbeddedCheckout
|
|
|
26
30
|
|
|
27
31
|
// Port instances/factories (bring-your-own ports, testing, custom flows).
|
|
28
32
|
export { rnConfirmer, rnPorts } from './instance'
|
|
29
|
-
export {
|
|
33
|
+
export {
|
|
34
|
+
createRnStripeConfirmer,
|
|
35
|
+
createRnPorts,
|
|
36
|
+
type NativePaymentSheetOptions,
|
|
37
|
+
} from './confirmer'
|
package/src/instance.ts
CHANGED
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
* The package-level native platform instances. Created once at module load, so
|
|
3
3
|
* the hook and the UI components all bind to the SAME confirmer/ports.
|
|
4
4
|
*/
|
|
5
|
-
import type { PaymentConfirmer } from 'tf-checkout-shared'
|
|
6
|
-
|
|
7
5
|
import { createRnPorts, createRnStripeConfirmer } from './confirmer'
|
|
8
6
|
|
|
9
7
|
/** The confirmer every `useCheckout`/`<CardInput/>` binds to. */
|
|
10
|
-
export const rnConfirmer
|
|
8
|
+
export const rnConfirmer = createRnStripeConfirmer()
|
|
11
9
|
|
|
12
10
|
/** The native ports the alternative Stripe rails (OXXO / plan) run on. */
|
|
13
11
|
export const rnPorts = createRnPorts()
|