@tagadapay/headless-sdk 1.20.0 → 1.21.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +2 -0
  3. package/dist/core/helpers/cryptoOnchain.d.ts +83 -0
  4. package/dist/core/helpers/cryptoOnchain.d.ts.map +1 -0
  5. package/dist/core/helpers/cryptoOnchain.js +247 -0
  6. package/dist/core/helpers/cryptoOnchain.js.map +1 -0
  7. package/dist/core/helpers/oceanpayment.d.ts +33 -0
  8. package/dist/core/helpers/oceanpayment.d.ts.map +1 -0
  9. package/dist/core/helpers/oceanpayment.js +111 -0
  10. package/dist/core/helpers/oceanpayment.js.map +1 -0
  11. package/dist/core/helpers/paymentsApi.d.ts +139 -0
  12. package/dist/core/helpers/paymentsApi.d.ts.map +1 -0
  13. package/dist/core/helpers/paymentsApi.js +287 -0
  14. package/dist/core/helpers/paymentsApi.js.map +1 -0
  15. package/dist/core/helpers/paymentsApiAnswer.d.ts +22 -0
  16. package/dist/core/helpers/paymentsApiAnswer.d.ts.map +1 -0
  17. package/dist/core/helpers/paymentsApiAnswer.js +51 -0
  18. package/dist/core/helpers/paymentsApiAnswer.js.map +1 -0
  19. package/dist/core/http.d.ts.map +1 -1
  20. package/dist/core/http.js +3 -0
  21. package/dist/core/http.js.map +1 -1
  22. package/dist/core/index.d.ts +3 -1
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/index.js +2 -0
  25. package/dist/core/index.js.map +1 -1
  26. package/dist/core/modules/checkout.d.ts.map +1 -1
  27. package/dist/core/modules/checkout.js +1 -0
  28. package/dist/core/modules/checkout.js.map +1 -1
  29. package/dist/core/modules/payment.d.ts +74 -1
  30. package/dist/core/modules/payment.d.ts.map +1 -1
  31. package/dist/core/modules/payment.js +331 -8
  32. package/dist/core/modules/payment.js.map +1 -1
  33. package/dist/core/modules/portal/types.d.ts +2 -0
  34. package/dist/core/modules/portal/types.d.ts.map +1 -1
  35. package/dist/core/types.d.ts +164 -0
  36. package/dist/core/types.d.ts.map +1 -1
  37. package/dist/core/version.d.ts +1 -1
  38. package/dist/core/version.js +1 -1
  39. package/dist/pixels/pixelMapping.d.ts.map +1 -1
  40. package/dist/pixels/pixelMapping.js +9 -1
  41. package/dist/pixels/pixelMapping.js.map +1 -1
  42. package/dist/react/hooks/useCryptoOnchainPayment.d.ts +40 -0
  43. package/dist/react/hooks/useCryptoOnchainPayment.d.ts.map +1 -0
  44. package/dist/react/hooks/useCryptoOnchainPayment.js +77 -0
  45. package/dist/react/hooks/useCryptoOnchainPayment.js.map +1 -0
  46. package/dist/react/hooks/usePayment.d.ts.map +1 -1
  47. package/dist/react/hooks/usePayment.js +3 -0
  48. package/dist/react/hooks/usePayment.js.map +1 -1
  49. package/dist/react/index.d.ts +4 -1
  50. package/dist/react/index.d.ts.map +1 -1
  51. package/dist/react/index.js +2 -0
  52. package/dist/react/index.js.map +1 -1
  53. package/dist/tagada-headless-commerce.min.js +1 -1
  54. package/dist/tagada-headless-react.min.js +2 -2
  55. package/dist/tagada-headless-react.min.js.map +4 -4
  56. package/dist/tagada-headless.js +685 -16
  57. package/dist/tagada-headless.min.js +2 -2
  58. package/dist/tagada-headless.min.js.map +4 -4
  59. package/dist/tagada-pixels.min.js +2 -2
  60. package/dist/tagada-pixels.min.js.map +3 -3
  61. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,62 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.21.0] — 2026-10-23
