@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.
- package/CHANGELOG.md +56 -0
- package/README.md +2 -0
- package/dist/core/helpers/cryptoOnchain.d.ts +83 -0
- package/dist/core/helpers/cryptoOnchain.d.ts.map +1 -0
- package/dist/core/helpers/cryptoOnchain.js +247 -0
- package/dist/core/helpers/cryptoOnchain.js.map +1 -0
- package/dist/core/helpers/oceanpayment.d.ts +33 -0
- package/dist/core/helpers/oceanpayment.d.ts.map +1 -0
- package/dist/core/helpers/oceanpayment.js +111 -0
- package/dist/core/helpers/oceanpayment.js.map +1 -0
- package/dist/core/helpers/paymentsApi.d.ts +139 -0
- package/dist/core/helpers/paymentsApi.d.ts.map +1 -0
- package/dist/core/helpers/paymentsApi.js +287 -0
- package/dist/core/helpers/paymentsApi.js.map +1 -0
- package/dist/core/helpers/paymentsApiAnswer.d.ts +22 -0
- package/dist/core/helpers/paymentsApiAnswer.d.ts.map +1 -0
- package/dist/core/helpers/paymentsApiAnswer.js +51 -0
- package/dist/core/helpers/paymentsApiAnswer.js.map +1 -0
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +3 -0
- package/dist/core/http.js.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/modules/checkout.d.ts.map +1 -1
- package/dist/core/modules/checkout.js +1 -0
- package/dist/core/modules/checkout.js.map +1 -1
- package/dist/core/modules/payment.d.ts +74 -1
- package/dist/core/modules/payment.d.ts.map +1 -1
- package/dist/core/modules/payment.js +331 -8
- package/dist/core/modules/payment.js.map +1 -1
- package/dist/core/modules/portal/types.d.ts +2 -0
- package/dist/core/modules/portal/types.d.ts.map +1 -1
- package/dist/core/types.d.ts +164 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/pixels/pixelMapping.d.ts.map +1 -1
- package/dist/pixels/pixelMapping.js +9 -1
- package/dist/pixels/pixelMapping.js.map +1 -1
- package/dist/react/hooks/useCryptoOnchainPayment.d.ts +40 -0
- package/dist/react/hooks/useCryptoOnchainPayment.d.ts.map +1 -0
- package/dist/react/hooks/useCryptoOnchainPayment.js +77 -0
- package/dist/react/hooks/useCryptoOnchainPayment.js.map +1 -0
- package/dist/react/hooks/usePayment.d.ts.map +1 -1
- package/dist/react/hooks/usePayment.js +3 -0
- package/dist/react/hooks/usePayment.js.map +1 -1
- package/dist/react/index.d.ts +4 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -1
- package/dist/tagada-headless-commerce.min.js +1 -1
- package/dist/tagada-headless-react.min.js +2 -2
- package/dist/tagada-headless-react.min.js.map +4 -4
- package/dist/tagada-headless.js +685 -16
- package/dist/tagada-headless.min.js +2 -2
- package/dist/tagada-headless.min.js.map +4 -4
- package/dist/tagada-pixels.min.js +2 -2
- package/dist/tagada-pixels.min.js.map +3 -3
- 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":"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"}
|