@tagadapay/headless-sdk 1.9.0 → 1.12.0

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 CHANGED
@@ -1,216 +1,220 @@
1
- # @tagadapay/headless-sdk
2
-
3
- Use TagadaPay as a **headless checkout and CRM backend** from any website — React, Vue, vanilla JS, or plain HTML. Zero runtime dependencies in core.
4
-
5
- ## Install
6
-
7
- ```bash
8
- npm install @tagadapay/headless-sdk
9
- # Optional: for card tokenization
10
- npm install @tagadapay/core-js
11
- ```
12
-
13
- ## Quick Start (Vanilla JS)
14
-
15
- ```typescript
16
- import { createHeadlessClient } from '@tagadapay/headless-sdk';
17
-
18
- const tagada = createHeadlessClient({
19
- storeId: 'store_abc123',
20
- environment: 'production',
21
- });
22
-
23
- // Load checkout session
24
- const session = await tagada.checkout.loadSession(checkoutToken, sessionToken);
25
-
26
- // Set customer info (stored in memory, sent with address)
27
- await tagada.checkout.updateCustomer(session.id, {
28
- email: 'john@example.com',
29
- firstName: 'John',
30
- lastName: 'Doe',
31
- });
32
-
33
- // Set address — customer fields are included automatically
34
- await tagada.checkout.updateAddress(session.id, {
35
- shippingAddress: { line1: '10 Rue de la Paix', city: 'Paris', postalCode: '75002', country: 'FR' },
36
- });
37
-
38
- // Tokenize card + pay with automatic 3DS / redirect handling
39
- const { tagadaToken } = await tagada.payment.tokenizeCard({
40
- cardNumber: '4242424242424242',
41
- expiryDate: '12/28',
42
- cvc: '123',
43
- });
44
-
45
- const result = await tagada.payment.processPayment({
46
- checkoutSessionId: session.id,
47
- tagadaToken,
48
- });
49
-
50
- if (result.status === 'succeeded') {
51
- console.log('Payment succeeded!', result.order);
52
- } else if (result.status === 'requires_redirect') {
53
- // SDK handles this automatically in React; in vanilla JS, redirect manually:
54
- window.location.href = result.redirectUrl;
55
- } else if (result.status === 'failed') {
56
- console.error(result.error);
57
- }
58
- ```
59
-
60
- ## Quick Start (React)
61
-
62
- ```tsx
63
- import { TagadaHeadlessProvider, useCheckout, usePayment } from '@tagadapay/headless-sdk/react';
64
-
65
- function App() {
66
- return (
67
- <TagadaHeadlessProvider storeId="store_abc123" environment="production">
68
- <CheckoutPage />
69
- </TagadaHeadlessProvider>
70
- );
71
- }
72
-
73
- function CheckoutPage() {
74
- const { session, updateCustomer, updateAddress } = useCheckout(checkoutToken, sessionToken);
75
- const { processPayment, isProcessing, tokenizeCard } = usePayment({
76
- onPaymentSuccess: (result) => router.push('/thank-you'),
77
- onPaymentFailed: (result) => alert(result.error),
78
- });
79
-
80
- const handlePay = async () => {
81
- // Customer + address (single atomic API call under the hood)
82
- await updateCustomer({ email, firstName, lastName });
83
- await updateAddress({ shippingAddress: { line1, city, postalCode, country } });
84
-
85
- // Tokenize + pay — 3DS redirects and polling are handled automatically
86
- const { tagadaToken } = await tokenizeCard({ cardNumber, expiryDate, cvc });
87
- await processPayment({ checkoutSessionId: session.id, tagadaToken });
88
- // onPaymentSuccess / onPaymentFailed callbacks fire automatically
89
- };
90
-
91
- return <button onClick={handlePay} disabled={isProcessing}>Pay Now</button>;
92
- }
93
- ```
94
-
95
- ## Payment Flow
96
-
97
- `processPayment()` handles the full payment lifecycle automatically:
98
-
99
- 1. **Tokenize** → creates a payment instrument from the card
100
- 2. **Pay** → submits payment to the processor
101
- 3. **3DS / Redirect** → if the bank requires authentication, the SDK redirects the user and resumes automatically on return
102
- 4. **Poll** → waits for async processing to complete
103
- 5. **Result** → returns a typed `ProcessPaymentResult`
104
-
105
- The return type is a discriminated union:
106
-
107
- | Status | Meaning |
108
- |--------|---------|
109
- | `succeeded` | Payment confirmed. Show success screen. |
110
- | `requires_redirect` | User must visit a URL (3DS, bank auth). Auto-handled in React. |
111
- | `failed` | Payment declined or errored. `result.error` has details. |
112
- | `pending` | Still processing (rare — polling timed out). |
113
-
114
- ## Address Handling
115
-
116
- The SDK handles both **shipped** and **digital** products:
117
-
118
- ```typescript
119
- // Shipped product — billing defaults to shipping
120
- await tagada.checkout.updateAddress(sessionId, {
121
- shippingAddress: { line1: '...', city: '...', country: 'FR' },
122
- });
123
-
124
- // Digital product — billing only, no shipping needed
125
- await tagada.checkout.updateAddress(sessionId, {
126
- billingAddress: { country: 'FR' },
127
- });
128
-
129
- // Separate billing and shipping
130
- await tagada.checkout.updateAddress(sessionId, {
131
- shippingAddress: { line1: '...', city: '...', country: 'FR' },
132
- billingAddress: { line1: '...', city: '...', country: 'DE' },
133
- });
134
-
135
- // Or use the combined method
136
- await tagada.checkout.updateCustomerAndAddress(sessionId, {
137
- customer: { email: 'john@example.com', firstName: 'John', lastName: 'Doe' },
138
- shippingAddress: { line1: '...', city: '...', country: 'FR' },
139
- });
140
- ```
141
-
142
- ## Funnel Navigation
143
-
144
- Send a shopper into a TagadaPay funnel using the cart from the loaded checkout
145
- session — same flow as the WooCommerce / Shopify plugins, in one call.
146
-
147
- ```typescript
148
- const tagada = createHeadlessClient({
149
- storeId: 'store_abc123',
150
- accountId: 'acc_abc123', // required for funnel.navigate()
151
- environment: 'production',
152
- });
153
-
154
- // Load (or create) a checkout session so the SDK has a cart.
155
- await tagada.checkout.loadSession(checkoutToken, sessionToken);
156
-
157
- // Get a funnel URL — caller decides how to redirect (SPA-friendly).
158
- const { url } = await tagada.funnel.navigate({
159
- funnelId: 'fnlv2_abc123',
160
- stepId: 'step_abc123',
161
- returnUrl: 'https://merchant.com/thank-you',
162
- });
163
-
164
- window.location.assign(url);
165
- ```
166
-
167
- `funnel.navigate()` bootstraps an anonymous CMS session on first call and
168
- caches it for subsequent calls in the same page. The cart, currency, customer
169
- email, and promotion code are sourced from the loaded `CheckoutSession`.
170
-
171
- ## Modules
172
-
173
- | Module | What it does |
174
- |--------|-------------|
175
- | `tagada.checkout` | Session management — load, update cart, promo codes, shipping |
176
- | `tagada.payment` | Full payment lifecycle — APMs, tokenize, 3DS, pay, poll, express checkout |
177
- | `tagada.offers` | Upsells & order bumps — preview, pay, post-purchase accept/decline |
178
- | `tagada.customer` | CRM data — profile, orders, subscriptions |
179
- | `tagada.catalog` | Product browsing — list products, variants, prices |
180
- | `tagada.funnel` | Funnel navigation — bootstrap session and redirect into a funnel step |
181
-
182
- ## CDN Usage
183
-
184
- ```html
185
- <script src="https://cdn.jsdelivr.net/npm/@tagadapay/headless-sdk/dist/tagada-headless.min.js"></script>
186
- <script>
187
- const tagada = TagadaHeadless.create({ storeId: 'store_abc123' });
188
- </script>
189
- ```
190
-
191
- ## Example
192
-
193
- A complete React checkout app is available as a live demo and open-source reference:
194
-
195
- - **Live demo**: [headless-react-eta.vercel.app](https://headless-react-eta.vercel.app)
196
- - **Source code**: [github.com/TagadaPay/examples/tree/main/headless-react](https://github.com/TagadaPay/examples/tree/main/headless-react)
197
-
198
- ## Architecture
199
-
200
- - **Zero dependencies** in core — native `fetch`, no axios
201
- - **Tree-shakeable ESM** — unused modules are eliminated by bundlers
202
- - **Framework-agnostic core** + thin React hooks layer
203
- - **TypeScript first** — full type coverage, JSDoc on every method
204
- - **Optional `@tagadapay/core-js`** peer — only needed for client-side card tokenization
205
- - **Automatic 3DS** — handles redirects, iframe challenges, and polling out of the box
206
-
207
- ## Package Exports
208
-
209
- | Import path | What you get |
210
- |------------|--------------|
211
- | `@tagadapay/headless-sdk` | Core client + all modules |
212
- | `@tagadapay/headless-sdk/react` | React hooks + provider |
213
-
214
- ## License
215
-
216
- MIT
1
+ # @tagadapay/headless-sdk
2
+
3
+ Use TagadaPay as a **headless checkout and CRM backend** from any website — React, Vue, vanilla JS, or plain HTML. Zero runtime dependencies in core.
4
+
5
+ This package is for when **TagadaPay manages the cart**. If you already have the order (Medusa, WooCommerce, a custom order API), do not use this SDK — tokenize with `@tagadapay/core-js` and charge with Node `payments.process()`. See [Choose how you accept payments](https://docs.tagada.io/developer-tools/payments/choose-your-integration). Canonical example: [headless-vanilla](https://github.com/TagadaPay/examples/tree/main/headless-vanilla).
6
+
7
+ `processPayment()` pays a checkout **session**. Server `payments.process()` charges a vaulted **instrument**. They are not the same call.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @tagadapay/headless-sdk
13
+ # Optional: for card tokenization
14
+ npm install @tagadapay/core-js
15
+ ```
16
+
17
+ ## Quick Start (Vanilla JS)
18
+
19
+ ```typescript
20
+ import { createHeadlessClient } from '@tagadapay/headless-sdk';
21
+
22
+ const tagada = createHeadlessClient({
23
+ storeId: 'store_abc123',
24
+ environment: 'production',
25
+ });
26
+
27
+ // Load checkout session
28
+ const session = await tagada.checkout.loadSession(checkoutToken, sessionToken);
29
+
30
+ // Set customer info (stored in memory, sent with address)
31
+ await tagada.checkout.updateCustomer(session.id, {
32
+ email: 'john@example.com',
33
+ firstName: 'John',
34
+ lastName: 'Doe',
35
+ });
36
+
37
+ // Set address — customer fields are included automatically
38
+ await tagada.checkout.updateAddress(session.id, {
39
+ shippingAddress: { line1: '10 Rue de la Paix', city: 'Paris', postalCode: '75002', country: 'FR' },
40
+ });
41
+
42
+ // Tokenize card + pay with automatic 3DS / redirect handling
43
+ const { tagadaToken } = await tagada.payment.tokenizeCard({
44
+ cardNumber: '4242424242424242',
45
+ expiryDate: '12/28',
46
+ cvc: '123',
47
+ });
48
+
49
+ const result = await tagada.payment.processPayment({
50
+ checkoutSessionId: session.id,
51
+ tagadaToken,
52
+ });
53
+
54
+ if (result.status === 'succeeded') {
55
+ console.log('Payment succeeded!', result.order);
56
+ } else if (result.status === 'requires_redirect') {
57
+ // SDK handles this automatically in React; in vanilla JS, redirect manually:
58
+ window.location.href = result.redirectUrl;
59
+ } else if (result.status === 'failed') {
60
+ console.error(result.error);
61
+ }
62
+ ```
63
+
64
+ ## Quick Start (React)
65
+
66
+ ```tsx
67
+ import { TagadaHeadlessProvider, useCheckout, usePayment } from '@tagadapay/headless-sdk/react';
68
+
69
+ function App() {
70
+ return (
71
+ <TagadaHeadlessProvider storeId="store_abc123" environment="production">
72
+ <CheckoutPage />
73
+ </TagadaHeadlessProvider>
74
+ );
75
+ }
76
+
77
+ function CheckoutPage() {
78
+ const { session, updateCustomer, updateAddress } = useCheckout(checkoutToken, sessionToken);
79
+ const { processPayment, isProcessing, tokenizeCard } = usePayment({
80
+ onPaymentSuccess: (result) => router.push('/thank-you'),
81
+ onPaymentFailed: (result) => alert(result.error),
82
+ });
83
+
84
+ const handlePay = async () => {
85
+ // Customer + address (single atomic API call under the hood)
86
+ await updateCustomer({ email, firstName, lastName });
87
+ await updateAddress({ shippingAddress: { line1, city, postalCode, country } });
88
+
89
+ // Tokenize + pay — 3DS redirects and polling are handled automatically
90
+ const { tagadaToken } = await tokenizeCard({ cardNumber, expiryDate, cvc });
91
+ await processPayment({ checkoutSessionId: session.id, tagadaToken });
92
+ // onPaymentSuccess / onPaymentFailed callbacks fire automatically
93
+ };
94
+
95
+ return <button onClick={handlePay} disabled={isProcessing}>Pay Now</button>;
96
+ }
97
+ ```
98
+
99
+ ## Payment Flow
100
+
101
+ `processPayment()` handles the full payment lifecycle automatically:
102
+
103
+ 1. **Tokenize** → creates a payment instrument from the card
104
+ 2. **Pay** → submits payment to the processor
105
+ 3. **3DS / Redirect** → if the bank requires authentication, the SDK redirects the user and resumes automatically on return
106
+ 4. **Poll** → waits for async processing to complete
107
+ 5. **Result** → returns a typed `ProcessPaymentResult`
108
+
109
+ The return type is a discriminated union:
110
+
111
+ | Status | Meaning |
112
+ |--------|---------|
113
+ | `succeeded` | Payment confirmed. Show success screen. |
114
+ | `requires_redirect` | User must visit a URL (3DS, bank auth). Auto-handled in React. |
115
+ | `failed` | Payment declined or errored. `result.error` has details. |
116
+ | `pending` | Still processing (rare — polling timed out). |
117
+
118
+ ## Address Handling
119
+
120
+ The SDK handles both **shipped** and **digital** products:
121
+
122
+ ```typescript
123
+ // Shipped product — billing defaults to shipping
124
+ await tagada.checkout.updateAddress(sessionId, {
125
+ shippingAddress: { line1: '...', city: '...', country: 'FR' },
126
+ });
127
+
128
+ // Digital product — billing only, no shipping needed
129
+ await tagada.checkout.updateAddress(sessionId, {
130
+ billingAddress: { country: 'FR' },
131
+ });
132
+
133
+ // Separate billing and shipping
134
+ await tagada.checkout.updateAddress(sessionId, {
135
+ shippingAddress: { line1: '...', city: '...', country: 'FR' },
136
+ billingAddress: { line1: '...', city: '...', country: 'DE' },
137
+ });
138
+
139
+ // Or use the combined method
140
+ await tagada.checkout.updateCustomerAndAddress(sessionId, {
141
+ customer: { email: 'john@example.com', firstName: 'John', lastName: 'Doe' },
142
+ shippingAddress: { line1: '...', city: '...', country: 'FR' },
143
+ });
144
+ ```
145
+
146
+ ## Funnel Navigation
147
+
148
+ Send a shopper into a TagadaPay funnel using the cart from the loaded checkout
149
+ session — same flow as the WooCommerce / Shopify plugins, in one call.
150
+
151
+ ```typescript
152
+ const tagada = createHeadlessClient({
153
+ storeId: 'store_abc123',
154
+ accountId: 'acc_abc123', // required for funnel.navigate()
155
+ environment: 'production',
156
+ });
157
+
158
+ // Load (or create) a checkout session so the SDK has a cart.
159
+ await tagada.checkout.loadSession(checkoutToken, sessionToken);
160
+
161
+ // Get a funnel URL — caller decides how to redirect (SPA-friendly).
162
+ const { url } = await tagada.funnel.navigate({
163
+ funnelId: 'fnlv2_abc123',
164
+ stepId: 'step_abc123',
165
+ returnUrl: 'https://merchant.com/thank-you',
166
+ });
167
+
168
+ window.location.assign(url);
169
+ ```
170
+
171
+ `funnel.navigate()` bootstraps an anonymous CMS session on first call and
172
+ caches it for subsequent calls in the same page. The cart, currency, customer
173
+ email, and promotion code are sourced from the loaded `CheckoutSession`.
174
+
175
+ ## Modules
176
+
177
+ | Module | What it does |
178
+ |--------|-------------|
179
+ | `tagada.checkout` | Session management — load, update cart, promo codes, shipping |
180
+ | `tagada.payment` | Full payment lifecycle — APMs, tokenize, 3DS, pay, poll, express checkout |
181
+ | `tagada.offers` | Upsells & order bumps — preview, pay, post-purchase accept/decline |
182
+ | `tagada.customer` | CRM data — profile, orders, subscriptions |
183
+ | `tagada.catalog` | Product browsing — list products, variants, prices |
184
+ | `tagada.funnel` | Funnel navigation — bootstrap session and redirect into a funnel step |
185
+
186
+ ## CDN Usage
187
+
188
+ ```html
189
+ <script src="https://cdn.jsdelivr.net/npm/@tagadapay/headless-sdk/dist/tagada-headless.min.js"></script>
190
+ <script>
191
+ const tagada = TagadaHeadless.create({ storeId: 'store_abc123' });
192
+ </script>
193
+ ```
194
+
195
+ ## Example
196
+
197
+ A complete React checkout app is available as a live demo and open-source reference:
198
+
199
+ - **Live demo**: [headless-react-eta.vercel.app](https://headless-react-eta.vercel.app)
200
+ - **Source code**: [github.com/TagadaPay/examples/tree/main/headless-react](https://github.com/TagadaPay/examples/tree/main/headless-react)
201
+
202
+ ## Architecture
203
+
204
+ - **Zero dependencies** in core — native `fetch`, no axios
205
+ - **Tree-shakeable ESM** — unused modules are eliminated by bundlers
206
+ - **Framework-agnostic core** + thin React hooks layer
207
+ - **TypeScript first** — full type coverage, JSDoc on every method
208
+ - **Optional `@tagadapay/core-js`** peer — only needed for client-side card tokenization
209
+ - **Automatic 3DS** — handles redirects, iframe challenges, and polling out of the box
210
+
211
+ ## Package Exports
212
+
213
+ | Import path | What you get |
214
+ |------------|--------------|
215
+ | `@tagadapay/headless-sdk` | Core client + all modules |
216
+ | `@tagadapay/headless-sdk/react` | React hooks + provider |
217
+
218
+ ## License
219
+
220
+ MIT
@@ -30,6 +30,12 @@ export declare class PaymentModule {
30
30
  private readonly environment;
31
31
  /** Used to persist wallet contacts onto the session before charging. */
32
32
  private readonly checkout?;
33
+ /**
34
+ * Last payment setup seen by `getPaymentSetup` — lets `processApplePay`
35
+ * resolve the domain's Apple Pay registration (BYOK / platform-integrator)
36
+ * without a second network round-trip inside Apple's authorization window.
37
+ */
38
+ private lastPaymentSetup;
33
39
  constructor(http: HttpClient, storeId: string, environment: Environment,
34
40
  /** Used to persist wallet contacts onto the session before charging. */
35
41
  checkout?: CheckoutModule | undefined);
@@ -167,7 +173,36 @@ export declare class PaymentModule {
167
173
  shippingContact?: ApplePayContact | null;
168
174
  paymentFlowId?: string;
169
175
  processorId?: string;
176
+ /**
177
+ * Basis Theory Apple Pay registration the checkout domain is bound to.
178
+ * Required when the domain runs merchant-scoped Apple Pay — BYOK or the
179
+ * platform-integrator (PPIID) system — because Apple encrypts the token
180
+ * to that registration's certificate. Resolved automatically from the
181
+ * payment setup (`applePayByok` domain map) when omitted; pass it only to
182
+ * override that resolution.
183
+ */
184
+ merchantRegistrationId?: string;
170
185
  }): Promise<PayResult>;
186
+ /**
187
+ * Resolve the Apple Pay registration binding for the domain the page is
188
+ * served from. Both merchant-certificate (BYOK) and platform-integrator
189
+ * (PPIID) Apple Pay attach a domain-keyed `applePayByok` map to the
190
+ * `apple_pay` method's metadata; a hit means every Basis Theory call —
191
+ * merchant session AND tokenization — must carry that
192
+ * `merchant_registration_id`. A miss (the default) means legacy platform
193
+ * Apple Pay: omit the id and behavior is unchanged.
194
+ *
195
+ * Sources, in order: the Tagada routing injection, the last payment setup
196
+ * seen by `getPaymentSetup`, then one API fetch when a `checkoutSessionId`
197
+ * is given and no setup was ever loaded.
198
+ *
199
+ * Use `displayName` (when present) as the `display_name` of the Basis
200
+ * Theory merchant session.
201
+ */
202
+ resolveApplePayRegistration(checkoutSessionId?: string): Promise<{
203
+ merchantRegistrationId: string;
204
+ displayName?: string;
205
+ } | null>;
171
206
  /**
172
207
  * Process a Google Pay payment.
173
208
  * Tokenizes the Google Pay token and processes payment in one call.
@@ -1 +1 @@
1
- {"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["../../../src/core/modules/payment.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["../../../src/core/modules/payment.ts"],"names":[],"mappings":"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"}
@@ -29,6 +29,12 @@ export class PaymentModule {
29
29
  this.storeId = storeId;
30
30
  this.environment = environment;
31
31
  this.checkout = checkout;
32
+ /**
33
+ * Last payment setup seen by `getPaymentSetup` — lets `processApplePay`
34
+ * resolve the domain's Apple Pay registration (BYOK / platform-integrator)
35
+ * without a second network round-trip inside Apple's authorization window.
36
+ */
37
+ this.lastPaymentSetup = null;
32
38
  }
33
39
  // ---------------------------------------------------------------------------
34
40
  // Discovery: what payment methods are available?
@@ -45,10 +51,12 @@ export class PaymentModule {
45
51
  */
46
52
  async getPaymentSetup(checkoutSessionId, opts) {
47
53
  if (opts?.paymentSetupConfig) {
54
+ this.lastPaymentSetup = opts.paymentSetupConfig;
48
55
  return opts.paymentSetupConfig;
49
56
  }
50
57
  const injected = this.readInjectedStepConfig();
51
58
  if (injected) {
59
+ this.lastPaymentSetup = injected;
52
60
  return injected;
53
61
  }
54
62
  const methods = await this.http.get('/api/v1/payment-methods', { params: { checkoutSessionId } });
@@ -57,6 +65,7 @@ export class PaymentModule {
57
65
  const key = m.provider ? `${m.method}:${m.provider}` : m.method;
58
66
  config[key] = m;
59
67
  }
68
+ this.lastPaymentSetup = config;
60
69
  return config;
61
70
  }
62
71
  /**
@@ -336,10 +345,17 @@ export class PaymentModule {
336
345
  firstName: opts.shippingContact?.givenName ?? opts.billingContact?.givenName,
337
346
  lastName: opts.shippingContact?.familyName ?? opts.billingContact?.familyName,
338
347
  });
348
+ const merchantRegistrationId = opts.merchantRegistrationId ??
349
+ (await this.resolveApplePayRegistration(opts.checkoutSessionId))?.merchantRegistrationId;
339
350
  const coreJs = await this.loadCoreJs();
340
351
  const tokenizer = new coreJs.Tokenizer({ environment: this.environment });
341
352
  await tokenizer.initialize();
342
- const { tagadaToken } = await tokenizer.tokenizeApplePay(opts.applePayToken);
353
+ // The tokenizer returns the normalized Basis Theory response — it carries
354
+ // no `tagadaToken` field. Wrap it into the standard base64 TagadaToken the
355
+ // pay endpoint expects (destructuring `{ tagadaToken }` here used to yield
356
+ // undefined and silently push the charge down the instrument-less path).
357
+ const rawToken = await tokenizer.tokenizeApplePay(opts.applePayToken, merchantRegistrationId);
358
+ const tagadaToken = btoa(JSON.stringify(coreJs.createTagadaDigitalWalletToken(rawToken, 'apple_pay')));
343
359
  return this.pay({
344
360
  checkoutSessionId: opts.checkoutSessionId,
345
361
  tagadaToken,
@@ -349,6 +365,55 @@ export class PaymentModule {
349
365
  isExpress: true,
350
366
  });
351
367
  }
368
+ /**
369
+ * Resolve the Apple Pay registration binding for the domain the page is
370
+ * served from. Both merchant-certificate (BYOK) and platform-integrator
371
+ * (PPIID) Apple Pay attach a domain-keyed `applePayByok` map to the
372
+ * `apple_pay` method's metadata; a hit means every Basis Theory call —
373
+ * merchant session AND tokenization — must carry that
374
+ * `merchant_registration_id`. A miss (the default) means legacy platform
375
+ * Apple Pay: omit the id and behavior is unchanged.
376
+ *
377
+ * Sources, in order: the Tagada routing injection, the last payment setup
378
+ * seen by `getPaymentSetup`, then one API fetch when a `checkoutSessionId`
379
+ * is given and no setup was ever loaded.
380
+ *
381
+ * Use `displayName` (when present) as the `display_name` of the Basis
382
+ * Theory merchant session.
383
+ */
384
+ async resolveApplePayRegistration(checkoutSessionId) {
385
+ if (typeof window === 'undefined')
386
+ return null;
387
+ let host = window.location.hostname.toLowerCase().split(':')[0];
388
+ if (host.startsWith('www.'))
389
+ host = host.slice(4);
390
+ const fromConfig = (setup) => {
391
+ for (const entry of Object.values(setup ?? {})) {
392
+ const map = entry?.metadata?.applePayByok;
393
+ if (!map || typeof map !== 'object')
394
+ continue;
395
+ const hit = map[host];
396
+ if (hit?.merchantRegistrationId) {
397
+ return { merchantRegistrationId: hit.merchantRegistrationId, displayName: hit.displayName };
398
+ }
399
+ }
400
+ return null;
401
+ };
402
+ const local = fromConfig(this.readInjectedStepConfig()) ?? fromConfig(this.lastPaymentSetup);
403
+ if (local)
404
+ return local;
405
+ // A setup we already hold that carries no binding is authoritative — don't
406
+ // burn a network round-trip (inside Apple's authorization window) to
407
+ // re-learn that this is a legacy platform domain.
408
+ if (this.lastPaymentSetup || this.readInjectedStepConfig() || !checkoutSessionId)
409
+ return null;
410
+ try {
411
+ return fromConfig(await this.getPaymentSetup(checkoutSessionId));
412
+ }
413
+ catch {
414
+ return null;
415
+ }
416
+ }
352
417
  /**
353
418
  * Process a Google Pay payment.
354
419
  * Tokenizes the Google Pay token and processes payment in one call.
@@ -368,7 +433,10 @@ export class PaymentModule {
368
433
  const coreJs = await this.loadCoreJs();
369
434
  const tokenizer = new coreJs.Tokenizer({ environment: this.environment });
370
435
  await tokenizer.initialize();
371
- const { tagadaToken } = await tokenizer.tokenizeGooglePay(opts.googlePayToken);
436
+ // Same wrapping as Apple Pay above — the tokenizer's normalized response
437
+ // has no `tagadaToken` field.
438
+ const rawToken = await tokenizer.tokenizeGooglePay(opts.googlePayToken);
439
+ const tagadaToken = btoa(JSON.stringify(coreJs.createTagadaDigitalWalletToken(rawToken, 'google_pay')));
372
440
  return this.pay({
373
441
  checkoutSessionId: opts.checkoutSessionId,
374
442
  tagadaToken,