11
+
12
+ ### Added
13
+
14
+ - Stores on the Payments API (V3) pay with the same calls. When `pay()` / `payDirect()` / `processApm()` answer
15
+ `{ paymentId, clientSecret, amount, currency }` with no `payment` (a payment the page confirms: local methods,
16
+ payDirect), `finalize()` and `processPayment()` confirm it with `@tagadapay/core-js/payments-v3`, run its next
17
+ action (3D Secure in a modal, a wallet step), call `complete` for the order and answer the usual
18
+ `ProcessPaymentResult` (`succeeded` with the order, `failed` with the decline message, `requires_redirect`,
19
+ or `pending` while the payment is still processing). A redirect returns with the usual `paymentAction` /
20
+ `paymentId` parameters; `maybeResumeFromUrl()`, `resumeAfterRedirect()` and `usePayment()` resume it.
21
+ Callers provide nothing new on Tagada-hosted pages: the publishable key comes from the injected step config
22
+ (`window.__TGD_STEP_CONFIG__.paymentsApi`). Needs `@tagadapay/core-js` 3.7.0 or later, loaded only on a V3
23
+ payment. Not in the CDN build yet: there the payment answers `failed` with a message saying so.
24
+ - The publishable key follows the checkout session's mode, as in Studio: a draft session takes the test key, any
25
+ other the live key, never the other mode's. With no key for that mode the payment answers `failed` with a
26
+ message saying so. `CheckoutSession` gains the optional `draft` field (`loadSession()` reads it); a session the
27
+ page never loaded pays live.
28
+ - Card, Apple Pay and Google Pay payments on a V3 store keep the V2-shaped answer and path. V2 stores are unchanged.
29
+
30
+ ### Changed
31
+
32
+ - The optional peer `@tagadapay/core-js` is now `^3.7.0` (was `^3.0.0`). If you install core-js next to this
33
+ SDK, upgrade it with this release: `npm install @tagadapay/headless-sdk@1.21 @tagadapay/core-js@^3.7`. Bundlers
34
+ resolve the new `@tagadapay/core-js/payments-v3` import at build time, and core-js 3.6 and older do not have it.
35
+ core-js 3.7 is a minor release: nothing you use from 3.x changes. If you do not install core-js, nothing changes.
36
+
37
+ ## [1.20.1] — 2026-10-22
38
+
39
+ ### Added
40
+
41
+ - Every request carries `X-Tagada-SDK: headless-sdk/<version>`, so the platform can tell which SDK version a store runs. No contract change.
42
+
43
+ ### Added
44
+
45
+ - `payment.mountOceanPayment({ checkoutSessionId, container, mode?, returnUrl?, locale?, signal? })`: pay a
46
+ checkout session with the merchant's own Ocean Payment account, the way the hosted checkout does. With
47
+ Ocean's public key on the integration, Ocean's card fields render in `container`; otherwise, or with
48
+ `mode: 'redirect'`, `pay()` sends the shopper to Ocean's page and back to `returnUrl` (default: the
49
+ current page). `payment.getOceanPaymentConfig(checkoutSessionId)` tells which of the two the store runs.
50
+ - `maybeResumeFromUrl()` and `usePayment()` resume an Ocean card payment after 3-D Secure, which returns
51
+ the shopper to the page with nothing in the URL.
52
+ - On-chain crypto (`crypto_onchain`), paid from your own page with no redirect:
53
+ `payment.getCryptoOnchainOptions(checkoutSessionId)` (networks and coins),
54
+ `payment.createCryptoOnchainPayment({ checkoutSessionId, chainId, asset })` (deposit address, exact amount,
55
+ wallet URI for the QR code; call again for another coin or an expired quote) and
56
+ `payment.getCryptoOnchainStatus({ checkoutSessionId, cryptoOnchainPaymentId })`. `payment.createCryptoOnchainCheckout()`
57
+ runs the whole flow (default network and coin, status polling, quote countdown, `onPaid` once confirmed) and
58
+ `useCryptoOnchainPayment()` is its React hook. Never send `crypto_onchain` to `pay()` / `processApm()`: the API
59
+ refuses it with `422 CRYPTO_ONCHAIN_INLINE_ONLY`.
60
+
61
+ ### Fixed
62
+
63
+ - Polling a payment (`processPayment()`, `resumeAfterRedirect()`, `pollPaymentStatus()`) stops at once on
64
+ `rejected` and `error` instead of waiting the full timeout and returning `pending`.
65
+
10
66
  ## [1.20.0] — 2026-10-16
11
67
 
12
68
  ### Added
package/README.md CHANGED
@@ -115,6 +115,8 @@ The return type is a discriminated union:
115
115
  | `failed` | Payment declined or errored. `result.error` has details. |
116
116
  | `pending` | Still processing (rare — polling timed out). |
117
117
 
118
+ **Stores on the Payments API (V3), from 1.21.0.** Same calls. Card and Apple Pay / Google Pay payments are answered as on V2. For a payment the page confirms (`processApm()`, `payDirect()`, `processPayment()` with no card), `pay()` answers `{ paymentId, clientSecret, amount, currency }`: pass it to `finalize()` (`processPayment()` does it), which confirms it with `@tagadapay/core-js/payments-v3` (install `@tagadapay/core-js` 3.7.0 or later) and answers the result above. It needs the page's publishable key, injected on Tagada-hosted pages (`window.__TGD_STEP_CONFIG__.paymentsApi`); the CDN build does not support it yet.
119
+
118
120
  ## Address Handling
