tf-checkout-react-native 2.0.0-beta.21 → 2.0.0-beta.23
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/README.md +216 -97
- package/dist/index.d.mts +83 -61
- package/dist/index.d.ts +83 -61
- package/dist/index.js +50 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -5
- package/src/configure.ts +40 -10
- package/src/index.tsx +5 -2
- package/src/useCheckout.ts +1 -1
- package/src/storage.ts +0 -26
package/README.md
CHANGED
|
@@ -1,39 +1,40 @@
|
|
|
1
1
|
# tf-checkout-react-native
|
|
2
2
|
|
|
3
|
-
The
|
|
4
|
-
ticket selection, add-ons, buyer details, payment
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
The
|
|
12
|
-
|
|
3
|
+
The Ticket Fairy checkout for React Native. Use the embedded flow or compose its
|
|
4
|
+
ticket selection, add-ons, buyer details, payment and confirmation sections.
|
|
5
|
+
Available features depend on the event, backend, device and payment provider.
|
|
6
|
+
|
|
7
|
+
The native and [web](https://www.npmjs.com/package/tf-checkout-react)
|
|
8
|
+
packages share a checkout core, with platform-specific UI and payment adapters.
|
|
9
|
+
They do not yet have complete hosted-checkout parity.
|
|
10
|
+
|
|
11
|
+
The 2.x source is maintained in `tf-shared-libraries/packages/tf-checkout-react-native`;
|
|
12
|
+
the source repository requires access.
|
|
13
|
+
The shared core is private and bundled into both platform packages. Native host
|
|
14
|
+
requirements and peer dependencies are listed below.
|
|
13
15
|
Never put a confidential OAuth client secret in browser or mobile code. Public
|
|
14
16
|
checkout integrations use registered SDK client IDs and owned buyer sessions.
|
|
15
17
|
|
|
16
18
|
## Install
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
This README describes `2.0.0-beta.23`, the paired 2.x prerelease. Pin it
|
|
21
|
+
explicitly to use this version. `beta` and `latest` are mutable npm tags, not
|
|
22
|
+
guarantees about the highest version; an unqualified install can still resolve
|
|
23
|
+
a stable 1.x package. Check the current dist-tags when choosing a version.
|
|
24
|
+
Publication does not by itself establish backend rollout, device compatibility
|
|
25
|
+
or provider/payment acceptance.
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
publication. The unqualified npm package continues to resolve to stable 1.x;
|
|
24
|
-
`@beta` follows the most recently accepted prerelease. Use the exact version
|
|
25
|
-
below when integrating this contract.
|
|
27
|
+
```bash
|
|
28
|
+
npm view tf-checkout-react-native dist-tags
|
|
29
|
+
```
|
|
26
30
|
|
|
27
31
|
This version preserves quantities across rotating encrypted ticket IDs using
|
|
28
32
|
the API's optional canonical `selectionKey`, while quantity/change APIs and cart
|
|
29
33
|
requests retain the latest encrypted IDs. Deploy and verify the canonical
|
|
30
34
|
base/dated key and dated numeric context before relying on that behavior:
|
|
31
35
|
`display_price`, `price_with_fees`, `fee_included`, `price_currency` and
|
|
32
|
-
`price_symbol`.
|
|
33
|
-
|
|
34
|
-
rotation-retention claim. The [beta.20 notes](https://github.com/theticketfairy/tf-shared-libraries/blob/main/docs/releases/2.0.0-beta.21.md)
|
|
35
|
-
and [app integration contract](https://github.com/theticketfairy/tf-shared-libraries/blob/main/docs/integration/app-sdk.md)
|
|
36
|
-
record API rollout requirements and separate deployment/provider/device gates.
|
|
36
|
+
`price_symbol`. Missing optional keys retain opaque-ID compatibility without a
|
|
37
|
+
rotation-retention claim. The deployed API must provide and validate that data.
|
|
37
38
|
|
|
38
39
|
Post-purchase upgrade waiting is a separate server-owned contract. Only valid
|
|
39
40
|
change-options targets can expose Join or Leave; malformed target IDs, labels
|
|
@@ -41,11 +42,12 @@ or existing entries are ignored. A sold-out row does not enable it. The backend
|
|
|
41
42
|
must explicitly offer that organizer-configured flow; local receiver validation
|
|
42
43
|
does not prove its deployed availability or a completed join.
|
|
43
44
|
|
|
45
|
+
For an Expo host with compatible React and React Native versions:
|
|
46
|
+
|
|
44
47
|
```bash
|
|
45
|
-
npx expo install tf-checkout-react-native@2.0.0-beta.
|
|
48
|
+
npx expo install tf-checkout-react-native@2.0.0-beta.23 \
|
|
46
49
|
@tanstack/react-query \
|
|
47
50
|
@stripe/stripe-react-native \
|
|
48
|
-
react-native-mmkv@3 \
|
|
49
51
|
@react-native-clipboard/clipboard \
|
|
50
52
|
react-native-webview \
|
|
51
53
|
react-native-render-html@6.3.4
|
|
@@ -54,31 +56,31 @@ npx expo install tf-checkout-react-native@2.0.0-beta.21 \
|
|
|
54
56
|
Bare React Native:
|
|
55
57
|
|
|
56
58
|
```bash
|
|
57
|
-
npm install tf-checkout-react-native@2.0.0-beta.
|
|
59
|
+
npm install tf-checkout-react-native@2.0.0-beta.23 @tanstack/react-query @stripe/stripe-react-native @react-native-clipboard/clipboard react-native-webview react-native-render-html@6.3.4
|
|
58
60
|
```
|
|
59
61
|
|
|
60
|
-
These
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
needed.
|
|
62
|
+
These packages, React and React Native are **peer dependencies**. Install
|
|
63
|
+
compatible versions in the host and ensure the SDK and host resolve the same
|
|
64
|
+
React, React Query and native-module instances. Check your dependency tree and
|
|
65
|
+
native build; declaring peers does not itself prevent duplicates.
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
The private shared core and native UI are bundled; npm installs the package's
|
|
68
|
+
declared runtime dependencies separately.
|
|
67
69
|
|
|
68
|
-
This
|
|
70
|
+
This version requires the `react-native-render-html` peer `>=6.3.4 <7`
|
|
69
71
|
for formatted ticket, add-on, bundle and accommodation descriptions and its
|
|
70
72
|
public `RichText` renderer. Development and component tests use `6.3.4`; the
|
|
71
|
-
install commands above pin that compatible version.
|
|
72
|
-
|
|
73
|
+
install commands above pin that compatible version. Verify the rendering and
|
|
74
|
+
link behavior needed by your host on its supported devices.
|
|
73
75
|
|
|
74
76
|
## Requirements
|
|
75
77
|
|
|
76
|
-
- React Native `0.81+` with the New Architecture enabled (
|
|
77
|
-
`react-native-mmkv` 3.x)
|
|
78
|
+
- React Native `0.81+` with the New Architecture enabled (the supported build cohort)
|
|
78
79
|
- React `19.1+`, Node `20.19.4+`
|
|
79
|
-
- iOS 15.1+ · Android API 24+
|
|
80
|
-
- A development build
|
|
81
|
-
|
|
80
|
+
- iOS 15.1+ · Android API 24+ for the supported React Native 0.81 cohort
|
|
81
|
+
- A development build is recommended for this SDK and real wallet
|
|
82
|
+
configuration. Expo Go compatibility for the complete checkout is not
|
|
83
|
+
established.
|
|
82
84
|
|
|
83
85
|
Add the Stripe plugin to `app.json`:
|
|
84
86
|
|
|
@@ -98,26 +100,104 @@ npx expo run:ios
|
|
|
98
100
|
|
|
99
101
|
## Use it
|
|
100
102
|
|
|
103
|
+
Native `configureCheckout` and the root `configure` alias require a host-owned
|
|
104
|
+
synchronous `StorageAdapter`. The SDK does not create a persistent MMKV store
|
|
105
|
+
or verify that the host's adapter is encrypted. Restore an OS-protected adapter
|
|
106
|
+
before configuring checkout; migrate old credentials securely or require a new
|
|
107
|
+
sign-in. Use a separate namespace per environment.
|
|
108
|
+
|
|
109
|
+
The adapter has synchronous `getItem(key)`, `setItem(key, value)` and
|
|
110
|
+
`removeItem(key)` methods. `getItem` returns a string, `null` or `undefined`.
|
|
111
|
+
An adapter that queues asynchronous persistence may also expose `flush():
|
|
112
|
+
Promise<void>`; the host must await it after credential-changing operations
|
|
113
|
+
before reporting durable success. The built-in auth completion does not await
|
|
114
|
+
that final flush automatically. Never pass async functions as the synchronous
|
|
115
|
+
methods or mount SDK hooks before restoring an asynchronous mirror.
|
|
116
|
+
|
|
117
|
+
### Expo storage example
|
|
118
|
+
|
|
119
|
+
The following host-owned example restores one Keychain/Keystore-backed entry
|
|
120
|
+
before setup, then writes the next snapshot synchronously before updating its
|
|
121
|
+
in-memory mirror. Install it in an Expo development build with
|
|
122
|
+
`npx expo install expo-secure-store`:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// credentialStorage.ts
|
|
126
|
+
import * as SecureStore from 'expo-secure-store'
|
|
127
|
+
import type { StorageAdapter } from 'tf-checkout-react-native'
|
|
128
|
+
|
|
129
|
+
export async function restoreCredentialStorage(namespace: string): Promise<StorageAdapter> {
|
|
130
|
+
const raw = await SecureStore.getItemAsync(namespace)
|
|
131
|
+
const parsed: unknown = raw === null ? {} : JSON.parse(raw)
|
|
132
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) ||
|
|
133
|
+
Object.values(parsed).some(value => typeof value !== 'string')) {
|
|
134
|
+
throw new Error('Could not restore checkout credentials')
|
|
135
|
+
}
|
|
136
|
+
let values = new Map(Object.entries(parsed as Record<string, string>))
|
|
137
|
+
const commit = (next: Map<string, string>) => {
|
|
138
|
+
SecureStore.setItem(namespace, JSON.stringify(Object.fromEntries(next)), {
|
|
139
|
+
keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
|
140
|
+
requireAuthentication: false,
|
|
141
|
+
})
|
|
142
|
+
values = next
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
getItem: key => values.get(key) ?? null,
|
|
146
|
+
setItem: (key, value) => { const next = new Map(values); next.set(key, value); commit(next) },
|
|
147
|
+
removeItem: key => { const next = new Map(values); next.delete(key); commit(next) },
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
This example does not request biometric authentication. Secure storage has
|
|
153
|
+
platform-specific size and backup behavior; configure your host's backup policy
|
|
154
|
+
and handle restoration/write failures. Synchronous writes can block the JS
|
|
155
|
+
thread. See the public [Expo SecureStore documentation](https://docs.expo.dev/versions/latest/sdk/securestore/).
|
|
156
|
+
|
|
101
157
|
```tsx
|
|
158
|
+
import { useEffect, useState } from 'react'
|
|
159
|
+
import { Button, Text, View } from 'react-native'
|
|
102
160
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
103
161
|
import { configureCheckout, EmbeddedCheckout } from 'tf-checkout-react-native'
|
|
104
|
-
|
|
105
|
-
|
|
162
|
+
import { restoreCredentialStorage } from './credentialStorage'
|
|
163
|
+
|
|
164
|
+
// Share initialization across repeated effects and permit retry after failure.
|
|
165
|
+
let initialization: Promise<void> | undefined
|
|
166
|
+
function prepareCheckout(): Promise<void> {
|
|
167
|
+
if (!initialization) {
|
|
168
|
+
initialization = (async () => {
|
|
169
|
+
const storage = await restoreCredentialStorage('my_app_checkout_prod')
|
|
170
|
+
configureCheckout({ env: 'PROD', clientId: 'PUBLIC_SDK_CLIENT_ID', clientSecret: '', storage })
|
|
171
|
+
})().catch(error => { initialization = undefined; throw error })
|
|
172
|
+
}
|
|
173
|
+
return initialization
|
|
174
|
+
}
|
|
106
175
|
|
|
107
176
|
const queryClient = new QueryClient()
|
|
108
177
|
|
|
109
178
|
export default function App() {
|
|
179
|
+
const [ready, setReady] = useState(false)
|
|
180
|
+
const [failed, setFailed] = useState(false)
|
|
181
|
+
const [attempt, setAttempt] = useState(0)
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
let active = true
|
|
184
|
+
prepareCheckout().then(() => { if (active) setReady(true) })
|
|
185
|
+
.catch(() => { if (active) setFailed(true) })
|
|
186
|
+
return () => { active = false }
|
|
187
|
+
}, [attempt])
|
|
188
|
+
if (failed) return <View><Text>Checkout setup failed.</Text><Button title="Try again" onPress={() => { setFailed(false); setAttempt(value => value + 1) }} /></View>
|
|
189
|
+
if (!ready) return <Text>Getting checkout ready…</Text>
|
|
110
190
|
return (
|
|
111
191
|
<QueryClientProvider client={queryClient}>
|
|
112
|
-
<EmbeddedCheckout eventId="14304"
|
|
192
|
+
<EmbeddedCheckout eventId="14304" />
|
|
113
193
|
</QueryClientProvider>
|
|
114
194
|
)
|
|
115
195
|
}
|
|
116
196
|
```
|
|
117
197
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
198
|
+
The SDK adds no Expo storage peer. Bare React Native hosts can implement the
|
|
199
|
+
same contract with their own OS-protected storage. The backend still validates
|
|
200
|
+
restored sessions; setup checks adapter shape but does not prove its security.
|
|
121
201
|
|
|
122
202
|
### Checkout sessions and account scope
|
|
123
203
|
|
|
@@ -126,12 +206,20 @@ cookie handling. Owned credentials use the SDK's header-backed session storage.
|
|
|
126
206
|
Public checkout sign-in preserves the event-bound guest carrier and keeps its
|
|
127
207
|
buyer credential server-side; it does not return a full-account ticket or grant
|
|
128
208
|
private account access. First-party full-account flows explicitly use
|
|
129
|
-
`accountAuth: true
|
|
209
|
+
`accountAuth: true` when the host is authorized for that scope.
|
|
130
210
|
|
|
131
211
|
### Environments
|
|
132
212
|
|
|
133
213
|
```ts
|
|
134
|
-
|
|
214
|
+
export async function prepareStagingCheckout() {
|
|
215
|
+
const storage = await restoreCredentialStorage('my_app_checkout_stag')
|
|
216
|
+
configureCheckout({
|
|
217
|
+
env: 'STAG',
|
|
218
|
+
clientId: 'PUBLIC_SDK_CLIENT_ID',
|
|
219
|
+
clientSecret: '',
|
|
220
|
+
storage,
|
|
221
|
+
})
|
|
222
|
+
}
|
|
135
223
|
```
|
|
136
224
|
|
|
137
225
|
`PROD` → `www.ticketfairy.com`, `STAG` → `test.ticketfairy.com`, `DEV` →
|
|
@@ -142,7 +230,7 @@ configureCheckout({ env: 'STAG', clientId: 'PUBLIC_SDK_CLIENT_ID', clientSecret:
|
|
|
142
230
|
| Prop | Type | Default | Does |
|
|
143
231
|
| ----------------------------------- | ----------------------------------- | ----------- | --------------------------------------------- |
|
|
144
232
|
| `eventId` | `string` | — | The event to sell. Required. |
|
|
145
|
-
| `onComplete` | `(order: PlaceOrderResult) => void` | — |
|
|
233
|
+
| `onComplete` | `(order: PlaceOrderResult) => void` | — | Receives an order result, possibly pending. |
|
|
146
234
|
| `confirmationMode` | `'inline' \| 'redirect'` | `'inline'` | `redirect` leaves routing to `onComplete`. |
|
|
147
235
|
| `initialView` | `'tickets' \| 'seats'` | `'tickets'` | Opening screen. |
|
|
148
236
|
| `onViewChange` | `(view) => void` | — | Reports the visible view. |
|
|
@@ -150,7 +238,7 @@ configureCheckout({ env: 'STAG', clientId: 'PUBLIC_SDK_CLIENT_ID', clientSecret:
|
|
|
150
238
|
| `collectTicketHolders` | `boolean` | `true` | One named holder per ticket. |
|
|
151
239
|
| `collectHolderContact` | `boolean` | `true` | Also collect holder email/phone. |
|
|
152
240
|
| `skipBillingWhenProfileComplete` | `boolean` | `false` | Skip billing for complete signed-in profiles. |
|
|
153
|
-
| `auth` | `
|
|
241
|
+
| `auth` | `EmbeddedCheckoutProps['auth']` | built-in | Configure or remove the login modal. |
|
|
154
242
|
| `onLogin` | `(email?: string) => void` | — | Open your own login UI instead. |
|
|
155
243
|
| `privacyPolicy` | `CheckoutPrivacyPolicy` | — | Your privacy policy link and brand name. |
|
|
156
244
|
| `additionalConditions` | `EventCondition[]` | — | Extra acknowledgements. |
|
|
@@ -163,6 +251,13 @@ configureCheckout({ env: 'STAG', clientId: 'PUBLIC_SDK_CLIENT_ID', clientSecret:
|
|
|
163
251
|
`readAttributionFromUrl` is accepted for parity with web and is a no-op —
|
|
164
252
|
there is no `window.location` on a device.
|
|
165
253
|
|
|
254
|
+
Native hosts must resolve and supply any supported promo or referral explicitly.
|
|
255
|
+
An ambiguous numeric `?r=` can represent either; do not send it as a referral
|
|
256
|
+
without authoritative classification. This beta also leaves conditional custom
|
|
257
|
+
field requiredness to the backend and does not reproduce every hosted-checkout
|
|
258
|
+
conditional visibility rule. Verify the event's field behavior before relying
|
|
259
|
+
on parity.
|
|
260
|
+
|
|
166
261
|
## Theming
|
|
167
262
|
|
|
168
263
|
```tsx
|
|
@@ -181,6 +276,7 @@ The same token names as web: color (`accent`, `surface`, `text`, `muted`,
|
|
|
181
276
|
Every section reads the same `useCheckout()` store from the provider.
|
|
182
277
|
|
|
183
278
|
```tsx
|
|
279
|
+
import { ScrollView } from 'react-native'
|
|
184
280
|
import {
|
|
185
281
|
CheckoutProvider,
|
|
186
282
|
Tickets,
|
|
@@ -188,14 +284,13 @@ import {
|
|
|
188
284
|
BillingInfo,
|
|
189
285
|
Payment,
|
|
190
286
|
OrderSummary,
|
|
191
|
-
useCheckout,
|
|
192
287
|
} from 'tf-checkout-react-native'
|
|
193
288
|
|
|
194
289
|
const MyCheckout = () => (
|
|
195
290
|
<CheckoutProvider theme={{ colorScheme: 'dark' }}>
|
|
196
291
|
<ScrollView>
|
|
197
|
-
<Tickets />
|
|
198
|
-
<AddOns />
|
|
292
|
+
<Tickets eventId="14304" />
|
|
293
|
+
<AddOns eventId="14304" />
|
|
199
294
|
<BillingInfo />
|
|
200
295
|
<OrderSummary />
|
|
201
296
|
<Payment />
|
|
@@ -213,39 +308,59 @@ const MyCheckout = () => (
|
|
|
213
308
|
**Auth** — `AuthModal`, `LoginForm`, `RegisterForm`, `ForgotPasswordForm`,
|
|
214
309
|
`ResetPasswordForm`.
|
|
215
310
|
|
|
311
|
+
## Payment methods
|
|
312
|
+
|
|
313
|
+
The payment section uses event and server eligibility plus device support to
|
|
314
|
+
select available methods. Stripe card entry uses the native card field by
|
|
315
|
+
default. A composed host can opt into Stripe PaymentSheet by supplying
|
|
316
|
+
`paymentSheet={{ merchantDisplayName, returnURL }}` and
|
|
317
|
+
`options={{ urlScheme }}` to `CheckoutProvider`; the registered return URL must
|
|
318
|
+
be `${urlScheme}://stripe-redirect` and the host must route Stripe callbacks.
|
|
319
|
+
The embedded component does not expose this opt-in.
|
|
320
|
+
|
|
321
|
+
This beta admits the sheet for eligible non-MXN Stripe PaymentIntents containing
|
|
322
|
+
card alone, or card plus Cash App Pay in USD; delayed methods are not admitted
|
|
323
|
+
in that sheet. MXN installments and new-card payment plans use the card field.
|
|
324
|
+
Stripe may offer Link when the account and intent qualify. Link is separate from
|
|
325
|
+
saved-card AutoFill, which is not guaranteed by the SDK or PaymentSheet.
|
|
326
|
+
Apple Pay and Google Pay require backend, device and merchant configuration;
|
|
327
|
+
the event's payment account and the host's wallet setup must be compatible.
|
|
328
|
+
Other gateway or redirect flows can require a provider page. Test each enabled
|
|
329
|
+
method on the intended account and device before relying on it.
|
|
330
|
+
|
|
216
331
|
## Headless
|
|
217
332
|
|
|
218
333
|
`useCheckout()` gives you the whole flow with no UI.
|
|
219
334
|
|
|
220
335
|
```tsx
|
|
221
|
-
|
|
336
|
+
import { Button } from 'react-native'
|
|
337
|
+
import { useCheckout, type TicketSelection } from 'tf-checkout-react-native'
|
|
222
338
|
|
|
223
|
-
|
|
224
|
-
checkout
|
|
225
|
-
|
|
226
|
-
|
|
339
|
+
function Reserve({ eventId, selections }: { eventId: string; selections: TicketSelection[] }) {
|
|
340
|
+
const checkout = useCheckout()
|
|
341
|
+
return <Button title="Reserve" onPress={() => { void checkout.setTickets(eventId, selections) }} />
|
|
342
|
+
}
|
|
227
343
|
```
|
|
228
344
|
|
|
229
|
-
`
|
|
230
|
-
|
|
345
|
+
Build `selections` from the current event catalog, then collect the applicable
|
|
346
|
+
billing, holder and consent data before calling `placeOrder`. The engine
|
|
347
|
+
coordinates concurrent placement and retains an order when a payment outcome
|
|
348
|
+
is uncertain. Confirm the authoritative order and provider status before
|
|
349
|
+
retrying or fulfilling; this is not a guarantee of exactly-once charging across
|
|
350
|
+
all hosts and providers.
|
|
231
351
|
|
|
232
352
|
### Starting a new checkout
|
|
233
353
|
|
|
234
|
-
`reset()`
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
of the order, such as an expired reservation, releases it.
|
|
245
|
-
|
|
246
|
-
To know which happened, await `resetWhenSettled()` instead. It resolves `true`
|
|
247
|
-
once the checkout is reset, or `false` when it kept the order. In that case,
|
|
248
|
-
show the buyer `PAID_ORDER_PENDING_MESSAGE`. While it waits for a payment
|
|
354
|
+
`reset()` returns nothing and starts a reset. If payment is in flight, the
|
|
355
|
+
engine waits for that operation; it retains an order when its recorded state
|
|
356
|
+
indicates money may have moved. A closed provider page or network error is not
|
|
357
|
+
proof that no payment occurred. Reconcile the order with the backend and payment
|
|
358
|
+
provider before retrying or fulfilling it.
|
|
359
|
+
|
|
360
|
+
To know whether reset completed, await `resetWhenSettled()` instead. It resolves
|
|
361
|
+
`true` after reset or `false` when reset cannot proceed, including a retained
|
|
362
|
+
order or a retired checkout. For a payment that may have moved, show the buyer
|
|
363
|
+
`PAID_ORDER_PENDING_MESSAGE` and reconcile it. While it waits for a payment
|
|
249
364
|
(`status === 'placing'`), tell the buyer, for example "Finishing your
|
|
250
365
|
payment…":
|
|
251
366
|
|
|
@@ -254,20 +369,25 @@ if (checkout.status === 'placing') setNotice('Finishing your payment…')
|
|
|
254
369
|
if (!(await checkout.resetWhenSettled())) setNotice(PAID_ORDER_PENDING_MESSAGE)
|
|
255
370
|
```
|
|
256
371
|
|
|
257
|
-
A custom `providerPage` must resolve `'dismiss'` when it closes or
|
|
258
|
-
|
|
372
|
+
A custom `providerPage` must resolve `{ event: 'dismiss' }` when it closes or
|
|
373
|
+
unmounts, and other custom ports must settle too, or `resetWhenSettled()` waits.
|
|
259
374
|
|
|
260
|
-
A custom confirmer
|
|
261
|
-
`paymentProviderError(message,
|
|
262
|
-
|
|
375
|
+
A custom confirmer can classify a failure with
|
|
376
|
+
`paymentProviderError(message, outcome)`, where `outcome` is `declined`,
|
|
377
|
+
`cancelled`, `notStarted` or `unknown`. Use the first three only when the
|
|
378
|
+
provider confirms that no charge occurred. An unknown outcome needs
|
|
379
|
+
reconciliation, not an assumption that payment failed.
|
|
263
380
|
|
|
264
|
-
The
|
|
265
|
-
|
|
381
|
+
The shared SDK exports are available from this package, including `ttf`, hooks,
|
|
382
|
+
types and the engine. The package's TypeScript declarations describe its public
|
|
383
|
+
exports.
|
|
266
384
|
|
|
267
385
|
## Rewards
|
|
268
386
|
|
|
269
387
|
The native analogue of the web Rewards section: standing, the referral tree,
|
|
270
388
|
the prize ladder, packs, the share card, and one share button.
|
|
389
|
+
Render standalone `Confirmation` within an active `CheckoutProvider`; the host
|
|
390
|
+
provides the `orderHash` used in this example.
|
|
271
391
|
|
|
272
392
|
```tsx
|
|
273
393
|
import { Confirmation, Rewards } from 'tf-checkout-react-native'
|
|
@@ -277,7 +397,8 @@ import { Confirmation, Rewards } from 'tf-checkout-react-native'
|
|
|
277
397
|
```
|
|
278
398
|
|
|
279
399
|
When `<EmbeddedCheckout/>` renders the receipt itself, pass the block through
|
|
280
|
-
`confirmationChildren
|
|
400
|
+
`confirmationChildren`. In this example the host supplies `eventId` and
|
|
401
|
+
`MyRewards`:
|
|
281
402
|
|
|
282
403
|
```tsx
|
|
283
404
|
<EmbeddedCheckout eventId={eventId} confirmationChildren={<MyRewards />} />
|
|
@@ -289,8 +410,7 @@ Native adaptations, and why:
|
|
|
289
410
|
web SVG uses. Adding `react-native-svg` would be a new native peer for every
|
|
290
411
|
customer app; a dot-and-line picture does not need one.
|
|
291
412
|
- **One share button.** Per-channel buttons collapse into `Share.share()` — the
|
|
292
|
-
OS sheet
|
|
293
|
-
sheet channel reaches for through the browser.
|
|
413
|
+
OS sheet presents the eligible share targets available on that device.
|
|
294
414
|
- **Copy comes in as props**, this package's idiom. `translate`, or `locale` and
|
|
295
415
|
`messages` for the built-in `rewards.*` catalogue.
|
|
296
416
|
|
|
@@ -299,13 +419,14 @@ Native adaptations, and why:
|
|
|
299
419
|
`SeatMap` uses the existing WebView peer to render internal charts or the event's
|
|
300
420
|
Seats.io chart, with the shared core owning selection, holds and reservations.
|
|
301
421
|
It requires the matching backend seating/session contracts. Provider-backed hold
|
|
302
|
-
and deployed-cart acceptance remain separate checks
|
|
303
|
-
[seating contract and evidence](https://github.com/theticketfairy/tf-shared-libraries/blob/main/docs/integration/app-sdk.md#seat-chart-and-ticket-refund-checkpoint--25-october-2026).
|
|
422
|
+
and deployed-cart acceptance remain separate checks.
|
|
304
423
|
|
|
305
|
-
## Known gaps vs
|
|
424
|
+
## Known gaps vs hosted checkout
|
|
306
425
|
|
|
307
|
-
|
|
308
|
-
|
|
426
|
+
Native URL attribution requires host-supplied values, conditional custom-field
|
|
427
|
+
visibility is not fully mirrored, and native smooth scroll is not equivalent to
|
|
428
|
+
web's `autoScroll`. Provider, wallet and device behavior also needs separate
|
|
429
|
+
qualification. The list is not a claim of full hosted or web SDK parity.
|
|
309
430
|
|
|
310
431
|
## Development
|
|
311
432
|
|
|
@@ -323,24 +444,22 @@ Both need a dev client. Build one with:
|
|
|
323
444
|
cd example && pnpm run ios
|
|
324
445
|
```
|
|
325
446
|
|
|
326
|
-
|
|
327
|
-
`
|
|
447
|
+
Use only public, non-secret configuration in `example/.env`
|
|
448
|
+
(`EXPO_PUBLIC_`-prefixed); never put a confidential credential in the app.
|
|
328
449
|
|
|
329
450
|
The example's `metro.config.js` pins React, react-query, and Stripe to single
|
|
330
451
|
copies. That is a **workspace-only** concern — it exists because the example
|
|
331
|
-
consumes the library through a symlink.
|
|
332
|
-
|
|
452
|
+
consumes the library through a symlink. An npm host should still verify that its
|
|
453
|
+
dependency tree resolves the intended single instances.
|
|
333
454
|
|
|
334
455
|
---
|
|
335
456
|
|
|
336
|
-
|
|
337
|
-
|
|
457
|
+
The checkout monorepo is available to authorised repository collaborators.
|
|
338
458
|
|
|
339
459
|
Public checkout sign-in uses event-scoped server-only buyer binding, without a bundled
|
|
340
460
|
client secret. The checkout's auth modal supplies the event automatically; standalone
|
|
341
461
|
`AuthModal` callers pass `checkoutEventId` and can receive the narrow
|
|
342
462
|
`onCheckoutAuthenticated(CheckoutBuyerProfile)` callback. Password/signup/email/phone
|
|
343
463
|
proofs still complete required two-factor verification. These credentials cannot read
|
|
344
|
-
private account preferences or tickets.
|
|
345
|
-
scope, retry, logout and configuration ownership. Existing confidential OAuth clients
|
|
464
|
+
private account preferences or tickets. Existing confidential OAuth clients
|
|
346
465
|
are trusted-server integrations; never distribute their secrets in browser/native code.
|