tf-checkout-react-native 2.0.0-beta.23 → 2.0.0-beta.24
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 +896 -366
- package/docs/API.md +602 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,133 +1,126 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
The
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
checkout
|
|
17
|
-
|
|
18
|
-
##
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
# Ticket Fairy Checkout for React Native
|
|
2
|
+
|
|
3
|
+
Native React components and a headless checkout engine for selling Ticket Fairy
|
|
4
|
+
tickets. Start with `EmbeddedCheckout`, compose individual sections, or own the
|
|
5
|
+
presentation using `useCheckout` and the shared API. The package includes its
|
|
6
|
+
private shared core; hosts do not install `tf-checkout-shared`.
|
|
7
|
+
|
|
8
|
+
This reference is for **`tf-checkout-react-native@2.0.0-beta.24`**, a
|
|
9
|
+
prerelease with a different API from 1.x (including its `CheckoutV2`). Pin the
|
|
10
|
+
version: `latest` and `beta` tags can move; check `npm view tf-checkout-react-native dist-tags`.
|
|
11
|
+
The [API reference](https://unpkg.com/tf-checkout-react-native@2.0.0-beta.24/docs/API.md)
|
|
12
|
+
covers components and hooks; the [declarations](https://unpkg.com/tf-checkout-react-native@2.0.0-beta.24/dist/index.d.ts)
|
|
13
|
+
contain all types. The [web package](https://www.npmjs.com/package/tf-checkout-react/v/2.0.0-beta.24)
|
|
14
|
+
shares the engine but has different UI and ports. Availability depends on the
|
|
15
|
+
backend, event, gateway, account and device; installation does not establish
|
|
16
|
+
hosted-checkout parity or payment acceptance.
|
|
17
|
+
|
|
18
|
+
## Contents
|
|
19
|
+
|
|
20
|
+
- [Requirements and installation](#requirements-and-installation)
|
|
21
|
+
- [Initialize secure storage and configure](#initialize-secure-storage-and-configure)
|
|
22
|
+
- [Embed a checkout](#embed-a-checkout)
|
|
23
|
+
- [Compose sections and navigation](#compose-sections-and-navigation)
|
|
24
|
+
- [Checkout section reference](#checkout-section-reference)
|
|
25
|
+
- [Tickets, seats, codes and attribution](#tickets-seats-codes-and-attribution)
|
|
26
|
+
- [Billing, custom fields and consent](#billing-custom-fields-and-consent)
|
|
27
|
+
- [Payments and safe completion](#payments-and-safe-completion)
|
|
28
|
+
- [Headless checkout](#headless-checkout)
|
|
29
|
+
- [Hooks and API](#hooks-and-api)
|
|
30
|
+
- [Authentication and account components](#authentication-and-account-components)
|
|
31
|
+
- [Theme, primitives and localization](#theme-primitives-and-localization)
|
|
32
|
+
- [Migration from 1.x](#migration-from-1x)
|
|
33
|
+
- [Testing and troubleshooting](#testing-and-troubleshooting)
|
|
34
|
+
|
|
35
|
+
## Requirements and installation
|
|
36
|
+
|
|
37
|
+
The package declares Node `>=20.19.4`, React `>=19.1`, React Native `>=0.81`,
|
|
38
|
+
`@tanstack/react-query >=5`, `@stripe/stripe-react-native >=0.45.0`,
|
|
39
|
+
`@react-native-clipboard/clipboard >=1.14.0`, `react-native-webview >=13.15.0`,
|
|
40
|
+
and `react-native-render-html >=6.3.4 <7` as host requirements or peers. The
|
|
41
|
+
supported RN 0.81 cohort uses the New Architecture, iOS 15.1+ and Android API
|
|
42
|
+
24+. Check the native dependencies' own requirements and the versions supported
|
|
43
|
+
by your app; a peer range is not proof that every combination has been tested.
|
|
44
|
+
The package itself declares `axios`, `entities` and `htmlparser2` as runtime
|
|
45
|
+
dependencies. Use one copy of React, React Query and each native module in the
|
|
46
|
+
host. `npm ls react @tanstack/react-query @stripe/stripe-react-native` helps
|
|
47
|
+
identify duplicates.
|
|
48
|
+
|
|
49
|
+
For an Expo project with compatible React and RN versions:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
npx expo install tf-checkout-react-native@2.0.0-beta.24 \
|
|
53
|
+
@tanstack/react-query @stripe/stripe-react-native \
|
|
54
|
+
@react-native-clipboard/clipboard react-native-webview \
|
|
55
|
+
react-native-render-html@6.3.4 expo-secure-store
|
|
29
56
|
```
|
|
30
57
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
rotation-retention claim. The deployed API must provide and validate that data.
|
|
38
|
-
|
|
39
|
-
Post-purchase upgrade waiting is a separate server-owned contract. Only valid
|
|
40
|
-
change-options targets can expose Join or Leave; malformed target IDs, labels
|
|
41
|
-
or existing entries are ignored. A sold-out row does not enable it. The backend
|
|
42
|
-
must explicitly offer that organizer-configured flow; local receiver validation
|
|
43
|
-
does not prove its deployed availability or a completed join.
|
|
44
|
-
|
|
45
|
-
For an Expo host with compatible React and React Native versions:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
npx expo install tf-checkout-react-native@2.0.0-beta.23 \
|
|
49
|
-
@tanstack/react-query \
|
|
50
|
-
@stripe/stripe-react-native \
|
|
51
|
-
@react-native-clipboard/clipboard \
|
|
52
|
-
react-native-webview \
|
|
53
|
-
react-native-render-html@6.3.4
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Bare React Native:
|
|
57
|
-
|
|
58
|
-
```bash
|
|
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
|
|
60
|
-
```
|
|
61
|
-
|
|
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.
|
|
66
|
-
|
|
67
|
-
The private shared core and native UI are bundled; npm installs the package's
|
|
68
|
-
declared runtime dependencies separately.
|
|
69
|
-
|
|
70
|
-
This version requires the `react-native-render-html` peer `>=6.3.4 <7`
|
|
71
|
-
for formatted ticket, add-on, bundle and accommodation descriptions and its
|
|
72
|
-
public `RichText` renderer. Development and component tests use `6.3.4`; the
|
|
73
|
-
install commands above pin that compatible version. Verify the rendering and
|
|
74
|
-
link behavior needed by your host on its supported devices.
|
|
75
|
-
|
|
76
|
-
## Requirements
|
|
77
|
-
|
|
78
|
-
- React Native `0.81+` with the New Architecture enabled (the supported build cohort)
|
|
79
|
-
- React `19.1+`, Node `20.19.4+`
|
|
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.
|
|
84
|
-
|
|
85
|
-
Add the Stripe plugin to `app.json`:
|
|
58
|
+
The last package is used by the storage example below; the SDK does not install
|
|
59
|
+
or require Expo SecureStore. Build a development client containing the native
|
|
60
|
+
modules. Complete checkout compatibility with Expo Go is not established. Add
|
|
61
|
+
the Stripe config plugin to the app configuration, choosing wallet settings for
|
|
62
|
+
your own app, and rebuild after changing native configuration. For example,
|
|
63
|
+
this starts without Google Pay:
|
|
86
64
|
|
|
87
65
|
```json
|
|
88
66
|
{
|
|
89
67
|
"expo": {
|
|
68
|
+
"scheme": "myapp",
|
|
90
69
|
"plugins": [["@stripe/stripe-react-native", { "enableGooglePay": false }]]
|
|
91
70
|
}
|
|
92
71
|
}
|
|
93
72
|
```
|
|
94
73
|
|
|
95
|
-
|
|
74
|
+
Run `npx expo run:ios` or `npx expo run:android`, or make a development build
|
|
75
|
+
using your normal process. For a bare React Native app, install the peers in
|
|
76
|
+
the app (React and React Native are normally already present):
|
|
96
77
|
|
|
97
|
-
```
|
|
98
|
-
|
|
78
|
+
```sh
|
|
79
|
+
npm install tf-checkout-react-native@2.0.0-beta.24 @tanstack/react-query \
|
|
80
|
+
@stripe/stripe-react-native @react-native-clipboard/clipboard \
|
|
81
|
+
react-native-webview react-native-render-html@6.3.4
|
|
99
82
|
```
|
|
100
83
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
The
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
84
|
+
Configure autolinking and Stripe's native setup, run `pod install` in the iOS
|
|
85
|
+
directory, and rebuild both platforms. Provide a Keychain/Keystore-backed
|
|
86
|
+
storage adapter using a native library appropriate to the host. Consult the
|
|
87
|
+
[Stripe React Native setup](https://docs.stripe.com/payments/accept-a-payment?platform=react-native)
|
|
88
|
+
and [Expo SecureStore documentation](https://docs.expo.dev/versions/latest/sdk/securestore/)
|
|
89
|
+
for native configuration and platform behavior. Configure wallet entitlements,
|
|
90
|
+
merchant registration and deep links separately if those features are used.
|
|
91
|
+
|
|
92
|
+
## Initialize secure storage and configure
|
|
93
|
+
|
|
94
|
+
`configureCheckout(config)` is synchronous and must run **after credential
|
|
95
|
+
storage is restored**, before any SDK API call, hook or component mounts. Its
|
|
96
|
+
root export alias `configure` has the same native requirements. Configuration
|
|
97
|
+
is app-wide. Set up one environment and buyer session at a time; avoid
|
|
98
|
+
reconfiguring during checkout or payment.
|
|
99
|
+
|
|
100
|
+
`storage` is required. It has synchronous `getItem(key): string | null |
|
|
101
|
+
undefined`, `setItem(key, value): void`, `removeItem(key): void`, and optional
|
|
102
|
+
`flush(): Promise<void>`. The wrapper checks method shape; it does not certify
|
|
103
|
+
encryption, persistence, isolation, backup or correctness. `createMemoryStorage`
|
|
104
|
+
is exported for tests and loses credentials at process exit; it is not a
|
|
105
|
+
production secure store. Use separate storage namespaces per app/environment.
|
|
106
|
+
Never put a confidential OAuth client secret or provider secret key in mobile
|
|
107
|
+
code. Obtain a public SDK client ID authorized for the integration and use an
|
|
108
|
+
empty `clientSecret` for public checkout; do not substitute a confidential
|
|
109
|
+
credential. Environment variables compiled into an app are visible to its users.
|
|
110
|
+
|
|
111
|
+
The SDK reads storage synchronously. The following Expo example asynchronously
|
|
112
|
+
restores one protected snapshot first, then makes each subsequent OS write
|
|
113
|
+
synchronously *before* publishing the new in-memory snapshot. The methods are
|
|
114
|
+
not `async` and a failed write throws instead of reporting success. Replace the
|
|
115
|
+
namespace with an app-owned, environment-specific key.
|
|
123
116
|
|
|
124
117
|
```ts
|
|
125
118
|
// credentialStorage.ts
|
|
126
119
|
import * as SecureStore from 'expo-secure-store'
|
|
127
120
|
import type { StorageAdapter } from 'tf-checkout-react-native'
|
|
128
121
|
|
|
129
|
-
export async function restoreCredentialStorage(
|
|
130
|
-
const raw = await SecureStore.getItemAsync(
|
|
122
|
+
export async function restoreCredentialStorage(key: string): Promise<StorageAdapter> {
|
|
123
|
+
const raw = await SecureStore.getItemAsync(key)
|
|
131
124
|
const parsed: unknown = raw === null ? {} : JSON.parse(raw)
|
|
132
125
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) ||
|
|
133
126
|
Object.values(parsed).some(value => typeof value !== 'string')) {
|
|
@@ -135,331 +128,868 @@ export async function restoreCredentialStorage(namespace: string): Promise<Stora
|
|
|
135
128
|
}
|
|
136
129
|
let values = new Map(Object.entries(parsed as Record<string, string>))
|
|
137
130
|
const commit = (next: Map<string, string>) => {
|
|
138
|
-
SecureStore.setItem(
|
|
131
|
+
SecureStore.setItem(key, JSON.stringify(Object.fromEntries(next)), {
|
|
139
132
|
keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
|
140
133
|
requireAuthentication: false,
|
|
141
134
|
})
|
|
142
135
|
values = next
|
|
143
136
|
}
|
|
144
137
|
return {
|
|
145
|
-
getItem:
|
|
146
|
-
setItem: (
|
|
147
|
-
removeItem:
|
|
138
|
+
getItem: name => values.get(name) ?? null,
|
|
139
|
+
setItem: (name, value) => { const next = new Map(values); next.set(name, value); commit(next) },
|
|
140
|
+
removeItem: name => { const next = new Map(values); next.delete(name); commit(next) },
|
|
148
141
|
}
|
|
149
142
|
}
|
|
150
143
|
```
|
|
151
144
|
|
|
152
|
-
This
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
145
|
+
This illustrative adapter does not request biometric authentication. Account
|
|
146
|
+
for OS storage size limits, backup/restore policy, device locking and write
|
|
147
|
+
failures in the host; synchronous storage can block the JS thread. If your
|
|
148
|
+
persistence API is asynchronous, hydrate a synchronous mirror before setup,
|
|
149
|
+
serialize its writes, implement `flush`, and await the final flush after every
|
|
150
|
+
credential-changing operation before telling the user that persistence
|
|
151
|
+
succeeded. Built-in auth completion does not await that final flush for you.
|
|
152
|
+
Do not pass promises or async functions as the three synchronous methods. A
|
|
153
|
+
corrupt snapshot or unavailable OS store must be handled explicitly; do not
|
|
154
|
+
silently report durable sign-in. Migrate old credentials securely or require
|
|
155
|
+
sign-in again.
|
|
156
156
|
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
import {
|
|
160
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
161
|
-
import { configureCheckout, EmbeddedCheckout } from 'tf-checkout-react-native'
|
|
157
|
+
```ts
|
|
158
|
+
// checkoutSetup.ts
|
|
159
|
+
import { configureCheckout } from 'tf-checkout-react-native'
|
|
162
160
|
import { restoreCredentialStorage } from './credentialStorage'
|
|
163
161
|
|
|
164
|
-
// Share initialization across repeated effects and permit retry after failure.
|
|
165
162
|
let initialization: Promise<void> | undefined
|
|
166
|
-
function prepareCheckout(): Promise<void> {
|
|
163
|
+
export function prepareCheckout(): Promise<void> {
|
|
167
164
|
if (!initialization) {
|
|
168
165
|
initialization = (async () => {
|
|
169
166
|
const storage = await restoreCredentialStorage('my_app_checkout_prod')
|
|
170
|
-
configureCheckout({
|
|
167
|
+
configureCheckout({
|
|
168
|
+
env: 'PROD',
|
|
169
|
+
clientId: 'YOUR_PUBLIC_SDK_CLIENT_ID',
|
|
170
|
+
clientSecret: '',
|
|
171
|
+
storage,
|
|
172
|
+
})
|
|
171
173
|
})().catch(error => { initialization = undefined; throw error })
|
|
172
174
|
}
|
|
173
175
|
return initialization
|
|
174
176
|
}
|
|
177
|
+
```
|
|
175
178
|
|
|
176
|
-
|
|
179
|
+
Configuration fields:
|
|
180
|
+
|
|
181
|
+
| Field | Meaning |
|
|
182
|
+
| --- | --- |
|
|
183
|
+
| `clientId`, `clientSecret` | Required strings. Public checkout uses a registered public SDK ID and empty secret. |
|
|
184
|
+
| `storage` | Required host-owned synchronous `StorageAdapter`, restored before setup. |
|
|
185
|
+
| `env` | `PROD` (default, `www.ticketfairy.com`), `STAG` (`test.ticketfairy.com`) or `DEV` (`sandbox.ticketfairy.com`). |
|
|
186
|
+
| `baseUrl` | Optional host origin overriding `env`; the SDK appends `/api`. Use only a trusted host. |
|
|
187
|
+
| `sourceOrigin` | Optional `X-Source-Origin` header value. |
|
|
188
|
+
| `auth` | Optional synchronous callback returning additional request headers. Treat credentials in headers as sensitive; the native wrapper also forces header-only session mode and no-store/no-cache. |
|
|
189
|
+
| `cookieAuth` | Defaults to `true` on native. The native wrapper forces `withCredentials: false`; its header-backed session is stored via the adapter, rather than relying on ambient OS cookies. |
|
|
190
|
+
| `accountAuth` | Opt in only for an authorized first-party full-account journey and deployed backend. Defaults off. |
|
|
191
|
+
| `browserAuth`, `sdkCheckoutBrowserAuth` | Optional platform ports for supported browser authentication. The host owns secure proof generation and browser/callback handling. |
|
|
192
|
+
| `sdkCheckoutPhoneAuth` | Opt in only when the corresponding first-party checkout phone journey is deployed. |
|
|
193
|
+
|
|
194
|
+
A restored session is still validated by the backend. Public checkout sign-in
|
|
195
|
+
preserves the event-bound guest cart and keeps the checkout buyer credential
|
|
196
|
+
server-side; it does not return a full-account ticket or grant private account
|
|
197
|
+
access. Do not infer full account authorization from checkout authentication.
|
|
198
|
+
|
|
199
|
+
## Embed a checkout
|
|
200
|
+
|
|
201
|
+
Mount only after `prepareCheckout` resolves. `EmbeddedCheckout` provides its own
|
|
202
|
+
`CheckoutProvider` and React Query client. Any separate React Query hooks outside
|
|
203
|
+
that subtree need a `QueryClientProvider` of their own.
|
|
177
204
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
205
|
+
```tsx
|
|
206
|
+
import React, { useEffect, useState } from 'react'
|
|
207
|
+
import { Button, Text, View } from 'react-native'
|
|
208
|
+
import { EmbeddedCheckout, type PlaceOrderResult } from 'tf-checkout-react-native'
|
|
209
|
+
import { prepareCheckout } from './checkoutSetup'
|
|
210
|
+
|
|
211
|
+
export default function EventCheckout() {
|
|
212
|
+
const [status, setStatus] = useState<'loading' | 'ready' | 'error'>('loading')
|
|
181
213
|
const [attempt, setAttempt] = useState(0)
|
|
214
|
+
const [order, setOrder] = useState<PlaceOrderResult | null>(null)
|
|
182
215
|
useEffect(() => {
|
|
183
216
|
let active = true
|
|
184
|
-
prepareCheckout().then(() => { if (active)
|
|
185
|
-
.catch(() => { if (active)
|
|
217
|
+
prepareCheckout().then(() => { if (active) setStatus('ready') })
|
|
218
|
+
.catch(() => { if (active) setStatus('error') })
|
|
186
219
|
return () => { active = false }
|
|
187
220
|
}, [attempt])
|
|
188
|
-
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
221
|
+
|
|
222
|
+
if (status === 'error') return (
|
|
223
|
+
<View><Text>Could not prepare checkout.</Text>
|
|
224
|
+
<Button title="Try again" onPress={() => { setStatus('loading'); setAttempt(n => n + 1) }} />
|
|
225
|
+
</View>
|
|
226
|
+
)
|
|
227
|
+
if (status !== 'ready') return <Text>Preparing checkout…</Text>
|
|
228
|
+
|
|
229
|
+
if (order) return (
|
|
230
|
+
<View>
|
|
231
|
+
<Text>Order submitted: {order.orderHash}. Check payment status before fulfillment.</Text>
|
|
232
|
+
{order.pending && <Text>Payment pending.</Text>}
|
|
233
|
+
{order.pendingVoucherUrl && <Text selectable>{order.pendingVoucherUrl}</Text>}
|
|
234
|
+
{order.needIdVerification && <Text>Identity verification is required.</Text>}
|
|
235
|
+
{order.free && <Text>Check if registration needs approval or verification.</Text>}
|
|
236
|
+
</View>
|
|
194
237
|
)
|
|
238
|
+
return <EmbeddedCheckout eventId="YOUR_EVENT_ID" confirmationMode="redirect"
|
|
239
|
+
onComplete={setOrder} />
|
|
195
240
|
}
|
|
196
241
|
```
|
|
197
242
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
243
|
+
`onComplete` is an order result, not proof of settled payment. `pending: true`
|
|
244
|
+
requires pending instructions, but `pending !== true` also does not prove
|
|
245
|
+
capture or settlement. `confirmationMode="inline"` (default) shows the stock
|
|
246
|
+
receipt, which can say tickets are confirmed for a non-pending order before
|
|
247
|
+
final capture/settlement. Hosts requiring verified paid status should use
|
|
248
|
+
`"redirect"` or custom UI and reconcile with the authoritative backend and
|
|
249
|
+
provider before presenting that status or fulfilling. Redirect mode delegates
|
|
250
|
+
routing to `onComplete` and keeps checkout mounted during handoff; the host owns
|
|
251
|
+
navigation. The example shows a local fallback, not a complete status screen:
|
|
252
|
+
the host must implement reconciliation, retain the order reference and voucher
|
|
253
|
+
across its navigation/restart, and arrange verification when required. Validate
|
|
254
|
+
provider URLs against the host's policy before opening them.
|
|
255
|
+
|
|
256
|
+
### `EmbeddedCheckout` props
|
|
257
|
+
|
|
258
|
+
| Prop | Default | Purpose |
|
|
259
|
+
| --- | --- | --- |
|
|
260
|
+
| `eventId: string` | required | Event to sell. |
|
|
261
|
+
| `onComplete?: (order: PlaceOrderResult) => void` | — | Reports ticket or bundle result; reconcile payment status, even if not marked pending. |
|
|
262
|
+
| `confirmationMode?: 'inline' \| 'redirect'` | `inline` | Show receipt inline or let host route. |
|
|
263
|
+
| `confirmationChildren?: ReactNode` | — | Content inside the inline ticket receipt, e.g. `Rewards`. |
|
|
264
|
+
| `initialView?: 'tickets' \| 'seats'` | `tickets` | Initial selection screen. |
|
|
265
|
+
| `onViewChange?: (view) => void` | — | Reports `tickets`, `seats` or `checkout`. |
|
|
266
|
+
| `requireEmailConfirmation?: boolean` | `true` | Require matching guest email confirmation. |
|
|
267
|
+
| `collectTicketHolders?: boolean` | `true` | Collect one named holder per ticket. |
|
|
268
|
+
| `collectHolderContact?: boolean` | `true` | Also collect holder email/phone when holder details are shown. |
|
|
269
|
+
| `skipBillingWhenProfileComplete?: boolean` | `false` | May hide billing for a complete profile; see the privacy-policy interaction below. |
|
|
270
|
+
| `onLogin?: (email?: string) => void` | — | Use host login instead of the built-in modal. |
|
|
271
|
+
| `auth?: false \| object` | built-in | `false` removes built-in auth; otherwise supports `allowRegistration`, `allowPasswordReset`, `requireRegistrationAddress`, `logoUrl`, `onCheckoutAuthenticated` and `onAuthenticated`. `onLogin` takes precedence. |
|
|
272
|
+
| `privacyPolicy?: CheckoutPrivacyPolicy` | — | Host policy: `{ url, brandName, linkLabel? }`; requires acknowledgment. |
|
|
273
|
+
| `additionalConditions?: EventCondition[]` | `[]` | Extra required/optional acknowledgment blocks. |
|
|
274
|
+
| `ttfOptInLabel`, `brandOptInLabel` | SDK copy / absent | Label overrides; `privacyPolicy` reuses the Ticket Fairy opt-in value. See consent limitations below. |
|
|
275
|
+
| `initialPromoCode?: string`, `referrer?: string \| number` | — | Explicit attribution supplied by the host. |
|
|
276
|
+
| `readAttributionFromUrl?: boolean` | accepted, no-op | Native has no browser URL to read. |
|
|
277
|
+
| `pickImage?: CheckoutImagePicker` | — | Host OS image picker for applicable questions. |
|
|
278
|
+
| `showTestCardHint?: boolean` | `false` | Sandbox demo copy only; keep off in production. |
|
|
279
|
+
| `theme?: CheckoutTheme` | light tokens | Partial token overrides. |
|
|
280
|
+
|
|
281
|
+
`pickImage` is `() => Promise<{ base64: string; width?: number; height?: number } | null>`.
|
|
282
|
+
Return raw base64, not a data URI, or `null` for cancellation. The SDK checks
|
|
283
|
+
base64 encoding and the image signature, the 2 MiB maximum and any supplied
|
|
284
|
+
dimensions (at most 4096 pixels per side); the server validates decoded bytes.
|
|
285
|
+
The host owns permission and picker UX. For the separate event discussion picker, see its props below.
|
|
286
|
+
|
|
287
|
+
## Compose sections and navigation
|
|
288
|
+
|
|
289
|
+
`CheckoutProvider` owns a native `useCheckout()` instance, a query provider,
|
|
290
|
+
theme and the built-in provider-page and installment modals. Sections inside
|
|
291
|
+
it share the app checkout store. `useCheckoutContext()` reads this instance and
|
|
292
|
+
throws outside the provider. The following intentionally uses one combined
|
|
293
|
+
billing/payment form after reservation; callbacks route tickets and seats.
|
|
201
294
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
295
|
+
```tsx
|
|
296
|
+
import React, { useState } from 'react'
|
|
297
|
+
import { ScrollView, Text, View } from 'react-native'
|
|
298
|
+
import {
|
|
299
|
+
CheckoutProvider, Tickets, SeatMap, AddOns, BillingInfo, WaiverSigning,
|
|
300
|
+
OrderSummary, Payment, IDVerification, useCheckoutContext,
|
|
301
|
+
type PlaceOrderResult,
|
|
302
|
+
} from 'tf-checkout-react-native'
|
|
210
303
|
|
|
211
|
-
|
|
304
|
+
function Flow({ eventId, onComplete }: {
|
|
305
|
+
eventId: string
|
|
306
|
+
onComplete: (order: PlaceOrderResult) => void
|
|
307
|
+
}) {
|
|
308
|
+
const [view, setView] = useState<'tickets' | 'seats' | 'checkout'>('tickets')
|
|
309
|
+
const checkout = useCheckoutContext()
|
|
310
|
+
if (checkout.status === 'succeeded' && checkout.order) return (
|
|
311
|
+
<View>
|
|
312
|
+
<Text>Order submitted: {checkout.order.orderHash}. Check payment status.</Text>
|
|
313
|
+
{checkout.order.pending && <Text>Payment pending.</Text>}
|
|
314
|
+
{checkout.order.pendingVoucherUrl && <Text selectable>{checkout.order.pendingVoucherUrl}</Text>}
|
|
315
|
+
<IDVerification />
|
|
316
|
+
</View>
|
|
317
|
+
)
|
|
318
|
+
return (
|
|
319
|
+
<ScrollView keyboardShouldPersistTaps="handled">
|
|
320
|
+
{view === 'tickets' && <Tickets eventId={eventId}
|
|
321
|
+
onSelectSeats={() => setView('seats')}
|
|
322
|
+
onContinue={() => setView('checkout')} />}
|
|
323
|
+
{view === 'seats' && <SeatMap eventId={eventId}
|
|
324
|
+
onContinue={() => setView('checkout')} />}
|
|
325
|
+
{view === 'checkout' && <>
|
|
326
|
+
<AddOns eventId={eventId} />
|
|
327
|
+
<BillingInfo eventId={eventId} requireEmailConfirmation
|
|
328
|
+
collectTicketHolders collectHolderContact />
|
|
329
|
+
<WaiverSigning eventId={eventId} />
|
|
330
|
+
<OrderSummary eventId={eventId} />
|
|
331
|
+
<Payment requireEmailConfirmation collectTicketHolders requireHolderContact
|
|
332
|
+
onComplete={onComplete} />
|
|
333
|
+
</>}
|
|
334
|
+
</ScrollView>
|
|
335
|
+
)
|
|
336
|
+
}
|
|
212
337
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
clientId: 'PUBLIC_SDK_CLIENT_ID',
|
|
219
|
-
clientSecret: '',
|
|
220
|
-
storage,
|
|
221
|
-
})
|
|
338
|
+
export function ComposedCheckout({ eventId, onComplete }: {
|
|
339
|
+
eventId: string
|
|
340
|
+
onComplete: (order: PlaceOrderResult) => void
|
|
341
|
+
}) {
|
|
342
|
+
return <CheckoutProvider><Flow eventId={eventId} onComplete={onComplete} /></CheckoutProvider>
|
|
222
343
|
}
|
|
223
344
|
```
|
|
224
345
|
|
|
225
|
-
`
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
346
|
+
`AddOns` can render nothing when there are no applicable items; do not make a
|
|
347
|
+
navigation transition depend solely on its `onContinue` unless your host handles
|
|
348
|
+
that case. With no `onContinue`, `AddOns` and `BillingInfo` use combined-form
|
|
349
|
+
behavior. Match `BillingInfo`'s email/holder policy to `Payment`'s
|
|
350
|
+
`requireEmailConfirmation`, `collectTicketHolders` and `requireHolderContact`.
|
|
351
|
+
The composed example renders the verification control for a required order;
|
|
352
|
+
the buyer must press Verify. Before changing event, leaving payment or starting
|
|
353
|
+
another cart, handle in-flight and retained orders as described under
|
|
354
|
+
[safe completion](#payments-and-safe-completion).
|
|
355
|
+
|
|
356
|
+
`CheckoutProvider` props: `children` (required), `theme`, `pickImage`,
|
|
357
|
+
`options` (wallet/redirect `CheckoutOptions`), `paymentSheet`, `queryClient`,
|
|
358
|
+
and `value`. Omit `value` to let the provider create the native hook, provider
|
|
359
|
+
page and installment picker. If supplying `value`, create it with the native
|
|
360
|
+
`useCheckout` and provide any necessary ports yourself; `options` and
|
|
361
|
+
`paymentSheet` on a controlled provider do not configure that value. Keep the
|
|
362
|
+
choice of controlled versus self-provided stable for the mount. A custom query
|
|
363
|
+
client can be supplied with `queryClient`. `CheckoutThemeProvider` supplies only
|
|
364
|
+
theme, not checkout state or a query client.
|
|
365
|
+
|
|
366
|
+
## Checkout section reference
|
|
367
|
+
|
|
368
|
+
Unless noted, these sections read checkout state from `CheckoutProvider` and
|
|
369
|
+
are imported from `tf-checkout-react-native`. All props are optional except
|
|
370
|
+
those marked **required**. Prop interfaces are represented on the component in
|
|
371
|
+
the published declarations; not every `*Props` name is itself a top-level type
|
|
372
|
+
export. Derive one if needed with `React.ComponentProps<typeof Tickets>`.
|
|
373
|
+
|
|
374
|
+
### `Tickets`
|
|
375
|
+
|
|
376
|
+
Loads the catalog, quantities, promo/access-code and waiting-list controls,
|
|
377
|
+
currency and time slots when offered. Reserves/prices the cart before calling
|
|
378
|
+
`onContinue`.
|
|
379
|
+
|
|
380
|
+
| Prop | Description |
|
|
381
|
+
| --- | --- |
|
|
382
|
+
| `eventId: string` | **Required** event ID. |
|
|
383
|
+
| `onContinue?: () => void` | Cart was primed; reveal the next screen. |
|
|
384
|
+
| `onSelectSeats?: () => void` | Show a seat map for seated events. Without it the seat CTA is hidden. |
|
|
385
|
+
| `title?: string \| false` | Catalog heading; default `false`. |
|
|
386
|
+
| `buttonLabel?: string` | Reserve CTA; default `Get tickets`. |
|
|
387
|
+
| `timeSlotDates?: string[]` | Force time-slot selection with ISO `yyyy-mm-dd` dates; omit to use event data. |
|
|
388
|
+
| `initialPromoCode?: string`, `referrer?: string \| number` | Apply/attribute once on mount. |
|
|
389
|
+
| `readAttributionFromUrl?: boolean` | Accepted for web parity; does nothing on native. |
|
|
390
|
+
| `unstyled?: boolean` | Place catalog in the host's own container. |
|
|
391
|
+
| `renderTicket?: (row) => ReactNode` | Replace row visuals. `row` has `ticket`, `quantity`, `range: {min,max,step}`, `unavailable`, `seated`, `onQuantityChange`. |
|
|
392
|
+
| `renderContinue?: (cta) => ReactNode` | Replace CTA; `cta` has `onPress`, `disabled`, `loading`, `title`. |
|
|
393
|
+
|
|
394
|
+
The render callbacks retain SDK selection/reservation logic. Preserve the
|
|
395
|
+
provided disabled/loading state and accessible labels in custom UI.
|
|
396
|
+
|
|
397
|
+
### `SeatMap`, `AddOns`, `Bundles`, `Accommodation`
|
|
398
|
+
|
|
399
|
+
| Component | Props and behavior |
|
|
400
|
+
| --- | --- |
|
|
401
|
+
| `SeatMap` | **`eventId`**; `onContinue?` after a seat cart is primed. Internal or Seats.io maps render through WebView. The event and backend must support the chart, hold and cart contracts. |
|
|
402
|
+
| `AddOns` | **`eventId`**; `onContinue?`, `onSkip?`, `buttonLabel?`, `eyebrow?`, `heading?`. Saves quantities and add-on answers; skip is hidden when a required add-on exists. It may render nothing. |
|
|
403
|
+
| `Bundles` | **`eventId`**; `onPurchased?(orderHash)`, `onComplete?(order)`, `confirmationMode?: 'inline' \| 'redirect'`, `visible?`, `heading?`, `locale?`, `messages?`, `translate?`. Bundles use their own purchase/order flow, not the ticket cart. Set `visible={false}` while hidden so its card field is not mounted. |
|
|
404
|
+
| `Accommodation` | **`eventId`**. Composes stay selection, quote and reservation state for supported events. Availability and authoritative prices come from the server. |
|
|
405
|
+
|
|
406
|
+
### `BillingInfo`
|
|
407
|
+
|
|
408
|
+
Collects buyer, order-level and ticket-holder answers, then saves them into the
|
|
409
|
+
shared checkout. With `onContinue`, it has a continue button; without it, edits
|
|
410
|
+
are committed in the combined form. It uses event gates and may show additional
|
|
411
|
+
required fields. All props are optional:
|
|
412
|
+
|
|
413
|
+
| Prop | Description |
|
|
414
|
+
| --- | --- |
|
|
415
|
+
| `eventId?: string` | Event context for country defaults. |
|
|
416
|
+
| `onContinue?: () => void` | Called after valid billing is saved. |
|
|
417
|
+
| `heading?`, `buttonLabel?` | Copy overrides. |
|
|
418
|
+
| `onLogin?: (email?: string) => void` | Offer sign-in, including an email already associated with an account. |
|
|
419
|
+
| `skipWhenProfileComplete?: boolean` | May hide the card for a complete signed-in profile; default `false`. Do not combine with `privacyPolicy` without a verified workaround. |
|
|
420
|
+
| `requireEmailConfirmation?: boolean` | Require guest email confirmation; standalone default `false`. |
|
|
421
|
+
| `collectTicketHolders?: boolean` | Require named holders beyond event gates; standalone default `false`. |
|
|
422
|
+
| `collectHolderContact?: boolean` | Collect holder email and phone when holders are shown; standalone default `false`. |
|
|
423
|
+
| `ttfOptInLabel?`, `brandOptInLabel?` | Label overrides; privacy and Ticket Fairy opt-in share a value when `privacyPolicy` is supplied. |
|
|
424
|
+
| `privacyPolicy?: { url: string; brandName: string; linkLabel?: string }` | Host policy and required acknowledgment. |
|
|
425
|
+
| `showPrivacyError?: boolean` | Show inline privacy error after a failed attempt. |
|
|
426
|
+
| `step?: ReactNode` | Accepted for API parity but not rendered by native `SectionHeader`. |
|
|
427
|
+
|
|
428
|
+
### `OrderSummary`, `WaiverSigning`, `Payment`, `IDVerification`, `Confirmation`
|
|
429
|
+
|
|
430
|
+
| Component | Props and behavior |
|
|
431
|
+
| --- | --- |
|
|
432
|
+
| `OrderSummary` | `eventId?`, `locale?`, `messages?`, `translate?`. Displays server-priced cart totals and optional event name. |
|
|
433
|
+
| `WaiverSigning` | **`eventId`**; `locale?`, `messages?`, `translate?`. Renders only when the event has a waiver; supports the server-offered signing methods. |
|
|
434
|
+
| `Payment` | `onComplete?(order)`, `eventId?` (currently unused by native payment), `requireEmailConfirmation?`, `collectTicketHolders?`, `requireHolderContact?`, `additionalConditions?`, `requirePrivacyPolicy?`, `onPrivacyRequired?`, `showTestCardHint?`, `locale?`, `messages?`, `translate?`. Standalone boolean defaults are false. It validates, renders eligible methods and submits. |
|
|
435
|
+
| `IDVerification` | No props. When required, it shows a Verify button; the buyer must press it to open and poll the provider. A stall proves neither failure nor settlement. |
|
|
436
|
+
| `Confirmation` | `shareUrl?`, `shareMessage?`, `viewOrderUrl?`, `message?`, `children?`. Reads the current order. Its stock title and share copy do not independently verify payment settlement or referral eligibility; see below. |
|
|
437
|
+
|
|
438
|
+
`Payment` gates its rendered event conditions, host `additionalConditions` and
|
|
439
|
+
requested privacy acknowledgment in its UI. Each `EventCondition` is
|
|
440
|
+
`{ id: string, name: string, content: string, isRequired: boolean }`. These
|
|
441
|
+
acknowledgments are client-side and are not sent as separate server fields.
|
|
442
|
+
Conditions use native `RichText`, which filters tags/styles and opens only
|
|
443
|
+
HTTP(S) links; its rendering is not browser HTML parity. A headless
|
|
444
|
+
`placeOrder` checks loaded event conditions in the engine, but does not receive
|
|
445
|
+
host `additionalConditions` or enforce the UI privacy gate. Headless hosts must
|
|
446
|
+
implement and test their own required host acknowledgments before placement.
|
|
447
|
+
|
|
448
|
+
`Rewards` renders order-scoped rewards, a referral tree, ladder, packs and a
|
|
449
|
+
native share sheet. Pass `orderHash` or an already loaded `data` payload; it also
|
|
450
|
+
accepts `locale`, `messages`, `translate`. Embed it as a `Confirmation` child,
|
|
451
|
+
or use `confirmationChildren` on `EmbeddedCheckout`. `RewardsTree` accepts
|
|
452
|
+
**`tree`** and optional `size` (default 260); `RewardsLadder` accepts **`rows`**,
|
|
453
|
+
**`translate`** and optional async `onSubmitDetails(awardId, details)`.
|
|
454
|
+
Account-level `AccountRewardsSection` has separate read-only account semantics.
|
|
455
|
+
`Rewards` uses the server's `share_link` and hides itself when
|
|
456
|
+
`referrals_enabled` is explicitly false. Verify the returned eligibility and
|
|
457
|
+
buyer-specific link against your deployed contract. Standalone `Confirmation`
|
|
458
|
+
displays reward-promising share copy for any non-pending order when `shareUrl`
|
|
459
|
+
is supplied, without checking eligibility or generating a buyer-specific link.
|
|
460
|
+
Omit it unless your host has verified both; `EmbeddedCheckout` does not expose a
|
|
461
|
+
`shareUrl` prop. The example below omits that block and assumes the host has
|
|
462
|
+
already verified the status required to show confirmed receipt copy, and the
|
|
463
|
+
original result was not pending. `Confirmation` reads the original
|
|
464
|
+
`checkout.order.pending`; it does not refresh that flag after later settlement.
|
|
465
|
+
Use a host status UI based on a fresh authoritative read for an asynchronously
|
|
466
|
+
settled order.
|
|
262
467
|
|
|
263
468
|
```tsx
|
|
264
|
-
|
|
265
|
-
eventId="14304"
|
|
266
|
-
theme={{ colorScheme: 'dark', accent: '#ff2d55', radiusCard: 16 }}
|
|
267
|
-
/>
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
The same token names as web: color (`accent`, `surface`, `text`, `muted`,
|
|
271
|
-
`border`, `error`, `success`), shape, and typography. Read them anywhere with
|
|
272
|
-
`useTheme()`.
|
|
469
|
+
import { Confirmation, Rewards, useCheckoutContext } from 'tf-checkout-react-native'
|
|
273
470
|
|
|
274
|
-
|
|
471
|
+
function Receipt({ verified }: { verified: boolean }) {
|
|
472
|
+
const checkout = useCheckoutContext()
|
|
473
|
+
if (!verified || checkout.order?.pending) return null
|
|
474
|
+
return (
|
|
475
|
+
<Confirmation>
|
|
476
|
+
{checkout.order?.orderHash ? <Rewards orderHash={checkout.order.orderHash} /> : null}
|
|
477
|
+
</Confirmation>
|
|
478
|
+
)
|
|
479
|
+
}
|
|
480
|
+
```
|
|
275
481
|
|
|
276
|
-
|
|
482
|
+
## Tickets, seats, codes and attribution
|
|
483
|
+
|
|
484
|
+
Use the server's current event catalog and displayed prices. Time-slot ticket
|
|
485
|
+
IDs encode their selected session. `Tickets` supports ticket quantities,
|
|
486
|
+
groups, promo/access codes, waiting-list forms and seat-map routing when the
|
|
487
|
+
backend offers them. Waiting-list availability is a separate, authoritative
|
|
488
|
+
catalog contract; a sold-out row alone does not imply that a list is open.
|
|
489
|
+
|
|
490
|
+
Unlike 1.x, native `Tickets` has no password prompt or password prop. For a
|
|
491
|
+
password-protected event, a host can collect the password and call
|
|
492
|
+
`ttf.events.authenticate(eventId, password)`, handle wrong-password and session
|
|
493
|
+
errors, then refetch the event/catalog for the same buyer and event context.
|
|
494
|
+
Keep the password out of logs and verify the deployed password/session contract
|
|
495
|
+
and the refreshed result; the API call alone is not proof of a working native
|
|
496
|
+
unlock flow.
|
|
497
|
+
|
|
498
|
+
For a headless flow, `TicketSelection` has `id`, `name`, `optionName`, `price`,
|
|
499
|
+
`quantity` and `currency`. Build it from current catalog data and call
|
|
500
|
+
`setTickets(eventId, selections)`. `setSeats` takes server-reserved `SeatLine`s;
|
|
501
|
+
`setSeatsIo` takes a `SeatsIoSelection`. Do not fabricate seat holds. The
|
|
502
|
+
native `SeatMap` drives the chart and shared selection controller; provider
|
|
503
|
+
holds, server cart acceptance and actual device behavior need separate testing.
|
|
504
|
+
|
|
505
|
+
Quantity retention across rotating encrypted IDs requires deployed canonical
|
|
506
|
+
`selectionKey`s and dated numeric context (`display_price`, `price_with_fees`,
|
|
507
|
+
`fee_included`, `price_currency`, `price_symbol`). Without those optional keys,
|
|
508
|
+
opaque-ID compatibility remains; rotation retention is not guaranteed.
|
|
509
|
+
|
|
510
|
+
Supply `initialPromoCode` and `referrer` explicitly to `EmbeddedCheckout` or
|
|
511
|
+
`Tickets`, or use `applyPromoCode` / `attributeReferrer` from the hook. Native
|
|
512
|
+
`readAttributionFromUrl` is a no-op; the host must parse and classify incoming
|
|
513
|
+
links. An ambiguous numeric `?r=` can be a promo or referral. Do not send it as
|
|
514
|
+
a referral without authoritative classification. This beta does not reproduce
|
|
515
|
+
every hosted conditional-field visibility rule; conditional requiredness is
|
|
516
|
+
left to the backend. Test your event's fields and referral/promo links against
|
|
517
|
+
the deployed service before relying on parity.
|
|
518
|
+
|
|
519
|
+
Post-purchase upgrade waiting is server-owned and separate from the event
|
|
520
|
+
waiting list. `TicketChange` exposes it only for valid targets actually offered
|
|
521
|
+
by the server; a sold-out ticket is not sufficient. Validate the organizer's
|
|
522
|
+
configuration and deployed join/leave behavior.
|
|
523
|
+
|
|
524
|
+
## Billing, custom fields and consent
|
|
525
|
+
|
|
526
|
+
`CheckoutBilling` requires `firstName`, `lastName` and `email`. Other fields
|
|
527
|
+
include `confirmEmail`, phone, address, `countryId`/`stateId` (backend numeric
|
|
528
|
+
IDs encoded as strings), `countryCode`/`stateCode` (payment billing codes), DOB,
|
|
529
|
+
`nationality`, `gender`, `instagramHandle` and order `dataCapture`. A
|
|
530
|
+
`CheckoutHolder` requires `firstName`, `lastName`, `email` and may include
|
|
531
|
+
`phone`, numeric `ticketTypeId` as a string and ticket `ticketDataCapture`.
|
|
532
|
+
`CheckoutConsent` is `{ ttfOptIn: boolean, brandOptIn: boolean }`.
|
|
533
|
+
|
|
534
|
+
The event supplies `buyerFields`, `fieldGates`, `customFields`, `conditions`,
|
|
535
|
+
required add-ons and waiver requirements. Use the server's ticket breakdown to
|
|
536
|
+
associate holder answers with ticket types. Add-on capture is separately keyed by
|
|
537
|
+
add-on ID. Do not hard-code a minimal buyer form as valid for every event. The
|
|
538
|
+
standalone `BillingInfo` default is less strict than `EmbeddedCheckout`'s holder
|
|
539
|
+
and email-confirmation defaults; match it with the paired `Payment` props. The
|
|
540
|
+
SDK's confirmation email value is a UI check; the order API uses the primary
|
|
541
|
+
email. Recheck terms, privacy and marketing consent with your own policy.
|
|
542
|
+
|
|
543
|
+
**Beta.23 consent limitation:** when `privacyPolicy` is supplied, the stock
|
|
544
|
+
privacy checkbox reads and writes `consent.ttfOptIn`, which the order sends as
|
|
545
|
+
`ttf_opt_in`. It is not independent of the Ticket Fairy marketing flag. The
|
|
546
|
+
brand opt-in uses a separate value. If both opt-in labels are explicitly empty,
|
|
547
|
+
`BillingInfo` can skip committing consent even while it shows the privacy
|
|
548
|
+
checkbox, leaving the payment gate blocked. Also, enabling
|
|
549
|
+
`skipBillingWhenProfileComplete` (or standalone `skipWhenProfileComplete`) can
|
|
550
|
+
hide that checkbox while `Payment` still requires it. Avoid those combinations
|
|
551
|
+
without a verified host workaround; where separate privacy and marketing consent
|
|
552
|
+
is required, use a custom flow and a tested backend consent contract.
|
|
553
|
+
|
|
554
|
+
During cart priming, failed custom-field, checkout-config, add-on or event
|
|
555
|
+
condition reads fall back to empty/default data. The stock UI cannot show or
|
|
556
|
+
gate requirements it did not load. Only a failed waiver read (except a 404) is
|
|
557
|
+
recorded in `unavailableDetails` and retried/gated. Do not treat an empty list as
|
|
558
|
+
proof that no requirement exists; verify deployment/failure behavior and add
|
|
559
|
+
tested host safeguards for mandatory fields and acknowledgments. Server
|
|
560
|
+
validation of required answers is not proof that a condition was acknowledged.
|
|
561
|
+
|
|
562
|
+
Waiver signatures are separate from holder fields. The waiver component reads
|
|
563
|
+
the event's permitted methods and stores the uploaded signature reference. If
|
|
564
|
+
waiver details could not be loaded, placement waits/retries that read rather
|
|
565
|
+
than assuming no waiver. Image questions require a host `pickImage` adapter.
|
|
566
|
+
|
|
567
|
+
## Payments and safe completion
|
|
568
|
+
|
|
569
|
+
`Payment` uses server event/method configuration and device support. Free
|
|
570
|
+
orders, card, native wallets, payment plans, OXXO, provider pages and other
|
|
571
|
+
methods are different paths; an SDK export does not enable a method on your
|
|
572
|
+
account. The server's review and breakdown are authoritative for amount,
|
|
573
|
+
currency and receiving account. Never collect PAN or CVC in your JS form or
|
|
574
|
+
logs. Use the provider-owned card field or eligible PaymentSheet.
|
|
575
|
+
|
|
576
|
+
### Result, retry and reset
|
|
577
|
+
|
|
578
|
+
`PlaceOrderResult` includes `orderHash`, `free`, `needIdVerification`, optional
|
|
579
|
+
`currency`, `paymentIntentId`, `alreadyPaid`, `pending` and
|
|
580
|
+
`pendingVoucherUrl`. `pending: true` represents an asynchronous result such as
|
|
581
|
+
an OXXO voucher or PayPal settlement. Show pending instructions and verify
|
|
582
|
+
status with the backend/provider; do not label it paid or fulfill it as paid.
|
|
583
|
+
Conversely, `pending !== true` and `status === 'succeeded'` are not settlement
|
|
584
|
+
proof: Stripe `Processing` and `RequiresCapture` can produce a result without
|
|
585
|
+
`pending`. The stock non-pending receipt can show confirmed copy before final
|
|
586
|
+
capture. Reconcile the authoritative order/provider status and use custom or
|
|
587
|
+
redirect routing when your fulfillment policy requires verified payment.
|
|
588
|
+
When `needIdVerification` is true, the host must arrange a separate verification
|
|
589
|
+
journey. Inline `IDVerification` requires the buyer to press Verify; redirect
|
|
590
|
+
mode does not render it. A composed host can render `IDVerification` under the
|
|
591
|
+
active `CheckoutProvider`, or call `runIdVerification(orderHash)` from a custom
|
|
592
|
+
flow. Handle navigation/returns; a failed or delayed verification proves neither
|
|
593
|
+
payment failure nor settlement.
|
|
594
|
+
|
|
595
|
+
**Zero-total orders:** this SDK ignores the free-registration response body and
|
|
596
|
+
reports `needIdVerification: false`. Verification or approval may still be
|
|
597
|
+
required, and `free: true` is not proof tickets were issued. The default inline
|
|
598
|
+
`EmbeddedCheckout` can nevertheless say registration is complete and the buyer
|
|
599
|
+
is registered while approval or verification is still outstanding. Use
|
|
600
|
+
`confirmationMode="redirect"` with host-owned authoritative order and
|
|
601
|
+
verification status, or a custom composed confirmation gated on that status.
|
|
602
|
+
If the status is unavailable, contact support. Do not retry payment for a free
|
|
603
|
+
order.
|
|
604
|
+
|
|
605
|
+
`status` is `idle`, `priming`, `ready`, `repricing`, `placing`, `succeeded`,
|
|
606
|
+
`expired` or `error`. Disable repeated submissions while `isBusy` or `placing`,
|
|
607
|
+
show the reservation countdown and handle expiry. The engine coordinates
|
|
608
|
+
concurrent placement and can retain the original order when payment may have
|
|
609
|
+
moved. A timeout, dismissed provider page, navigation or app interruption is
|
|
610
|
+
not proof that no payment occurred. Reconcile the original order with your
|
|
611
|
+
backend and payment provider before retrying a purchase or fulfilling it. This
|
|
612
|
+
is not an exactly-once charging guarantee across every host/provider.
|
|
613
|
+
|
|
614
|
+
`reset()` starts a reset and returns no completion signal. Prefer
|
|
615
|
+
`await resetWhenSettled()` when navigating to a new checkout: it waits for an
|
|
616
|
+
in-flight payment and returns `true` only if reset completed. `false` can mean
|
|
617
|
+
the original order is retained or the checkout was retired. Show
|
|
618
|
+
`PAID_ORDER_PENDING_MESSAGE` where appropriate and reconcile; do not clear the
|
|
619
|
+
store, start a new order or promise a refund merely to bypass this guard. A
|
|
620
|
+
custom `providerPage` must settle on close/unmount with `{ event: 'dismiss' }`;
|
|
621
|
+
other custom async ports must settle too, or reset can wait indefinitely.
|
|
277
622
|
|
|
278
623
|
```tsx
|
|
279
|
-
import {
|
|
280
|
-
import {
|
|
281
|
-
CheckoutProvider,
|
|
282
|
-
Tickets,
|
|
283
|
-
AddOns,
|
|
284
|
-
BillingInfo,
|
|
285
|
-
Payment,
|
|
286
|
-
OrderSummary,
|
|
287
|
-
} from 'tf-checkout-react-native'
|
|
624
|
+
import { PAID_ORDER_PENDING_MESSAGE, useCheckoutContext } from 'tf-checkout-react-native'
|
|
288
625
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
<Payment />
|
|
297
|
-
</ScrollView>
|
|
298
|
-
</CheckoutProvider>
|
|
299
|
-
)
|
|
626
|
+
function useStartAgain(setNotice: (message: string) => void) {
|
|
627
|
+
const checkout = useCheckoutContext()
|
|
628
|
+
return async () => {
|
|
629
|
+
if (checkout.status === 'placing') setNotice('Finishing your payment…')
|
|
630
|
+
if (!(await checkout.resetWhenSettled())) setNotice(PAID_ORDER_PENDING_MESSAGE)
|
|
631
|
+
}
|
|
632
|
+
}
|
|
300
633
|
```
|
|
301
634
|
|
|
302
|
-
|
|
303
|
-
`
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
635
|
+
`TtfError` exposes `message`, HTTP `status` (`0` for transport failure),
|
|
636
|
+
optional backend `code`, `data` and `retryAfter` seconds. `errorMessage(error)`
|
|
637
|
+
and `isCartExpiredError(error)` help with display and expiry handling. Do not
|
|
638
|
+
log tokens, raw payment credentials or sensitive buyer data. An error after
|
|
639
|
+
payment initiation can be ambiguous even when its UI says to retry.
|
|
307
640
|
|
|
308
|
-
|
|
309
|
-
`
|
|
641
|
+
A custom confirmer can reject with `paymentProviderError(message, outcome)`:
|
|
642
|
+
`declined`, `cancelled`, `notStarted` or `unknown`. Only classify the first
|
|
643
|
+
three when the provider establishes that no charge occurred. Other rejections
|
|
644
|
+
are treated as unknown. Preserve the original order and reconcile it.
|
|
310
645
|
|
|
311
|
-
|
|
646
|
+
### Stripe card and PaymentSheet
|
|
312
647
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
331
|
-
## Headless
|
|
332
|
-
|
|
333
|
-
`useCheckout()` gives you the whole flow with no UI.
|
|
648
|
+
By default, native `Payment` uses Stripe's native `CardInput` for an eligible
|
|
649
|
+
card path. A host composing its own UI can opt into PaymentSheet on the
|
|
650
|
+
provider. Use a static merchant name and scheme for the life of the mounted
|
|
651
|
+
checkout. Configure the app to receive the same deep link and pass cold- and
|
|
652
|
+
warm-start Stripe callback URLs to Stripe according to its React Native setup.
|
|
653
|
+
The embedded component does not expose the PaymentSheet opt-in.
|
|
334
654
|
|
|
335
655
|
```tsx
|
|
336
|
-
import
|
|
337
|
-
import {
|
|
656
|
+
import React from 'react'
|
|
657
|
+
import { CheckoutProvider, Payment } from 'tf-checkout-react-native'
|
|
338
658
|
|
|
339
|
-
function
|
|
340
|
-
|
|
341
|
-
|
|
659
|
+
export function PaymentStep() {
|
|
660
|
+
return (
|
|
661
|
+
<CheckoutProvider
|
|
662
|
+
options={{ urlScheme: 'myapp' }}
|
|
663
|
+
paymentSheet={{
|
|
664
|
+
merchantDisplayName: 'My Events',
|
|
665
|
+
returnURL: 'myapp://stripe-redirect',
|
|
666
|
+
}}
|
|
667
|
+
>
|
|
668
|
+
{/* Render the ticket, billing and consent sections in this provider too. */}
|
|
669
|
+
<Payment />
|
|
670
|
+
</CheckoutProvider>
|
|
671
|
+
)
|
|
342
672
|
}
|
|
343
673
|
```
|
|
344
674
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
675
|
+
The return URL must equal `${urlScheme}://stripe-redirect`; register the actual
|
|
676
|
+
scheme in iOS and Android and route callbacks before normal navigation. A
|
|
677
|
+
controlled provider must create its `value` with the same `paymentSheet` and
|
|
678
|
+
`options` on `useCheckout` itself. Changing the config during a mount is not
|
|
679
|
+
supported. In headless UI, use `checkout.paymentSheet?.placeOrder()` when
|
|
680
|
+
available; the normal `Payment` section chooses it for the eligible card row.
|
|
681
|
+
|
|
682
|
+
In beta.24 the sheet is limited to eligible non-MXN Stripe PaymentIntents whose
|
|
683
|
+
original methods are card alone, or card plus Cash App Pay in USD. It does not
|
|
684
|
+
admit delayed methods, create a new intent or add a Stripe Customer/saved-card
|
|
685
|
+
contract. The SDK checks the original intent's identity, amount and currency
|
|
686
|
+
against the order review. Where the method list or sheet setup cannot be
|
|
687
|
+
admitted, the flow may ask for the native card form; a financial identity
|
|
688
|
+
mismatch instead requires order review. MXN installment card payments and new
|
|
689
|
+
card payment plans retain CardField. Account, event, device and provider
|
|
690
|
+
eligibility still apply.
|
|
691
|
+
|
|
692
|
+
Stripe may show card-mode Link when its account and session qualify; the SDK
|
|
693
|
+
requests automatic Link display but cannot promise its availability. Link is
|
|
694
|
+
not the same thing as OS/password-manager card autofill, which is also
|
|
695
|
+
provider/device dependent. Test Link and callbacks on real devices and the
|
|
696
|
+
actual receiving Stripe account.
|
|
697
|
+
|
|
698
|
+
### Apple Pay, Google Pay and other providers
|
|
699
|
+
|
|
700
|
+
Native wallet rows are separate from PaymentSheet. They appear only when the
|
|
701
|
+
server enables the wallet, the corresponding OS/device supports it and Stripe's
|
|
702
|
+
platform-pay check succeeds. Configure merchant entitlements and identifiers,
|
|
703
|
+
the Stripe account, app scheme and native build for your app. The order review
|
|
704
|
+
must supply valid wallet amount, currency and merchant country.
|
|
705
|
+
A composed provider can pass:
|
|
366
706
|
|
|
367
707
|
```tsx
|
|
368
|
-
|
|
369
|
-
|
|
708
|
+
<CheckoutProvider options={{
|
|
709
|
+
merchantIdentifier: 'merchant.com.example.app',
|
|
710
|
+
merchantName: 'My Events',
|
|
711
|
+
googlePayTestEnv: false,
|
|
712
|
+
urlScheme: 'myapp',
|
|
713
|
+
}}>
|
|
714
|
+
{/* Render the ticket and billing sections in this provider too. */}
|
|
715
|
+
<Payment />
|
|
716
|
+
</CheckoutProvider>
|
|
370
717
|
```
|
|
371
718
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
719
|
+
`CheckoutOptions` also accepts `stripePublishableKey` as an override and
|
|
720
|
+
`redirectReturnUrl` and `merchantCountryCode`. Normally use the keys from the
|
|
721
|
+
order review; do not hard code a connected account or infer it from the event.
|
|
722
|
+
In this beta, native wallet confirmation takes merchant country from the
|
|
723
|
+
authoritative order review rather than the `merchantCountryCode` option. The
|
|
724
|
+
native confirmer uses `Order` as its merchant-name fallback; set your merchant
|
|
725
|
+
name explicitly. The example matches the earlier `PROD` setup. For Google Pay,
|
|
726
|
+
the `Payment` support probe uses test mode when `env` is `STAG` or `DEV`, while
|
|
727
|
+
confirmation uses `options.googlePayTestEnv`. Set both consistently for the
|
|
728
|
+
intended environment; a `baseUrl` override does not set `env`. Configure the
|
|
729
|
+
native/plugin wallet setting and verify both the support probe and confirmation
|
|
730
|
+
on device, especially with custom hosts. Expo settings, Apple entitlement,
|
|
731
|
+
deep links and provider account approval are host responsibilities. Test
|
|
732
|
+
eligibility, cancellation, return and settlement on each platform.
|
|
733
|
+
|
|
734
|
+
`CheckoutProvider` without a controlled value supplies a WebView provider-page
|
|
735
|
+
port and an installment picker. Provider pages may be used for PayPal,
|
|
736
|
+
Razorpay, Xendit, Adyen or redirect methods when the backend enables them.
|
|
737
|
+
Host routing and provider behavior still determine whether a particular flow
|
|
738
|
+
works; check network, app-link, browser and return behavior on device. A
|
|
739
|
+
headless `useCheckout` host must supply `ports.providerPage` and any other
|
|
740
|
+
needed ports; `rnPorts` alone does not provide the provider-page UI. Its
|
|
741
|
+
`providerPage` receives `{ uri?, html?, baseUrl?, returnUrlPrefix }` and resolves
|
|
742
|
+
`{ event: 'success' | 'dismiss' | 'failed', returnUrl?, data?, description?,
|
|
743
|
+
notOpened?, paymentOutcome? }`. Report `notOpened` or `paymentOutcome:
|
|
744
|
+
'declined'` only when actually established. The engine still reconciles the
|
|
745
|
+
provider outcome.
|
|
746
|
+
|
|
747
|
+
### `CardInput` for custom payment UI
|
|
748
|
+
|
|
749
|
+
`CardInput` props are `owner?: string` (default `checkout`),
|
|
750
|
+
`visible?: boolean`, and `onComplete?: (complete: boolean) => void`. The
|
|
751
|
+
native hook's default card claim uses the default owner. If multiple flows can
|
|
752
|
+
mount card fields, give each its own owner and use the matching
|
|
753
|
+
`claimCardField(owner)` and `cardClaimStep` as `placeOrder`'s `claimCard` option;
|
|
754
|
+
otherwise one flow must not use another's field. Keep the field mounted and
|
|
755
|
+
visible through its confirmation. `Payment` handles its own field ownership.
|
|
756
|
+
Do not treat `onComplete` as confirmation or payment success.
|
|
757
|
+
|
|
758
|
+
## Headless checkout
|
|
759
|
+
|
|
760
|
+
`useCheckout()` is the native-wired React binding, not the old 1.x React Query
|
|
761
|
+
mutation. It returns a flat `CheckoutSnapshot`, `snapshot`, `store`, derived
|
|
762
|
+
state and actions. It injects the native Stripe confirmer and ports. Use it
|
|
763
|
+
inside a React component after configuration, and supply a React Query provider
|
|
764
|
+
where the host also uses query hooks. The app-wide checkout store is shared
|
|
765
|
+
across native hook calls and provider sections: they are not independent carts.
|
|
391
766
|
|
|
392
767
|
```tsx
|
|
393
|
-
import {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
</Confirmation>
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
When `<EmbeddedCheckout/>` renders the receipt itself, pass the block through
|
|
400
|
-
`confirmationChildren`. In this example the host supplies `eventId` and
|
|
401
|
-
`MyRewards`:
|
|
768
|
+
import React, { useState } from 'react'
|
|
769
|
+
import { Button, Text, View } from 'react-native'
|
|
770
|
+
import { useCheckout, type TicketSelection } from 'tf-checkout-react-native'
|
|
402
771
|
|
|
403
|
-
|
|
404
|
-
|
|
772
|
+
// selections must come from this event's current server catalog.
|
|
773
|
+
export function Reserve({ eventId, selections }: {
|
|
774
|
+
eventId: string
|
|
775
|
+
selections: TicketSelection[]
|
|
776
|
+
}) {
|
|
777
|
+
const checkout = useCheckout()
|
|
778
|
+
const [message, setMessage] = useState('')
|
|
779
|
+
const reserve = async () => {
|
|
780
|
+
try {
|
|
781
|
+
await checkout.setTickets(eventId, selections)
|
|
782
|
+
setMessage('Tickets reserved. Collect the required details next.')
|
|
783
|
+
} catch {
|
|
784
|
+
setMessage('Could not reserve tickets. Check availability and try again.')
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return <View><Button title="Reserve" disabled={checkout.isBusy} onPress={() => { void reserve() }} />
|
|
788
|
+
<Text>{message}</Text></View>
|
|
789
|
+
}
|
|
405
790
|
```
|
|
406
791
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
`
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
792
|
+
A real headless host must collect the event's gated buyer/holder data, custom
|
|
793
|
+
answers, conditions, add-ons and waivers, render a provider-owned payment input
|
|
794
|
+
or wallet/provider UI, validate against the event, and handle every result and
|
|
795
|
+
error before placing an order. Enforce host privacy and `additionalConditions`
|
|
796
|
+
yourself; the headless engine does not receive these `Payment` props. The short
|
|
797
|
+
example only primes the cart.
|
|
798
|
+
|
|
799
|
+
### Snapshot and actions
|
|
800
|
+
|
|
801
|
+
| Member | Purpose |
|
|
802
|
+
| --- | --- |
|
|
803
|
+
| `status`, `error`, `order`, `orderFixed` | Lifecycle, error text, result and whether order inputs are frozen. |
|
|
804
|
+
| `eventId`, `selections`, `ticketTypeIds`, `breakdown`, `priceBreakdown` | Current cart and server-priced display data. |
|
|
805
|
+
| `secondsLeft`, `expiresAt`, `isBusy` | Countdown in whole seconds, absolute expiry in epoch ms and operation state. `secondsLeft` can be undefined. |
|
|
806
|
+
| `customFields`, `buyerFields`, `fieldGates`, `holderNamesRequired` | Event-specific capture requirements. |
|
|
807
|
+
| `requiredAddOns`, `missingRequiredAddonNames`, `addOns`, `addOnDataCapture` | Add-on requirements and selections. |
|
|
808
|
+
| `billing`, `holders`, `consent`, `conditions`, `acceptedConditions` | Buyer data and acknowledgments. |
|
|
809
|
+
| `waiver`, `waiverSignatures`, `waiverSigningIndexes`, `unavailableDetails` | Waiver state; `unavailableDetails` tracks waiver failures, not other failed detail reads. |
|
|
810
|
+
| `gateway`, `stripe`, `paymentStripe`, `paymentPlan`, `redirectPaymentMethods`, `walletAvailability`, `currency` | Server/engine payment and currency context. Their presence is not a guarantee of provider eligibility. |
|
|
811
|
+
| `extraItems`, `extraDraft`, `extraQuotes`, `extraRejections`, `extraExpiresAt`, `reviewedExtraOrderTotal` | Accommodation/extra reservation and price state. |
|
|
812
|
+
| `setTickets(eventId, selections)` | Prime ticket cart and price it. |
|
|
813
|
+
| `setSeats(eventId, lines)`, `setSeatsIo(eventId, selection)`, `resumeCart(eventId, options?)` | Prime a seat cart or attach to a supported existing cart. |
|
|
814
|
+
| `setAddons(selection)` | Set add-on quantities and reprice; await it. |
|
|
815
|
+
| `setExtraItems(items)`, `setExtraDraft(items \| undefined)` | Reserve or edit accommodation/extras. |
|
|
816
|
+
| `setBilling(billing)`, `setHolders(holders, options?)`, `setConsent(consent)` | Commit buyer data; optional holder `backfilled` describes copies of buyer details. |
|
|
817
|
+
| `setAcceptedConditions(map)`, `acceptCondition(id, accepted)` | Commit required/optional acknowledgment state. |
|
|
818
|
+
| `setAddonDataCapture(capture)` | Set answers keyed by add-on ID. |
|
|
819
|
+
| `holdWaiverSigning(index)`, `setWaiverSignature(index, signature, hold?)` | Bind a signature to its original cart/holder; release the returned hold after saving. |
|
|
820
|
+
| `checkBuyerEmail(email)`, `reloadCheckoutDetails()` | Check for an existing account; retry an unavailable waiver read. |
|
|
821
|
+
| `placeOrder(method?, options?)` | Place/pay via an eligible method; returns `Promise<PlaceOrderResult>`. Options include `autoRunIdVerification`, `idVerification`, `claimCard`. |
|
|
822
|
+
| `runIdVerification(orderHash, options?)` | Open/poll verification as a separate post-order step. |
|
|
823
|
+
| `syncCurrency(eventId, code?, options?)` | Seed/change currency; refetch catalog and re-prime as needed. |
|
|
824
|
+
| `applyPromoCode(eventId, promo)`, `attributeReferrer(eventId, referrer)`, `joinWaitingList(eventId, entry)` | Event engagement helpers. |
|
|
825
|
+
| `reset()`, `resetWhenSettled()` | Reset subject to payment safety; use the awaited form to know its outcome. |
|
|
826
|
+
| `paymentSheet?.placeOrder()` | Present the explicitly configured eligible native sheet. May be absent. |
|
|
827
|
+
|
|
828
|
+
`placeOrder` supports simple `'card'`, `'applePay'`, `'googlePay'` or typed
|
|
829
|
+
`PaymentRequest` objects: `auto`, `card`, `applePay`, `googlePay`, `redirect`
|
|
830
|
+
(with `methodType`), `oxxo`, `xenditEWallet` (with `walletKey` and optional extra),
|
|
831
|
+
`paypal`, `razorpay`, `adyen`, `xenditCard`, or `paymentPlan`. A union member
|
|
832
|
+
is not an availability promise. Render choices from server/provider eligibility
|
|
833
|
+
and supply the required port. `UseCheckoutOptions` accepts `{ ports?, options?,
|
|
834
|
+
paymentSheet? }`; ports merge over `rnPorts`. `createRnStripeConfirmer`,
|
|
835
|
+
`createRnPorts`, `rnConfirmer` and `rnPorts` are exported for advanced hosts.
|
|
836
|
+
For non-React integration, `createEngine({ ports, options?, store? })` provides
|
|
837
|
+
`prime`, `placeOrder`, `getSnapshot` and `subscribe`; its required `confirmer`
|
|
838
|
+
and optional ports are described by the published `CheckoutPorts` type.
|
|
839
|
+
|
|
840
|
+
## Hooks and API
|
|
841
|
+
|
|
842
|
+
Import the shared React Query hooks through the **`hooks` namespace**, not as
|
|
843
|
+
individual top-level imports. `CheckoutProvider` includes a query provider for
|
|
844
|
+
its descendants; outside it, wrap your components with your own
|
|
845
|
+
`QueryClientProvider`. Query hooks expose React Query states such as `data`,
|
|
846
|
+
`isLoading`, `error` and `refetch`; mutation hooks expose `mutate` /
|
|
847
|
+
`mutateAsync`, pending state and error. Query and mutation options follow the
|
|
848
|
+
published `QueryOpts` and `MutationOpts` types. Event/order hooks can be gated
|
|
849
|
+
until their ID is available.
|
|
432
850
|
|
|
433
|
-
|
|
851
|
+
```tsx
|
|
852
|
+
import React from 'react'
|
|
853
|
+
import { Text } from 'react-native'
|
|
854
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
855
|
+
import { hooks } from 'tf-checkout-react-native'
|
|
434
856
|
|
|
435
|
-
|
|
436
|
-
|
|
857
|
+
const queryClient = new QueryClient()
|
|
858
|
+
function CatalogName({ eventId }: { eventId: string }) {
|
|
859
|
+
const tickets = hooks.useTickets(eventId)
|
|
860
|
+
if (tickets.isLoading) return <Text>Loading tickets…</Text>
|
|
861
|
+
if (tickets.error) return <Text>Could not load tickets.</Text>
|
|
862
|
+
return <Text>{tickets.data?.length ?? 0} ticket types</Text>
|
|
863
|
+
}
|
|
864
|
+
export function Catalog({ eventId }: { eventId: string }) {
|
|
865
|
+
return <QueryClientProvider client={queryClient}><CatalogName eventId={eventId} /></QueryClientProvider>
|
|
866
|
+
}
|
|
437
867
|
```
|
|
438
868
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
869
|
+
The API reference lists the available hooks by domain and their parameters.
|
|
870
|
+
|
|
871
|
+
The top-level native `useCheckout()` adds native ports. `hooks.useCheckout` is
|
|
872
|
+
the shared lower-level binding and expects an `EngineConfig` with ports; do not
|
|
873
|
+
substitute it into the native quick start. The root also exports `ttf` (a
|
|
874
|
+
configured domain-grouped API facade), `api` (raw API namespace), types,
|
|
875
|
+
`createEngine`, store/logic utilities and translation functions. For example,
|
|
876
|
+
`ttf.events.tickets(eventId)`, `ttf.geo.countries()`, `ttf.orders.get(orderId)`
|
|
877
|
+
and `ttf.order.review(orderHash)` are promise-returning calls. Private account
|
|
878
|
+
reads require an appropriately authorized account session; the checkout buyer
|
|
879
|
+
scope does not grant it. Low-level APIs do not replace checkout preflight or
|
|
880
|
+
payment reconciliation.
|
|
881
|
+
|
|
882
|
+
The [API reference](https://unpkg.com/tf-checkout-react-native@2.0.0-beta.24/docs/API.md)
|
|
883
|
+
includes the full component and hook catalog.
|
|
884
|
+
|
|
885
|
+
## Authentication and account components
|
|
886
|
+
|
|
887
|
+
`AuthModal` accepts required `open` and `onClose`, plus `checkoutEventId`,
|
|
888
|
+
`onCheckoutAuthenticated`, `onAuthenticated`, `allowRegistration`,
|
|
889
|
+
`allowPasswordReset`, `requireRegistrationAddress`, `logoUrl`, `initialEmail`
|
|
890
|
+
and `message`. Supply `checkoutEventId` for checkout-scoped auth. The
|
|
891
|
+
`LoginForm`, `RegisterForm`, `ForgotPasswordForm`, `TwoFactorForm` and
|
|
892
|
+
`EmailCodeForm` present fields and delegate their callbacks/state to the host
|
|
893
|
+
or shared controller. `ResetPasswordForm` takes a required reset `token` and
|
|
894
|
+
optional `onSuccess` / `onBackToLogin`. `PasswordInput` wraps `Input` with a
|
|
895
|
+
visibility toggle. The expanded reference lists required form callbacks.
|
|
896
|
+
|
|
897
|
+
Additional exports include `TicketChange`, `TicketRefund`, `TicketGift`,
|
|
898
|
+
`Passes`, `Trips`, `AccountRewardsSection`, `AccountInformationSection`,
|
|
899
|
+
`AccountPaymentPlanSection`, account deletion, profile and notification views,
|
|
900
|
+
`Streaming`, `PhoneAuthentication`, `AccountEmailEnrichment`,
|
|
901
|
+
`EventDiscussionView` and post reactions. Their availability depends on the
|
|
902
|
+
deployed backend, authorized account scope and host-owned navigation. See the
|
|
903
|
+
[API reference](https://unpkg.com/tf-checkout-react-native@2.0.0-beta.24/docs/API.md)
|
|
904
|
+
for component variants and required props.
|
|
905
|
+
|
|
906
|
+
Standalone account components that use query hooks need a `QueryClientProvider`;
|
|
907
|
+
`CheckoutThemeProvider` can theme them without creating a checkout. The `View`
|
|
908
|
+
variants let a host create and retain a shared flow itself. The host must handle
|
|
909
|
+
account sign-in, deep links, browser handoff and navigation; these components do
|
|
910
|
+
not grant account scope by being rendered.
|
|
911
|
+
|
|
912
|
+
## Theme, primitives and localization
|
|
913
|
+
|
|
914
|
+
Pass a partial `CheckoutTheme` to `EmbeddedCheckout`, `CheckoutProvider` or
|
|
915
|
+
`CheckoutThemeProvider`. `colorScheme` is `'light'` (default) or `'dark'`;
|
|
916
|
+
other tokens are `accent`, `accentContrast`, `surface`, `surfaceMuted`,
|
|
917
|
+
`background`, `text`, `muted`, `border`, `error`, `success`, `radiusCard`,
|
|
918
|
+
`radiusControl`, `fontSize`, `fontFamily`. Colors/fonts are strings; radii and
|
|
919
|
+
font size are numbers. `resolveTheme(theme)` returns all resolved tokens and
|
|
920
|
+
`useTheme()` reads the current theme. Check contrast and native accessibility.
|
|
442
921
|
|
|
443
|
-
```
|
|
444
|
-
|
|
922
|
+
```tsx
|
|
923
|
+
<EmbeddedCheckout eventId="YOUR_EVENT_ID" theme={{
|
|
924
|
+
colorScheme: 'dark', accent: '#7c3aed', accentContrast: '#ffffff', radiusCard: 16,
|
|
925
|
+
}} />
|
|
445
926
|
```
|
|
446
927
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
`
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
928
|
+
The exported theme-aware primitives are `Loader`, `Card`, `SectionHeader`,
|
|
929
|
+
`Divider`, `BodyText`, `MutedText`, `ErrorText`, `Button`, `FrozenFields`,
|
|
930
|
+
`Field`, `Input`, `Checkbox`, `Select`, `QuantitySelect`, `Timer`, `TimerBanner`,
|
|
931
|
+
`PaymentMethods` and `RichText`. `useStyles` and `useFrozen` are also exported.
|
|
932
|
+
`PaymentMethods` is a controlled presentation component, not a Pay action.
|
|
933
|
+
`RichText` accepts `html`, `textStyle`, `tagsStyles`, `onLinkPress` and
|
|
934
|
+
`selectable`; it uses the render-html peer. Test external links with your host's
|
|
935
|
+
navigation policy. `FrozenFields` disables its supported SDK controls when the
|
|
936
|
+
order is fixed; custom controls need the same guard. The expanded API reference
|
|
937
|
+
has the individual control props.
|
|
938
|
+
|
|
939
|
+
Selected sections accept `locale`, `messages` and `translate`; these are per
|
|
940
|
+
component, not a promise that every `EmbeddedCheckout` string can be globally
|
|
941
|
+
replaced. Shared localization exports include `createTranslator`,
|
|
942
|
+
`resolveLocale`, `registerCatalogue` and `DEFAULT_LOCALE`. Organizer-authored
|
|
943
|
+
content is data, and should not be assumed to have a translation.
|
|
944
|
+
|
|
945
|
+
## Migration from 1.x
|
|
946
|
+
|
|
947
|
+
This is a major integration change. Pin 1.x while planning migration and test
|
|
948
|
+
against the exact new version, backend and devices. The historical
|
|
949
|
+
[1.0.47 README](https://unpkg.com/tf-checkout-react-native@1.0.47/README.md)
|
|
950
|
+
and [1.0.47 declarations](https://unpkg.com/tf-checkout-react-native@1.0.47/lib/typescript/src/index.d.ts)
|
|
951
|
+
describe the old package; examples there are not 2.x instructions.
|
|
952
|
+
|
|
953
|
+
| 1.x integration | 2.x beta.24 approach |
|
|
954
|
+
| --- | --- |
|
|
955
|
+
| `setConfig({ EVENT_ID, ENV, ... })` and event-bound global UI | Restore a secure adapter and call `configureCheckout({ env, clientId, clientSecret: '', storage })`; pass `eventId` to the checkout or section. |
|
|
956
|
+
| `Tickets` callbacks such as `onAddToCartSuccess` / `onPressMyOrders` | `Tickets` uses `onContinue` / `onSelectSeats`; the host owns orders navigation. Rework the host flow, not just prop names. |
|
|
957
|
+
| 1.x `Checkout`, `CheckoutV2`, `BillingInfo` and older core components | Use `EmbeddedCheckout` or compose the 2.x sections and `CheckoutProvider`. The 1.x `CheckoutV2` name does not mean this 2.x API. |
|
|
958
|
+
| 1.x `useCheckout()` mutation | Native `useCheckout()` is a stateful engine binding with `setTickets`, setters and `placeOrder`; query hooks are under `hooks`. |
|
|
959
|
+
| Implicit old native storage and session behavior | Supply and restore an OS-protected synchronous adapter. Migrate credentials securely or ask the buyer to sign in again. Checkout-only and account scopes differ. |
|
|
960
|
+
| Per-component style/text props from the old UI | Use theme tokens, available render callbacks and section props; rebuild custom presentation where required. |
|
|
961
|
+
| Existing Stripe setup | Verify current peer/native builds, receiving account, provider URLs, return scheme, wallets and PaymentSheet separately. |
|
|
962
|
+
|
|
963
|
+
Rebuild the native binary after replacing native dependencies and pods. The 1.x
|
|
964
|
+
My Orders/order-detail screens, PDF handling and resale screen are not drop-in
|
|
965
|
+
2.x equivalents: build the needed host UI, navigation and PDF handling around
|
|
966
|
+
the available order/resale hooks and APIs. Revalidate refund, resale, account
|
|
967
|
+
and password-protected event flows against the deployed service.
|
|
968
|
+
|
|
969
|
+
## Testing and troubleshooting
|
|
970
|
+
|
|
971
|
+
1. Pin the SDK and peer versions in the lockfile; verify one React, React Query
|
|
972
|
+
and native-module instance. Make a clean development build and install pods.
|
|
973
|
+
2. Use an authorized test event and receiving account. Check free and paid
|
|
974
|
+
orders, stock and pricing, required fields/waivers, add-ons, holders, promo
|
|
975
|
+
and referral classification, time slots and seat holds. Verify actual orders
|
|
976
|
+
against the backend and provider, including fee/currency/account context.
|
|
977
|
+
3. Test supported iOS/Android devices: backgrounding, cold/warm returns,
|
|
978
|
+
interruption, network loss, duplicate taps, expiry, cancellation, guest
|
|
979
|
+
sign-in and account changes. Test each enabled method, wallet and account.
|
|
980
|
+
4. Configure production merchant credentials, entitlements, public client ID,
|
|
981
|
+
HTTPS endpoint and callback scheme. Remove test-card hints; verify storage,
|
|
982
|
+
privacy and backend contracts. Never log real card details.
|
|
983
|
+
|
|
984
|
+
| Symptom | Check |
|
|
985
|
+
| --- | --- |
|
|
986
|
+
| `TS2304: Cannot find name 'Crypto'` in SDK declarations | A host without DOM and without a compatible ambient `Crypto` type may report this. Prefer compatible host type definitions. `skipLibCheck` skips declaration-file checks, and unresolved `Crypto` can degrade affected types to `any`, allowing invalid `newBundleIdempotencyKey` arguments. Neither option installs runtime crypto. |
|
|
987
|
+
| Configuration/storage error | Hydrate first, then pass an object with synchronous `getItem`, `setItem`, `removeItem`; check native storage failures. |
|
|
988
|
+
| `No QueryClient set` or provider context error | Wrap standalone query hooks in `QueryClientProvider`, and checkout sections in `CheckoutProvider`; check duplicate React Query copies. |
|
|
989
|
+
| Blank chart or missing seat CTA | Confirm event seating support, pass `onSelectSeats`, rebuild WebView, and verify provider/chart/hold contracts. |
|
|
990
|
+
| Missing Apple Pay / Google Pay / Link | Check backend eligibility, real receiving account, device support and host wallet configuration; a simulator or local configuration alone may not qualify. |
|
|
991
|
+
| PaymentSheet unavailable or card field requested | Check static scheme/return URL, original intent and supported method/currency combination. The embedded component does not expose sheet configuration. |
|
|
992
|
+
| Redirect does not return | Register and route the exact app scheme, including cold starts; verify the provider account and allowed return URL. |
|
|
993
|
+
| Email/holder/privacy validation does not match UI | Align `BillingInfo` and `Payment` policies; load current event gates and custom fields. |
|
|
994
|
+
| Outcome unclear or reset returns false | Keep the original order and reconcile backend/provider status. A page close or timeout is not proof of no charge. |
|
|
995
|
+
| Code/referral or conditional question differs from hosted checkout | Classify incoming links server-side or from an authoritative source; native URL reading is a no-op and conditional visibility is incomplete in this beta. |
|