119
121
 
120
122
  The SDK handles both **shipped** and **digital** products:
@@ -0,0 +1,83 @@
1
+ /**
2
+ * On-chain crypto (`crypto_onchain`): the pure rules of the inline payment and the
3
+ * `CryptoOnchainCheckout` flow. Same behaviour as the hosted (Studio) checkout's crypto panel:
4
+ * default network and coin, status phases, quote countdown, polling until paid.
5
+ */
6
+ import type { CryptoOnchainCheckoutOptions, CryptoOnchainCheckoutState, CryptoOnchainNetwork, CryptoOnchainOptionsResult, CryptoOnchainPaymentResult, CryptoOnchainPhase, CryptoOnchainSelection, CryptoOnchainStatus, CryptoOnchainStatusResult } from '../types.js';
7
+ /** `paymentSetupConfig` key and payment method name of on-chain crypto. */
8
+ export declare const CRYPTO_ONCHAIN_PAYMENT_METHOD = "crypto_onchain";
9
+ /** How often the checkout polls the status, in milliseconds. */
10
+ export declare const CRYPTO_ONCHAIN_STATUS_POLL_MS = 5000;
11
+ /** Phases after which polling stops. */
12
+ export declare const CRYPTO_ONCHAIN_FINAL_PHASES: readonly CryptoOnchainPhase[];
13
+ /** True for `crypto_onchain` in any spelling a checkout uses (`crypto-onchain`, `crypto_onchain:int_…`). */
14
+ export declare function isCryptoOnchainMethod(method: string | null | undefined): boolean;
15
+ /**
16
+ * The default pick: the first mainnet network (testnets only when nothing else is offered) and its
17
+ * first stablecoin, else its first coin. Null when no network offers a coin.
18
+ */
19
+ export declare function defaultCryptoOnchainSelection(networks: CryptoOnchainNetwork[]): CryptoOnchainSelection | null;
20
+ /** Switching network keeps the coin when the new network offers it, else takes its default coin. */
21
+ export declare function selectCryptoOnchainNetwork(networks: CryptoOnchainNetwork[], chainId: number, currentAsset: string): CryptoOnchainSelection;
22
+ /** The phase of the latest status (or of the payment as created, before the first poll). */
23
+ export declare function cryptoOnchainPhase(status: Pick<CryptoOnchainStatus, 'status' | 'paid' | 'expired'> | null): CryptoOnchainPhase;
24
+ /** Whole seconds until `expiresAt`, never negative; 0 for a missing or unparsable date. */
25
+ export declare function cryptoOnchainSecondsLeft(expiresAt: string | null, nowMs: number): number;
26
+ /** `m:ss` under an hour, `h:mm:ss` above. */
27
+ export declare function formatCryptoOnchainCountdown(seconds: number): string;
28
+ /** The three calls the flow needs; `PaymentModule` provides them. */
29
+ export interface CryptoOnchainApi {
30
+ getCryptoOnchainOptions(checkoutSessionId: string): Promise<CryptoOnchainOptionsResult>;
31
+ createCryptoOnchainPayment(opts: {
32
+ checkoutSessionId: string;
33
+ chainId: number;
34
+ asset: string;
35
+ }): Promise<CryptoOnchainPaymentResult>;
36
+ getCryptoOnchainStatus(opts: {
37
+ checkoutSessionId: string;
38
+ cryptoOnchainPaymentId: string;
39
+ }): Promise<CryptoOnchainStatusResult>;
40
+ }
41
+ /**
42
+ * The inline crypto payment of one checkout session: load the networks and coins, keep the pick,
43
+ * create (or re-quote) the payment, poll it until crypto-api confirms it and call `onPaid` once.
44
+ * Create it with `tagada.payment.createCryptoOnchainCheckout()`.
45
+ */
46
+ export declare class CryptoOnchainCheckout {
47
+ private readonly api;
48
+ private readonly options;
49
+ private state;
50
+ private readonly listeners;
51
+ private pollTimer;
52
+ private stopped;
53
+ private paidNotified;
54
+ constructor(api: CryptoOnchainApi, options: CryptoOnchainCheckoutOptions);
55
+ getState(): CryptoOnchainCheckoutState;
56
+ /** Listens to every state change; returns the unsubscribe function. */
57
+ subscribe(listener: (state: CryptoOnchainCheckoutState) => void): () => void;
58
+ /** What to show: the polled phase, or `expired` once an unpaid quote ran out (renew with `pay()`). */
59
+ getPhase(nowMs?: number): CryptoOnchainPhase;
60
+ /** Whole seconds left on the current quote. */
61
+ getSecondsLeft(nowMs?: number): number;
62
+ /** Loads the networks and coins and picks the default network and coin. */
63
+ loadOptions(): Promise<CryptoOnchainCheckoutState>;
64
+ /** Changes the pick. A new network keeps the coin when it offers it, else takes its default coin. */
65
+ select(next: {
66
+ chainId?: number;
67
+ asset?: string;
68
+ }): void;
69
+ /**
70
+ * Creates the payment for the pick (or `selection`), or re-quotes it: another coin, or a quote
71
+ * that expired. Save the customer and address on the session first. Starts polling.
72
+ */
73
+ pay(selection?: CryptoOnchainSelection): Promise<CryptoOnchainPaymentResult>;
74
+ /** Polls the status once now (the timer keeps polling until the payment is final). */
75
+ refreshStatus(): Promise<CryptoOnchainStatus | null>;
76
+ /** Polls again after `stop()`, while a payment is open (the customer came back to crypto). */
77
+ resume(): void;
78
+ /** Stops polling (unmount, or the customer chose another payment method). */
79
+ stop(): void;
80
+ private schedulePoll;
81
+ private setState;
82
+ }
83
+ //# sourceMappingURL=cryptoOnchain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cryptoOnchain.d.ts","sourceRoot":"","sources":["../../../src/core/helpers/cryptoOnchain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,247 @@
1
+ /** `paymentSetupConfig` key and payment method name of on-chain crypto. */
2
+ export const CRYPTO_ONCHAIN_PAYMENT_METHOD = 'crypto_onchain';
3
+ /** How often the checkout polls the status, in milliseconds. */
4
+ export const CRYPTO_ONCHAIN_STATUS_POLL_MS = 5000;
5
+ /** Phases after which polling stops. */
6
+ export const CRYPTO_ONCHAIN_FINAL_PHASES = ['paid', 'underpaid', 'failed', 'expired'];
7
+ /** True for `crypto_onchain` in any spelling a checkout uses (`crypto-onchain`, `crypto_onchain:int_…`). */
8
+ export function isCryptoOnchainMethod(method) {
9
+ if (!method)
10
+ return false;
11
+ return method.trim().toLowerCase().replace(/-/g, '_').split(':')[0] === CRYPTO_ONCHAIN_PAYMENT_METHOD;
12
+ }
13
+ /**
14
+ * The default pick: the first mainnet network (testnets only when nothing else is offered) and its
15
+ * first stablecoin, else its first coin. Null when no network offers a coin.
16
+ */
17
+ export function defaultCryptoOnchainSelection(networks) {
18
+ const usable = networks.filter((network) => network.assets.length > 0);
19
+ const network = usable.find((candidate) => !candidate.testnet) ?? usable[0];
20
+ if (!network)
21
+ return null;
22
+ const asset = network.assets.find((candidate) => candidate.stable) ?? network.assets[0];
23
+ return { chainId: network.chainId, asset: asset.symbol };
24
+ }
25
+ /** Switching network keeps the coin when the new network offers it, else takes its default coin. */
26
+ export function selectCryptoOnchainNetwork(networks, chainId, currentAsset) {
27
+ const assets = networks.find((network) => network.chainId === chainId)?.assets ?? [];
28
+ const keep = assets.find((asset) => asset.symbol === currentAsset);
29
+ const fallback = assets.find((asset) => asset.stable) ?? assets[0];
30
+ return { chainId, asset: (keep ?? fallback)?.symbol ?? currentAsset };
31
+ }
32
+ /** The phase of the latest status (or of the payment as created, before the first poll). */
33
+ export function cryptoOnchainPhase(status) {
34
+ if (!status)
35
+ return 'waiting';
36
+ if (status.paid)
37
+ return 'paid';
38
+ switch (status.status) {
39
+ case 'detected':
40
+ case 'overpaid':
41
+ return 'confirming';
42
+ case 'underpaid':
43
+ return 'underpaid';
44
+ case 'failed':
45
+ return 'failed';
46
+ case 'expired':
47
+ return 'expired';
48
+ default:
49
+ return status.expired ? 'expired' : 'waiting';
50
+ }
51
+ }
52
+ /** Whole seconds until `expiresAt`, never negative; 0 for a missing or unparsable date. */
53
+ export function cryptoOnchainSecondsLeft(expiresAt, nowMs) {
54
+ if (!expiresAt)
55
+ return 0;
56
+ const end = Date.parse(expiresAt);
57
+ if (Number.isNaN(end))
58
+ return 0;
59
+ return Math.max(0, Math.floor((end - nowMs) / 1000));
60
+ }
61
+ /** `m:ss` under an hour, `h:mm:ss` above. */
62
+ export function formatCryptoOnchainCountdown(seconds) {
63
+ const s = Math.max(0, Math.floor(seconds));
64
+ const h = Math.floor(s / 3600);
65
+ const m = Math.floor((s % 3600) / 60);
66
+ const sec = String(s % 60).padStart(2, '0');
67
+ return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${sec}` : `${m}:${sec}`;
68
+ }
69
+ const UNAVAILABLE = 'Crypto payments are not available right now. Choose another payment method.';
70
+ /**
71
+ * The inline crypto payment of one checkout session: load the networks and coins, keep the pick,
72
+ * create (or re-quote) the payment, poll it until crypto-api confirms it and call `onPaid` once.
73
+ * Create it with `tagada.payment.createCryptoOnchainCheckout()`.
74
+ */
75
+ export class CryptoOnchainCheckout {
76
+ constructor(api, options) {
77
+ this.api = api;
78
+ this.options = options;
79
+ this.state = {
80
+ options: 'idle',
81
+ networks: [],
82
+ selection: null,
83
+ payment: null,
84
+ paymentStatus: null,
85
+ isCreating: false,
86
+ error: null,
87
+ };
88
+ this.listeners = new Set();
89
+ this.pollTimer = null;
90
+ this.stopped = false;
91
+ this.paidNotified = false;
92
+ this.subscribe = this.subscribe.bind(this);
93
+ this.getState = this.getState.bind(this);
94
+ }
95
+ getState() {
96
+ return this.state;
97
+ }
98
+ /** Listens to every state change; returns the unsubscribe function. */
99
+ subscribe(listener) {
100
+ this.listeners.add(listener);
101
+ return () => {
102
+ this.listeners.delete(listener);
103
+ };
104
+ }
105
+ /** What to show: the polled phase, or `expired` once an unpaid quote ran out (renew with `pay()`). */
106
+ getPhase(nowMs = Date.now()) {
107
+ const { payment, paymentStatus } = this.state;
108
+ const phase = cryptoOnchainPhase(paymentStatus ?? (payment ? { status: payment.status, paid: false, expired: false } : null));
109
+ const quoteOver = payment !== null && phase === 'waiting' && cryptoOnchainSecondsLeft(payment.expiresAt, nowMs) === 0;
110
+ return quoteOver ? 'expired' : phase;
111
+ }
112
+ /** Whole seconds left on the current quote. */
113
+ getSecondsLeft(nowMs = Date.now()) {
114
+ return cryptoOnchainSecondsLeft(this.state.payment?.expiresAt ?? null, nowMs);
115
+ }
116
+ /** Loads the networks and coins and picks the default network and coin. */
117
+ async loadOptions() {
118
+ this.setState({ options: 'loading', error: null });
119
+ try {
120
+ const result = await this.api.getCryptoOnchainOptions(this.options.checkoutSessionId);
121
+ if (!result.ok) {
122
+ this.setState({ options: 'unavailable', networks: [], selection: null, error: result.message || UNAVAILABLE });
123
+ return this.state;
124
+ }
125
+ const selection = this.state.selection ?? defaultCryptoOnchainSelection(result.networks);
126
+ this.setState(selection
127
+ ? { options: 'ready', networks: result.networks, selection }
128
+ : { options: 'unavailable', networks: result.networks, selection: null, error: UNAVAILABLE });
129
+ }
130
+ catch (error) {
131
+ this.setState({ options: 'unavailable', error: messageOf(error) });
132
+ }
133
+ return this.state;
134
+ }
135
+ /** Changes the pick. A new network keeps the coin when it offers it, else takes its default coin. */
136
+ select(next) {
137
+ const current = this.state.selection;
138
+ if (next.chainId !== undefined && next.chainId !== current?.chainId) {
139
+ this.setState({ selection: selectCryptoOnchainNetwork(this.state.networks, next.chainId, next.asset ?? current?.asset ?? '') });
140
+ return;
141
+ }
142
+ if (next.asset !== undefined && current)
143
+ this.setState({ selection: { ...current, asset: next.asset } });
144
+ }
145
+ /**
146
+ * Creates the payment for the pick (or `selection`), or re-quotes it: another coin, or a quote
147
+ * that expired. Save the customer and address on the session first. Starts polling.
148
+ */
149
+ async pay(selection) {
150
+ const target = selection ?? this.state.selection;
151
+ if (!target)
152
+ return { ok: false, code: 'ASSET_NOT_AVAILABLE', message: 'Choose a network and a coin' };
153
+ if (this.state.isCreating)
154
+ return { ok: false, code: 'CHECKOUT_FAILED', message: 'A payment is already being created' };
155
+ this.setState({ isCreating: true, error: null, selection: target });
156
+ try {
157
+ const result = await this.api.createCryptoOnchainPayment({
158
+ checkoutSessionId: this.options.checkoutSessionId,
159
+ chainId: target.chainId,
160
+ asset: target.asset,
161
+ });
162
+ if (!result.ok) {
163
+ this.setState({ isCreating: false, error: result.message });
164
+ return result;
165
+ }
166
+ this.stopped = false;
167
+ this.setState({ isCreating: false, payment: result.payment, paymentStatus: null });
168
+ this.schedulePoll();
169
+ return result;
170
+ }
171
+ catch (error) {
172
+ const message = messageOf(error);
173
+ this.setState({ isCreating: false, error: message });
174
+ return { ok: false, code: 'CHECKOUT_FAILED', message };
175
+ }
176
+ }
177
+ /** Polls the status once now (the timer keeps polling until the payment is final). */
178
+ async refreshStatus() {
179
+ const payment = this.state.payment;
180
+ if (!payment)
181
+ return null;
182
+ try {
183
+ const result = await this.api.getCryptoOnchainStatus({
184
+ checkoutSessionId: this.options.checkoutSessionId,
185
+ cryptoOnchainPaymentId: payment.cryptoOnchainPaymentId,
186
+ });
187
+ // A newer quote replaced this payment while the call was in flight.
188
+ if (!result.ok || this.state.payment?.cryptoOnchainPaymentId !== payment.cryptoOnchainPaymentId)
189
+ return null;
190
+ const status = {
191
+ status: result.status,
192
+ paymentId: result.paymentId,
193
+ orderId: result.orderId,
194
+ confirmations: result.confirmations,
195
+ requiredConfirmations: result.requiredConfirmations,
196
+ txHash: result.txHash,
197
+ explorerTxUrl: result.explorerTxUrl,
198
+ paid: result.paid,
199
+ expired: result.expired,
200
+ redirectUrl: result.redirectUrl,
201
+ };
202
+ this.setState({ paymentStatus: status });
203
+ if (status.paid && !this.paidNotified) {
204
+ this.paidNotified = true;
205
+ this.options.onPaid?.(status);
206
+ }
207
+ return status;
208
+ }
209
+ catch {
210
+ // The next tick retries; the payment still settles server side.
211
+ return null;
212
+ }
213
+ }
214
+ /** Polls again after `stop()`, while a payment is open (the customer came back to crypto). */
215
+ resume() {
216
+ if (!this.state.payment || CRYPTO_ONCHAIN_FINAL_PHASES.includes(this.getPhase()))
217
+ return;
218
+ this.stopped = false;
219
+ this.schedulePoll();
220
+ }
221
+ /** Stops polling (unmount, or the customer chose another payment method). */
222
+ stop() {
223
+ this.stopped = true;
224
+ if (this.pollTimer)
225
+ clearTimeout(this.pollTimer);
226
+ this.pollTimer = null;
227
+ }
228
+ schedulePoll() {
229
+ if (this.pollTimer)
230
+ clearTimeout(this.pollTimer);
231
+ this.pollTimer = setTimeout(async () => {
232
+ this.pollTimer = null;
233
+ await this.refreshStatus();
234
+ if (!this.stopped && !CRYPTO_ONCHAIN_FINAL_PHASES.includes(this.getPhase()))
235
+ this.schedulePoll();
236
+ }, this.options.pollIntervalMs ?? CRYPTO_ONCHAIN_STATUS_POLL_MS);
237
+ }
238
+ setState(patch) {
239
+ this.state = { ...this.state, ...patch };
240
+ this.options.onChange?.(this.state);
241
+ this.listeners.forEach((listener) => listener(this.state));
242
+ }
243
+ }
244
+ function messageOf(error) {
245
+ return error instanceof Error && error.message ? error.message : UNAVAILABLE;
246
+ }
247
+ //# sourceMappingURL=cryptoOnchain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cryptoOnchain.js","sourceRoot":"","sources":["../../../src/core/helpers/cryptoOnchain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Browser plumbing for Ocean Payment's inline card form (Oceanpayment
3
+ * Embedded SDK). Mirrors the hosted checkout's OceanPaymentPanel
4
+ * (apps/studio-app/.../PaymentMethodSelector/TakeoverPanels.tsx).
5
+ */
6
+ export interface OceanpaymentGlobal {
7
+ init: (sandbox: boolean, cssUrl: string, language: string, configuration: Record<string, unknown>) => void;
8
+ checkout: (data: Record<string, string>) => void;
9
+ }
10
+ export type OceanWindow = Window & {
11
+ Oceanpayment?: OceanpaymentGlobal;
12
+ /** Ocean's SDK calls this global — the name is fixed by the SDK. */
13
+ oceanpaymentCallBack?: (data: unknown) => void;
14
+ };
15
+ export declare const OCEAN_SDK_URL = "https://secure.oceanpayment.com/pages/js/oceanpayment.js";
16
+ /** Fixed by Ocean's SDK, which looks the container up by this id. */
17
+ export declare const OCEAN_CONTAINER_ID = "oceanpayment-element";
18
+ /** Ocean's iframe answers every checkout(); this only guards a lost message. */
19
+ export declare const OCEAN_CALLBACK_TIMEOUT_MS = 120000;
20
+ export declare function oceanLanguage(locale: string | undefined): string;
21
+ /**
22
+ * Load Ocean's SDK once per page: a second copy would deliver every result
23
+ * twice. A failed or stalled load is forgotten, so the next mount retries.
24
+ */
25
+ export declare function loadOceanSdk(): Promise<OceanpaymentGlobal>;
26
+ export declare function parkOceanPayment(paymentId: string): void;
27
+ /**
28
+ * Back from Ocean's 3-D Secure page: turn the parked payment into the
29
+ * `paymentAction` / `paymentActionStatus` / `paymentId` markers that
30
+ * `maybeResumeFromUrl()` and `usePayment()` already resume from.
31
+ */
32
+ export declare function restoreOceanPaymentReturn(): void;
33
+ //# sourceMappingURL=oceanpayment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oceanpayment.d.ts","sourceRoot":"","sources":["../../../src/core/helpers/oceanpayment.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC3G,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;CAClD;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG;IACjC,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,oEAAoE;IACpE,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,eAAO,MAAM,aAAa,6DAA6D,CAAC;AACxF,qEAAqE;AACrE,eAAO,MAAM,kBAAkB,yBAAyB,CAAC;AACzD,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,SAAU,CAAC;AASjD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAIhE;AAKD;;;GAGG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAwB1D;AAWD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAMxD;AAeD;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAchD"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Browser plumbing for Ocean Payment's inline card form (Oceanpayment
3
+ * Embedded SDK). Mirrors the hosted checkout's OceanPaymentPanel
4
+ * (apps/studio-app/.../PaymentMethodSelector/TakeoverPanels.tsx).
5
+ */
6
+ export const OCEAN_SDK_URL = 'https://secure.oceanpayment.com/pages/js/oceanpayment.js';
7
+ /** Fixed by Ocean's SDK, which looks the container up by this id. */
8
+ export const OCEAN_CONTAINER_ID = 'oceanpayment-element';
9
+ /** Ocean's iframe answers every checkout(); this only guards a lost message. */
10
+ export const OCEAN_CALLBACK_TIMEOUT_MS = 120000;
11
+ /**
12
+ * Ocean builds its card fields server-side from `?language=`. An unknown code
13
+ * falls back to English, and `fi` comes back in Polish, so Finnish shoppers
14
+ * get English.
15
+ */
16
+ const OCEAN_LANGUAGE_OVERRIDES = { fi: 'en' };
17
+ export function oceanLanguage(locale) {
18
+ const primary = locale?.split(/[-_]/)[0]?.toLowerCase();
19
+ if (!primary)
20
+ return 'en';
21
+ return OCEAN_LANGUAGE_OVERRIDES[primary] ?? primary;
22
+ }
23
+ const OCEAN_SDK_LOAD_TIMEOUT_MS = 30000;
24
+ let oceanSdkLoading = null;
25
+ /**
26
+ * Load Ocean's SDK once per page: a second copy would deliver every result
27
+ * twice. A failed or stalled load is forgotten, so the next mount retries.
28
+ */
29
+ export function loadOceanSdk() {
30
+ const w = window;
31
+ if (w.Oceanpayment)
32
+ return Promise.resolve(w.Oceanpayment);
33
+ if (oceanSdkLoading)
34
+ return oceanSdkLoading;
35
+ oceanSdkLoading = new Promise((resolve, reject) => {
36
+ const script = document.createElement('script');
37
+ const fail = (message) => {
38
+ clearTimeout(timer);
39
+ script.remove();
40
+ oceanSdkLoading = null;
41
+ reject(new Error(message));
42
+ };
43
+ const timer = setTimeout(() => fail('The Ocean Payment card form took too long to load'), OCEAN_SDK_LOAD_TIMEOUT_MS);
44
+ script.addEventListener('load', () => {
45
+ if (!w.Oceanpayment)
46
+ return fail('Ocean Payment script loaded without window.Oceanpayment');
47
+ clearTimeout(timer);
48
+ resolve(w.Oceanpayment);
49
+ });
50
+ script.addEventListener('error', () => fail('Failed to load the Ocean Payment card form'));
51
+ script.src = OCEAN_SDK_URL;
52
+ document.head.appendChild(script);
53
+ });
54
+ return oceanSdkLoading;
55
+ }
56
+ /**
57
+ * Ocean's inline card form leaves the page only for 3-D Secure, and Ocean
58
+ * returns the shopper to the exact page URL the form was opened on — without
59
+ * any payment reference in it. The payment id is parked in sessionStorage
60
+ * before leaving; a stale entry (abandoned 3DS) is ignored after 30 minutes.
61
+ */
62
+ const OCEAN_PENDING_KEY = 'tgd:headless:oceanpayment:pending';
63
+ const OCEAN_PENDING_TTL_MS = 30 * 60 * 1000;
64
+ export function parkOceanPayment(paymentId) {
65
+ try {
66
+ sessionStorage.setItem(OCEAN_PENDING_KEY, JSON.stringify({ paymentId, at: Date.now() }));
67
+ }
68
+ catch {
69
+ // Storage unavailable — Ocean's webhook still settles the order.
70
+ }
71
+ }
72
+ function takeParkedOceanPayment() {
73
+ try {
74
+ const raw = sessionStorage.getItem(OCEAN_PENDING_KEY);
75
+ if (!raw)
76
+ return undefined;
77
+ sessionStorage.removeItem(OCEAN_PENDING_KEY);
78
+ const { paymentId, at } = JSON.parse(raw);
79
+ if (!paymentId || !at || Date.now() - at > OCEAN_PENDING_TTL_MS)
80
+ return undefined;
81
+ return paymentId;
82
+ }
83
+ catch {
84
+ return undefined;
85
+ }
86
+ }
87
+ /**
88
+ * Back from Ocean's 3-D Secure page: turn the parked payment into the
89
+ * `paymentAction` / `paymentActionStatus` / `paymentId` markers that
90
+ * `maybeResumeFromUrl()` and `usePayment()` already resume from.
91
+ */
92
+ export function restoreOceanPaymentReturn() {
93
+ if (typeof window === 'undefined')
94
+ return;
95
+ const paymentId = takeParkedOceanPayment();
96
+ if (!paymentId)
97
+ return;
98
+ try {
99
+ const url = new URL(window.location.href);
100
+ if (url.searchParams.get('paymentId'))
101
+ return;
102
+ url.searchParams.set('paymentId', paymentId);
103
+ url.searchParams.set('paymentAction', 'requireAction');
104
+ url.searchParams.set('paymentActionStatus', 'completed');
105
+ window.history.replaceState(window.history.state, '', url.toString());
106
+ }
107
+ catch {
108
+ // Best-effort: Ocean's webhook still settles the order.
109
+ }
110
+ }
111
+ //# sourceMappingURL=oceanpayment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oceanpayment.js","sourceRoot":"","sources":["../../../src/core/helpers/oceanpayment.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,MAAM,CAAC,MAAM,aAAa,GAAG,0DAA0D,CAAC;AACxF,qEAAqE;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AACzD,gFAAgF;AAChF,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAO,CAAC;AAEjD;;;;GAIG;AACH,MAAM,wBAAwB,GAA2B,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AAEtE,MAAM,UAAU,aAAa,CAAC,MAA0B;IACtD,MAAM,OAAO,GAAG,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IACxD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,wBAAwB,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC;AACtD,CAAC;AAED,MAAM,yBAAyB,GAAG,KAAM,CAAC;AACzC,IAAI,eAAe,GAAuC,IAAI,CAAC;AAE/D;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,CAAC,GAAG,MAAqB,CAAC;IAChC,IAAI,CAAC,CAAC,YAAY;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IAC3D,IAAI,eAAe;QAAE,OAAO,eAAe,CAAC;IAE5C,eAAe,GAAG,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACpE,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,EAAE;YAC/B,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,eAAe,GAAG,IAAI,CAAC;YACvB,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7B,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,mDAAmD,CAAC,EAAE,yBAAyB,CAAC,CAAC;QACrH,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE;YACnC,IAAI,CAAC,CAAC,CAAC,YAAY;gBAAE,OAAO,IAAI,CAAC,yDAAyD,CAAC,CAAC;YAC5F,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC,CAAC;QAC3F,MAAM,CAAC,GAAG,GAAG,aAAa,CAAC;QAC3B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IACH,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,mCAAmC,CAAC;AAC9D,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE5C,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,IAAI,CAAC;QACH,cAAc,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3F,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;IACnE,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACtD,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAC3B,cAAc,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QAC7C,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwC,CAAC;QACjF,IAAI,CAAC,SAAS,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,oBAAoB;YAAE,OAAO,SAAS,CAAC;QAClF,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB;IACvC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO;IAC1C,MAAM,SAAS,GAAG,sBAAsB,EAAE,CAAC;IAC3C,IAAI,CAAC,SAAS;QAAE,OAAO;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC;YAAE,OAAO;QAC9C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAC7C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;QACvD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;QACzD,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,wDAAwD;IAC1D,CAAC;AACH,CAAC"}