tf-checkout-react-native 2.0.0-beta.22 → 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 +201 -120
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,53 +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
|
-
## Prepared native storage change — beta.22
|
|
17
|
-
|
|
18
|
-
The prepared paired beta.22 prerelease requires an explicit synchronous credential-storage
|
|
19
|
-
adapter. Native `configureCheckout` and its root `configure` alias no longer create
|
|
20
|
-
an unencrypted MMKV store. The MMKV factory/default exports and peer dependency
|
|
21
|
-
are removed. This is a native beta breaking change; web defaults are unchanged.
|
|
22
|
-
See [beta.22 release preparation](../../docs/releases/2.0.0-beta.22.md) and
|
|
23
|
-
[secure native setup and migration](../../docs/integration/native-session-storage.md).
|
|
24
|
-
|
|
25
|
-
The replacement release is not published yet. The beta.21 receipt below remains
|
|
26
|
-
the last verified registry checkpoint; its published artifact still has the older
|
|
27
|
-
MMKV dependency. The following setup examples describe current source, not an
|
|
28
|
-
in-place change to an already published version.
|
|
29
|
-
|
|
30
18
|
## Install
|
|
31
19
|
|
|
32
|
-
|
|
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.
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
records exact archive/member/SHA/SRI proof. Both stable `latest` tags are unchanged.
|
|
38
|
-
Pin the exact version below; `@beta` follows the most recent prerelease. Publication
|
|
39
|
-
does not establish API rollout, app delivery or device/provider acceptance.
|
|
27
|
+
```bash
|
|
28
|
+
npm view tf-checkout-react-native dist-tags
|
|
29
|
+
```
|
|
40
30
|
|
|
41
31
|
This version preserves quantities across rotating encrypted ticket IDs using
|
|
42
32
|
the API's optional canonical `selectionKey`, while quantity/change APIs and cart
|
|
43
33
|
requests retain the latest encrypted IDs. Deploy and verify the canonical
|
|
44
34
|
base/dated key and dated numeric context before relying on that behavior:
|
|
45
35
|
`display_price`, `price_with_fees`, `fee_included`, `price_currency` and
|
|
46
|
-
`price_symbol`.
|
|
47
|
-
|
|
48
|
-
rotation-retention claim. The [beta.21 notes](https://github.com/theticketfairy/tf-shared-libraries/blob/main/docs/releases/2.0.0-beta.21.md)
|
|
49
|
-
and [app integration contract](https://github.com/theticketfairy/tf-shared-libraries/blob/main/docs/integration/app-sdk.md)
|
|
50
|
-
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.
|
|
51
38
|
|
|
52
39
|
Post-purchase upgrade waiting is a separate server-owned contract. Only valid
|
|
53
40
|
change-options targets can expose Join or Leave; malformed target IDs, labels
|
|
@@ -55,10 +42,10 @@ or existing entries are ignored. A sold-out row does not enable it. The backend
|
|
|
55
42
|
must explicitly offer that organizer-configured flow; local receiver validation
|
|
56
43
|
does not prove its deployed availability or a completed join.
|
|
57
44
|
|
|
58
|
-
|
|
45
|
+
For an Expo host with compatible React and React Native versions:
|
|
59
46
|
|
|
60
47
|
```bash
|
|
61
|
-
npx expo install tf-checkout-react-native@2.0.0-beta.
|
|
48
|
+
npx expo install tf-checkout-react-native@2.0.0-beta.23 \
|
|
62
49
|
@tanstack/react-query \
|
|
63
50
|
@stripe/stripe-react-native \
|
|
64
51
|
@react-native-clipboard/clipboard \
|
|
@@ -69,30 +56,31 @@ npx expo install tf-checkout-react-native@2.0.0-beta.22 \
|
|
|
69
56
|
Bare React Native:
|
|
70
57
|
|
|
71
58
|
```bash
|
|
72
|
-
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
|
|
73
60
|
```
|
|
74
61
|
|
|
75
|
-
These
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
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.
|
|
80
66
|
|
|
81
|
-
|
|
67
|
+
The private shared core and native UI are bundled; npm installs the package's
|
|
68
|
+
declared runtime dependencies separately.
|
|
82
69
|
|
|
83
|
-
|
|
70
|
+
This version requires the `react-native-render-html` peer `>=6.3.4 <7`
|
|
84
71
|
for formatted ticket, add-on, bundle and accommodation descriptions and its
|
|
85
72
|
public `RichText` renderer. Development and component tests use `6.3.4`; the
|
|
86
|
-
install commands above pin that compatible version.
|
|
87
|
-
|
|
73
|
+
install commands above pin that compatible version. Verify the rendering and
|
|
74
|
+
link behavior needed by your host on its supported devices.
|
|
88
75
|
|
|
89
76
|
## Requirements
|
|
90
77
|
|
|
91
78
|
- React Native `0.81+` with the New Architecture enabled (the supported build cohort)
|
|
92
79
|
- React `19.1+`, Node `20.19.4+`
|
|
93
|
-
- iOS 15.1+ · Android API 24+
|
|
94
|
-
- A development build
|
|
95
|
-
|
|
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.
|
|
96
84
|
|
|
97
85
|
Add the Stripe plugin to `app.json`:
|
|
98
86
|
|
|
@@ -112,25 +100,93 @@ npx expo run:ios
|
|
|
112
100
|
|
|
113
101
|
## Use it
|
|
114
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
|
+
|
|
115
157
|
```tsx
|
|
158
|
+
import { useEffect, useState } from 'react'
|
|
159
|
+
import { Button, Text, View } from 'react-native'
|
|
116
160
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
117
161
|
import { configureCheckout, EmbeddedCheckout } from 'tf-checkout-react-native'
|
|
118
162
|
import { restoreCredentialStorage } from './credentialStorage'
|
|
119
163
|
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
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
|
|
129
174
|
}
|
|
130
175
|
|
|
131
176
|
const queryClient = new QueryClient()
|
|
132
177
|
|
|
133
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>
|
|
134
190
|
return (
|
|
135
191
|
<QueryClientProvider client={queryClient}>
|
|
136
192
|
<EmbeddedCheckout eventId="14304" />
|
|
@@ -139,16 +195,9 @@ export default function App() {
|
|
|
139
195
|
}
|
|
140
196
|
```
|
|
141
197
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
Install `expo-secure-store` only if your host uses that example; the SDK adds no
|
|
146
|
-
Expo storage peer. Bare React Native hosts can use their own Keychain/Keystore
|
|
147
|
-
integration through the same `StorageAdapter` contract. The backend still validates
|
|
148
|
-
every restored session. Setup does not read/probe storage or prove its security.
|
|
149
|
-
The runnable example uses actual synchronous OS writes after hydration. If your
|
|
150
|
-
host chooses asynchronous writes instead, built-in auth completion does not await
|
|
151
|
-
the final flush automatically; follow the [host completion contract](../../docs/integration/native-session-storage.md).
|
|
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.
|
|
152
201
|
|
|
153
202
|
### Checkout sessions and account scope
|
|
154
203
|
|
|
@@ -157,7 +206,7 @@ cookie handling. Owned credentials use the SDK's header-backed session storage.
|
|
|
157
206
|
Public checkout sign-in preserves the event-bound guest carrier and keeps its
|
|
158
207
|
buyer credential server-side; it does not return a full-account ticket or grant
|
|
159
208
|
private account access. First-party full-account flows explicitly use
|
|
160
|
-
`accountAuth: true
|
|
209
|
+
`accountAuth: true` when the host is authorized for that scope.
|
|
161
210
|
|
|
162
211
|
### Environments
|
|
163
212
|
|
|
@@ -181,7 +230,7 @@ export async function prepareStagingCheckout() {
|
|
|
181
230
|
| Prop | Type | Default | Does |
|
|
182
231
|
| ----------------------------------- | ----------------------------------- | ----------- | --------------------------------------------- |
|
|
183
232
|
| `eventId` | `string` | — | The event to sell. Required. |
|
|
184
|
-
| `onComplete` | `(order: PlaceOrderResult) => void` | — |
|
|
233
|
+
| `onComplete` | `(order: PlaceOrderResult) => void` | — | Receives an order result, possibly pending. |
|
|
185
234
|
| `confirmationMode` | `'inline' \| 'redirect'` | `'inline'` | `redirect` leaves routing to `onComplete`. |
|
|
186
235
|
| `initialView` | `'tickets' \| 'seats'` | `'tickets'` | Opening screen. |
|
|
187
236
|
| `onViewChange` | `(view) => void` | — | Reports the visible view. |
|
|
@@ -189,7 +238,7 @@ export async function prepareStagingCheckout() {
|
|
|
189
238
|
| `collectTicketHolders` | `boolean` | `true` | One named holder per ticket. |
|
|
190
239
|
| `collectHolderContact` | `boolean` | `true` | Also collect holder email/phone. |
|
|
191
240
|
| `skipBillingWhenProfileComplete` | `boolean` | `false` | Skip billing for complete signed-in profiles. |
|
|
192
|
-
| `auth` | `
|
|
241
|
+
| `auth` | `EmbeddedCheckoutProps['auth']` | built-in | Configure or remove the login modal. |
|
|
193
242
|
| `onLogin` | `(email?: string) => void` | — | Open your own login UI instead. |
|
|
194
243
|
| `privacyPolicy` | `CheckoutPrivacyPolicy` | — | Your privacy policy link and brand name. |
|
|
195
244
|
| `additionalConditions` | `EventCondition[]` | — | Extra acknowledgements. |
|
|
@@ -202,6 +251,13 @@ export async function prepareStagingCheckout() {
|
|
|
202
251
|
`readAttributionFromUrl` is accepted for parity with web and is a no-op —
|
|
203
252
|
there is no `window.location` on a device.
|
|
204
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
|
+
|
|
205
261
|
## Theming
|
|
206
262
|
|
|
207
263
|
```tsx
|
|
@@ -220,6 +276,7 @@ The same token names as web: color (`accent`, `surface`, `text`, `muted`,
|
|
|
220
276
|
Every section reads the same `useCheckout()` store from the provider.
|
|
221
277
|
|
|
222
278
|
```tsx
|
|
279
|
+
import { ScrollView } from 'react-native'
|
|
223
280
|
import {
|
|
224
281
|
CheckoutProvider,
|
|
225
282
|
Tickets,
|
|
@@ -227,14 +284,13 @@ import {
|
|
|
227
284
|
BillingInfo,
|
|
228
285
|
Payment,
|
|
229
286
|
OrderSummary,
|
|
230
|
-
useCheckout,
|
|
231
287
|
} from 'tf-checkout-react-native'
|
|
232
288
|
|
|
233
289
|
const MyCheckout = () => (
|
|
234
290
|
<CheckoutProvider theme={{ colorScheme: 'dark' }}>
|
|
235
291
|
<ScrollView>
|
|
236
|
-
<Tickets />
|
|
237
|
-
<AddOns />
|
|
292
|
+
<Tickets eventId="14304" />
|
|
293
|
+
<AddOns eventId="14304" />
|
|
238
294
|
<BillingInfo />
|
|
239
295
|
<OrderSummary />
|
|
240
296
|
<Payment />
|
|
@@ -252,39 +308,59 @@ const MyCheckout = () => (
|
|
|
252
308
|
**Auth** — `AuthModal`, `LoginForm`, `RegisterForm`, `ForgotPasswordForm`,
|
|
253
309
|
`ResetPasswordForm`.
|
|
254
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
|
+
|
|
255
331
|
## Headless
|
|
256
332
|
|
|
257
333
|
`useCheckout()` gives you the whole flow with no UI.
|
|
258
334
|
|
|
259
335
|
```tsx
|
|
260
|
-
|
|
336
|
+
import { Button } from 'react-native'
|
|
337
|
+
import { useCheckout, type TicketSelection } from 'tf-checkout-react-native'
|
|
261
338
|
|
|
262
|
-
|
|
263
|
-
checkout
|
|
264
|
-
|
|
265
|
-
|
|
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
|
+
}
|
|
266
343
|
```
|
|
267
344
|
|
|
268
|
-
`
|
|
269
|
-
|
|
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.
|
|
270
351
|
|
|
271
352
|
### Starting a new checkout
|
|
272
353
|
|
|
273
|
-
`reset()`
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
of the order, such as an expired reservation, releases it.
|
|
284
|
-
|
|
285
|
-
To know which happened, await `resetWhenSettled()` instead. It resolves `true`
|
|
286
|
-
once the checkout is reset, or `false` when it kept the order. In that case,
|
|
287
|
-
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
|
|
288
364
|
(`status === 'placing'`), tell the buyer, for example "Finishing your
|
|
289
365
|
payment…":
|
|
290
366
|
|
|
@@ -293,20 +369,25 @@ if (checkout.status === 'placing') setNotice('Finishing your payment…')
|
|
|
293
369
|
if (!(await checkout.resetWhenSettled())) setNotice(PAID_ORDER_PENDING_MESSAGE)
|
|
294
370
|
```
|
|
295
371
|
|
|
296
|
-
A custom `providerPage` must resolve `'dismiss'` when it closes or
|
|
297
|
-
|
|
372
|
+
A custom `providerPage` must resolve `{ event: 'dismiss' }` when it closes or
|
|
373
|
+
unmounts, and other custom ports must settle too, or `resetWhenSettled()` waits.
|
|
298
374
|
|
|
299
|
-
A custom confirmer
|
|
300
|
-
`paymentProviderError(message,
|
|
301
|
-
|
|
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.
|
|
302
380
|
|
|
303
|
-
The
|
|
304
|
-
|
|
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.
|
|
305
384
|
|
|
306
385
|
## Rewards
|
|
307
386
|
|
|
308
387
|
The native analogue of the web Rewards section: standing, the referral tree,
|
|
309
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.
|
|
310
391
|
|
|
311
392
|
```tsx
|
|
312
393
|
import { Confirmation, Rewards } from 'tf-checkout-react-native'
|
|
@@ -316,7 +397,8 @@ import { Confirmation, Rewards } from 'tf-checkout-react-native'
|
|
|
316
397
|
```
|
|
317
398
|
|
|
318
399
|
When `<EmbeddedCheckout/>` renders the receipt itself, pass the block through
|
|
319
|
-
`confirmationChildren
|
|
400
|
+
`confirmationChildren`. In this example the host supplies `eventId` and
|
|
401
|
+
`MyRewards`:
|
|
320
402
|
|
|
321
403
|
```tsx
|
|
322
404
|
<EmbeddedCheckout eventId={eventId} confirmationChildren={<MyRewards />} />
|
|
@@ -328,8 +410,7 @@ Native adaptations, and why:
|
|
|
328
410
|
web SVG uses. Adding `react-native-svg` would be a new native peer for every
|
|
329
411
|
customer app; a dot-and-line picture does not need one.
|
|
330
412
|
- **One share button.** Per-channel buttons collapse into `Share.share()` — the
|
|
331
|
-
OS sheet
|
|
332
|
-
sheet channel reaches for through the browser.
|
|
413
|
+
OS sheet presents the eligible share targets available on that device.
|
|
333
414
|
- **Copy comes in as props**, this package's idiom. `translate`, or `locale` and
|
|
334
415
|
`messages` for the built-in `rewards.*` catalogue.
|
|
335
416
|
|
|
@@ -338,13 +419,14 @@ Native adaptations, and why:
|
|
|
338
419
|
`SeatMap` uses the existing WebView peer to render internal charts or the event's
|
|
339
420
|
Seats.io chart, with the shared core owning selection, holds and reservations.
|
|
340
421
|
It requires the matching backend seating/session contracts. Provider-backed hold
|
|
341
|
-
and deployed-cart acceptance remain separate checks
|
|
342
|
-
[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.
|
|
343
423
|
|
|
344
|
-
## Known gaps vs
|
|
424
|
+
## Known gaps vs hosted checkout
|
|
345
425
|
|
|
346
|
-
|
|
347
|
-
|
|
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.
|
|
348
430
|
|
|
349
431
|
## Development
|
|
350
432
|
|
|
@@ -362,23 +444,22 @@ Both need a dev client. Build one with:
|
|
|
362
444
|
cd example && pnpm run ios
|
|
363
445
|
```
|
|
364
446
|
|
|
365
|
-
|
|
366
|
-
`
|
|
447
|
+
Use only public, non-secret configuration in `example/.env`
|
|
448
|
+
(`EXPO_PUBLIC_`-prefixed); never put a confidential credential in the app.
|
|
367
449
|
|
|
368
450
|
The example's `metro.config.js` pins React, react-query, and Stripe to single
|
|
369
451
|
copies. That is a **workspace-only** concern — it exists because the example
|
|
370
|
-
consumes the library through a symlink.
|
|
371
|
-
|
|
452
|
+
consumes the library through a symlink. An npm host should still verify that its
|
|
453
|
+
dependency tree resolves the intended single instances.
|
|
372
454
|
|
|
373
455
|
---
|
|
374
456
|
|
|
375
|
-
|
|
457
|
+
The checkout monorepo is available to authorised repository collaborators.
|
|
376
458
|
|
|
377
459
|
Public checkout sign-in uses event-scoped server-only buyer binding, without a bundled
|
|
378
460
|
client secret. The checkout's auth modal supplies the event automatically; standalone
|
|
379
461
|
`AuthModal` callers pass `checkoutEventId` and can receive the narrow
|
|
380
462
|
`onCheckoutAuthenticated(CheckoutBuyerProfile)` callback. Password/signup/email/phone
|
|
381
463
|
proofs still complete required two-factor verification. These credentials cannot read
|
|
382
|
-
private account preferences or tickets.
|
|
383
|
-
scope, retry, logout and configuration ownership. Existing confidential OAuth clients
|
|
464
|
+
private account preferences or tickets. Existing confidential OAuth clients
|
|
384
465
|
are trusted-server integrations; never distribute their secrets in browser/native code.
|
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.23",
|
|
4
4
|
"description": "TheTicketFairy ReactNative Checkout library",
|
|
5
5
|
"homepage": "https://github.com/theticketfairy/tf-shared-libraries#readme",
|
|
6
6
|
"author": "The Ticket Fairy",
|