@tagadapay/headless-sdk 1.19.0 → 1.20.1
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 +193 -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/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 +4 -2
- 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/payment.d.ts +66 -1
- package/dist/core/modules/payment.d.ts.map +1 -1
- package/dist/core/modules/payment.js +297 -2
- package/dist/core/modules/payment.js.map +1 -1
- package/dist/core/modules/portal/types.d.ts +10 -1
- package/dist/core/modules/portal/types.d.ts.map +1 -1
- package/dist/core/modules/rx.d.ts +43 -0
- package/dist/core/modules/rx.d.ts.map +1 -1
- package/dist/core/modules/rx.js +13 -0
- package/dist/core/modules/rx.js.map +1 -1
- package/dist/core/types.d.ts +159 -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 +626 -9
- 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 +3 -2
package/dist/tagada-headless.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @tagadapay/headless-sdk v1.
|
|
2
|
+
* @tagadapay/headless-sdk v1.20.1
|
|
3
3
|
* Headless checkout, payments, offers, and CRM
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
@@ -44,8 +44,12 @@ var TagadaHeadlessBundle = (() => {
|
|
|
44
44
|
var core_exports = {};
|
|
45
45
|
__export(core_exports, {
|
|
46
46
|
AVAILABLE_LANGUAGES: () => AVAILABLE_LANGUAGES,
|
|
47
|
+
CRYPTO_ONCHAIN_FINAL_PHASES: () => CRYPTO_ONCHAIN_FINAL_PHASES,
|
|
48
|
+
CRYPTO_ONCHAIN_PAYMENT_METHOD: () => CRYPTO_ONCHAIN_PAYMENT_METHOD,
|
|
49
|
+
CRYPTO_ONCHAIN_STATUS_POLL_MS: () => CRYPTO_ONCHAIN_STATUS_POLL_MS,
|
|
47
50
|
CatalogModule: () => CatalogModule,
|
|
48
51
|
CheckoutModule: () => CheckoutModule,
|
|
52
|
+
CryptoOnchainCheckout: () => CryptoOnchainCheckout,
|
|
49
53
|
CurrencyUtils: () => CurrencyUtils,
|
|
50
54
|
CustomerModule: () => CustomerModule,
|
|
51
55
|
CustomerPortalModule: () => CustomerPortalModule,
|
|
@@ -67,14 +71,20 @@ var TagadaHeadlessBundle = (() => {
|
|
|
67
71
|
TagadaValidationError: () => TagadaValidationError,
|
|
68
72
|
TrackModule: () => TrackModule,
|
|
69
73
|
createHeadlessClient: () => createHeadlessClient,
|
|
74
|
+
cryptoOnchainPhase: () => cryptoOnchainPhase,
|
|
75
|
+
cryptoOnchainSecondsLeft: () => cryptoOnchainSecondsLeft,
|
|
76
|
+
defaultCryptoOnchainSelection: () => defaultCryptoOnchainSelection,
|
|
70
77
|
ensureGeoDataLoaded: () => ensureGeoDataLoaded,
|
|
78
|
+
formatCryptoOnchainCountdown: () => formatCryptoOnchainCountdown,
|
|
71
79
|
formatMoney: () => formatMoney,
|
|
72
80
|
getCountries: () => getCountries,
|
|
73
81
|
getStatesForCountry: () => getStatesForCountry,
|
|
82
|
+
isCryptoOnchainMethod: () => isCryptoOnchainMethod,
|
|
74
83
|
isLanguageLoaded: () => isLanguageLoaded,
|
|
75
84
|
isQuestionActive: () => isQuestionActive,
|
|
76
85
|
loadGoogleMapsScript: () => loadGoogleMapsScript,
|
|
77
86
|
parseTokensFromUrl: () => parseTokensFromUrl,
|
|
87
|
+
selectCryptoOnchainNetwork: () => selectCryptoOnchainNetwork,
|
|
78
88
|
toIntakeAnswers: () => toIntakeAnswers
|
|
79
89
|
});
|
|
80
90
|
|
|
@@ -130,6 +140,10 @@ var TagadaHeadlessBundle = (() => {
|
|
|
130
140
|
}
|
|
131
141
|
};
|
|
132
142
|
|
|
143
|
+
// src/core/version.ts
|
|
144
|
+
var SDK_NAME = "headless";
|
|
145
|
+
var SDK_VERSION = "1.20.1";
|
|
146
|
+
|
|
133
147
|
// src/core/http.ts
|
|
134
148
|
var BASE_URLS = {
|
|
135
149
|
production: "https://api.tagada.io",
|
|
@@ -163,6 +177,8 @@ var TagadaHeadlessBundle = (() => {
|
|
|
163
177
|
}
|
|
164
178
|
const reqHeaders = {
|
|
165
179
|
"Content-Type": "application/json",
|
|
180
|
+
// Which SDK and version sent the call: the Payments API readiness check counts them per store.
|
|
181
|
+
"X-Tagada-SDK": `headless-sdk/${SDK_VERSION}`,
|
|
166
182
|
...headers
|
|
167
183
|
};
|
|
168
184
|
const authToken = token ?? this.apiKey ?? ((_a = this.getToken) == null ? void 0 : _a.call(this));
|
|
@@ -787,12 +803,8 @@ var TagadaHeadlessBundle = (() => {
|
|
|
787
803
|
return Boolean(((_a = address.line1) == null ? void 0 : _a.trim()) && ((_b = address.country) == null ? void 0 : _b.trim()));
|
|
788
804
|
}
|
|
789
805
|
|
|
790
|
-
// src/core/version.ts
|
|
791
|
-
var SDK_NAME = "headless";
|
|
792
|
-
var SDK_VERSION = "1.19.0";
|
|
793
|
-
|
|
794
806
|
// src/core/modules/track.ts
|
|
795
|
-
var CDN_URL = false ? "" : "https://cdn.jsdelivr.net/npm/@tagadapay/headless-sdk@1.
|
|
807
|
+
var CDN_URL = false ? "" : "https://cdn.jsdelivr.net/npm/@tagadapay/headless-sdk@1.20.1/dist/tagada-pixels.min.js";
|
|
796
808
|
function loadScript(src, isReady) {
|
|
797
809
|
if (isReady())
|
|
798
810
|
return Promise.resolve();
|
|
@@ -1188,7 +1200,323 @@ var TagadaHeadlessBundle = (() => {
|
|
|
1188
1200
|
}
|
|
1189
1201
|
};
|
|
1190
1202
|
|
|
1203
|
+
// src/core/helpers/cryptoOnchain.ts
|
|
1204
|
+
var CRYPTO_ONCHAIN_PAYMENT_METHOD = "crypto_onchain";
|
|
1205
|
+
var CRYPTO_ONCHAIN_STATUS_POLL_MS = 5e3;
|
|
1206
|
+
var CRYPTO_ONCHAIN_FINAL_PHASES = ["paid", "underpaid", "failed", "expired"];
|
|
1207
|
+
function isCryptoOnchainMethod(method) {
|
|
1208
|
+
if (!method)
|
|
1209
|
+
return false;
|
|
1210
|
+
return method.trim().toLowerCase().replace(/-/g, "_").split(":")[0] === CRYPTO_ONCHAIN_PAYMENT_METHOD;
|
|
1211
|
+
}
|
|
1212
|
+
function defaultCryptoOnchainSelection(networks) {
|
|
1213
|
+
const usable = networks.filter((network2) => network2.assets.length > 0);
|
|
1214
|
+
const network = usable.find((candidate) => !candidate.testnet) ?? usable[0];
|
|
1215
|
+
if (!network)
|
|
1216
|
+
return null;
|
|
1217
|
+
const asset = network.assets.find((candidate) => candidate.stable) ?? network.assets[0];
|
|
1218
|
+
return { chainId: network.chainId, asset: asset.symbol };
|
|
1219
|
+
}
|
|
1220
|
+
function selectCryptoOnchainNetwork(networks, chainId, currentAsset) {
|
|
1221
|
+
var _a, _b;
|
|
1222
|
+
const assets = ((_a = networks.find((network) => network.chainId === chainId)) == null ? void 0 : _a.assets) ?? [];
|
|
1223
|
+
const keep = assets.find((asset) => asset.symbol === currentAsset);
|
|
1224
|
+
const fallback = assets.find((asset) => asset.stable) ?? assets[0];
|
|
1225
|
+
return { chainId, asset: ((_b = keep ?? fallback) == null ? void 0 : _b.symbol) ?? currentAsset };
|
|
1226
|
+
}
|
|
1227
|
+
function cryptoOnchainPhase(status) {
|
|
1228
|
+
if (!status)
|
|
1229
|
+
return "waiting";
|
|
1230
|
+
if (status.paid)
|
|
1231
|
+
return "paid";
|
|
1232
|
+
switch (status.status) {
|
|
1233
|
+
case "detected":
|
|
1234
|
+
case "overpaid":
|
|
1235
|
+
return "confirming";
|
|
1236
|
+
case "underpaid":
|
|
1237
|
+
return "underpaid";
|
|
1238
|
+
case "failed":
|
|
1239
|
+
return "failed";
|
|
1240
|
+
case "expired":
|
|
1241
|
+
return "expired";
|
|
1242
|
+
default:
|
|
1243
|
+
return status.expired ? "expired" : "waiting";
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
function cryptoOnchainSecondsLeft(expiresAt, nowMs) {
|
|
1247
|
+
if (!expiresAt)
|
|
1248
|
+
return 0;
|
|
1249
|
+
const end = Date.parse(expiresAt);
|
|
1250
|
+
if (Number.isNaN(end))
|
|
1251
|
+
return 0;
|
|
1252
|
+
return Math.max(0, Math.floor((end - nowMs) / 1e3));
|
|
1253
|
+
}
|
|
1254
|
+
function formatCryptoOnchainCountdown(seconds) {
|
|
1255
|
+
const s = Math.max(0, Math.floor(seconds));
|
|
1256
|
+
const h = Math.floor(s / 3600);
|
|
1257
|
+
const m = Math.floor(s % 3600 / 60);
|
|
1258
|
+
const sec = String(s % 60).padStart(2, "0");
|
|
1259
|
+
return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${sec}` : `${m}:${sec}`;
|
|
1260
|
+
}
|
|
1261
|
+
var UNAVAILABLE = "Crypto payments are not available right now. Choose another payment method.";
|
|
1262
|
+
var CryptoOnchainCheckout = class {
|
|
1263
|
+
constructor(api, options) {
|
|
1264
|
+
this.api = api;
|
|
1265
|
+
this.options = options;
|
|
1266
|
+
this.state = {
|
|
1267
|
+
options: "idle",
|
|
1268
|
+
networks: [],
|
|
1269
|
+
selection: null,
|
|
1270
|
+
payment: null,
|
|
1271
|
+
paymentStatus: null,
|
|
1272
|
+
isCreating: false,
|
|
1273
|
+
error: null
|
|
1274
|
+
};
|
|
1275
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1276
|
+
this.pollTimer = null;
|
|
1277
|
+
this.stopped = false;
|
|
1278
|
+
this.paidNotified = false;
|
|
1279
|
+
this.subscribe = this.subscribe.bind(this);
|
|
1280
|
+
this.getState = this.getState.bind(this);
|
|
1281
|
+
}
|
|
1282
|
+
getState() {
|
|
1283
|
+
return this.state;
|
|
1284
|
+
}
|
|
1285
|
+
/** Listens to every state change; returns the unsubscribe function. */
|
|
1286
|
+
subscribe(listener) {
|
|
1287
|
+
this.listeners.add(listener);
|
|
1288
|
+
return () => {
|
|
1289
|
+
this.listeners.delete(listener);
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
/** What to show: the polled phase, or `expired` once an unpaid quote ran out (renew with `pay()`). */
|
|
1293
|
+
getPhase(nowMs = Date.now()) {
|
|
1294
|
+
const { payment, paymentStatus } = this.state;
|
|
1295
|
+
const phase = cryptoOnchainPhase(paymentStatus ?? (payment ? { status: payment.status, paid: false, expired: false } : null));
|
|
1296
|
+
const quoteOver = payment !== null && phase === "waiting" && cryptoOnchainSecondsLeft(payment.expiresAt, nowMs) === 0;
|
|
1297
|
+
return quoteOver ? "expired" : phase;
|
|
1298
|
+
}
|
|
1299
|
+
/** Whole seconds left on the current quote. */
|
|
1300
|
+
getSecondsLeft(nowMs = Date.now()) {
|
|
1301
|
+
var _a;
|
|
1302
|
+
return cryptoOnchainSecondsLeft(((_a = this.state.payment) == null ? void 0 : _a.expiresAt) ?? null, nowMs);
|
|
1303
|
+
}
|
|
1304
|
+
/** Loads the networks and coins and picks the default network and coin. */
|
|
1305
|
+
async loadOptions() {
|
|
1306
|
+
this.setState({ options: "loading", error: null });
|
|
1307
|
+
try {
|
|
1308
|
+
const result = await this.api.getCryptoOnchainOptions(this.options.checkoutSessionId);
|
|
1309
|
+
if (!result.ok) {
|
|
1310
|
+
this.setState({ options: "unavailable", networks: [], selection: null, error: result.message || UNAVAILABLE });
|
|
1311
|
+
return this.state;
|
|
1312
|
+
}
|
|
1313
|
+
const selection = this.state.selection ?? defaultCryptoOnchainSelection(result.networks);
|
|
1314
|
+
this.setState(
|
|
1315
|
+
selection ? { options: "ready", networks: result.networks, selection } : { options: "unavailable", networks: result.networks, selection: null, error: UNAVAILABLE }
|
|
1316
|
+
);
|
|
1317
|
+
} catch (error) {
|
|
1318
|
+
this.setState({ options: "unavailable", error: messageOf(error) });
|
|
1319
|
+
}
|
|
1320
|
+
return this.state;
|
|
1321
|
+
}
|
|
1322
|
+
/** Changes the pick. A new network keeps the coin when it offers it, else takes its default coin. */
|
|
1323
|
+
select(next) {
|
|
1324
|
+
const current = this.state.selection;
|
|
1325
|
+
if (next.chainId !== void 0 && next.chainId !== (current == null ? void 0 : current.chainId)) {
|
|
1326
|
+
this.setState({ selection: selectCryptoOnchainNetwork(this.state.networks, next.chainId, next.asset ?? (current == null ? void 0 : current.asset) ?? "") });
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
if (next.asset !== void 0 && current)
|
|
1330
|
+
this.setState({ selection: { ...current, asset: next.asset } });
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Creates the payment for the pick (or `selection`), or re-quotes it: another coin, or a quote
|
|
1334
|
+
* that expired. Save the customer and address on the session first. Starts polling.
|
|
1335
|
+
*/
|
|
1336
|
+
async pay(selection) {
|
|
1337
|
+
const target = selection ?? this.state.selection;
|
|
1338
|
+
if (!target)
|
|
1339
|
+
return { ok: false, code: "ASSET_NOT_AVAILABLE", message: "Choose a network and a coin" };
|
|
1340
|
+
if (this.state.isCreating)
|
|
1341
|
+
return { ok: false, code: "CHECKOUT_FAILED", message: "A payment is already being created" };
|
|
1342
|
+
this.setState({ isCreating: true, error: null, selection: target });
|
|
1343
|
+
try {
|
|
1344
|
+
const result = await this.api.createCryptoOnchainPayment({
|
|
1345
|
+
checkoutSessionId: this.options.checkoutSessionId,
|
|
1346
|
+
chainId: target.chainId,
|
|
1347
|
+
asset: target.asset
|
|
1348
|
+
});
|
|
1349
|
+
if (!result.ok) {
|
|
1350
|
+
this.setState({ isCreating: false, error: result.message });
|
|
1351
|
+
return result;
|
|
1352
|
+
}
|
|
1353
|
+
this.stopped = false;
|
|
1354
|
+
this.setState({ isCreating: false, payment: result.payment, paymentStatus: null });
|
|
1355
|
+
this.schedulePoll();
|
|
1356
|
+
return result;
|
|
1357
|
+
} catch (error) {
|
|
1358
|
+
const message = messageOf(error);
|
|
1359
|
+
this.setState({ isCreating: false, error: message });
|
|
1360
|
+
return { ok: false, code: "CHECKOUT_FAILED", message };
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
/** Polls the status once now (the timer keeps polling until the payment is final). */
|
|
1364
|
+
async refreshStatus() {
|
|
1365
|
+
var _a, _b, _c;
|
|
1366
|
+
const payment = this.state.payment;
|
|
1367
|
+
if (!payment)
|
|
1368
|
+
return null;
|
|
1369
|
+
try {
|
|
1370
|
+
const result = await this.api.getCryptoOnchainStatus({
|
|
1371
|
+
checkoutSessionId: this.options.checkoutSessionId,
|
|
1372
|
+
cryptoOnchainPaymentId: payment.cryptoOnchainPaymentId
|
|
1373
|
+
});
|
|
1374
|
+
if (!result.ok || ((_a = this.state.payment) == null ? void 0 : _a.cryptoOnchainPaymentId) !== payment.cryptoOnchainPaymentId)
|
|
1375
|
+
return null;
|
|
1376
|
+
const status = {
|
|
1377
|
+
status: result.status,
|
|
1378
|
+
paymentId: result.paymentId,
|
|
1379
|
+
orderId: result.orderId,
|
|
1380
|
+
confirmations: result.confirmations,
|
|
1381
|
+
requiredConfirmations: result.requiredConfirmations,
|
|
1382
|
+
txHash: result.txHash,
|
|
1383
|
+
explorerTxUrl: result.explorerTxUrl,
|
|
1384
|
+
paid: result.paid,
|
|
1385
|
+
expired: result.expired,
|
|
1386
|
+
redirectUrl: result.redirectUrl
|
|
1387
|
+
};
|
|
1388
|
+
this.setState({ paymentStatus: status });
|
|
1389
|
+
if (status.paid && !this.paidNotified) {
|
|
1390
|
+
this.paidNotified = true;
|
|
1391
|
+
(_c = (_b = this.options).onPaid) == null ? void 0 : _c.call(_b, status);
|
|
1392
|
+
}
|
|
1393
|
+
return status;
|
|
1394
|
+
} catch {
|
|
1395
|
+
return null;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
/** Polls again after `stop()`, while a payment is open (the customer came back to crypto). */
|
|
1399
|
+
resume() {
|
|
1400
|
+
if (!this.state.payment || CRYPTO_ONCHAIN_FINAL_PHASES.includes(this.getPhase()))
|
|
1401
|
+
return;
|
|
1402
|
+
this.stopped = false;
|
|
1403
|
+
this.schedulePoll();
|
|
1404
|
+
}
|
|
1405
|
+
/** Stops polling (unmount, or the customer chose another payment method). */
|
|
1406
|
+
stop() {
|
|
1407
|
+
this.stopped = true;
|
|
1408
|
+
if (this.pollTimer)
|
|
1409
|
+
clearTimeout(this.pollTimer);
|
|
1410
|
+
this.pollTimer = null;
|
|
1411
|
+
}
|
|
1412
|
+
schedulePoll() {
|
|
1413
|
+
if (this.pollTimer)
|
|
1414
|
+
clearTimeout(this.pollTimer);
|
|
1415
|
+
this.pollTimer = setTimeout(async () => {
|
|
1416
|
+
this.pollTimer = null;
|
|
1417
|
+
await this.refreshStatus();
|
|
1418
|
+
if (!this.stopped && !CRYPTO_ONCHAIN_FINAL_PHASES.includes(this.getPhase()))
|
|
1419
|
+
this.schedulePoll();
|
|
1420
|
+
}, this.options.pollIntervalMs ?? CRYPTO_ONCHAIN_STATUS_POLL_MS);
|
|
1421
|
+
}
|
|
1422
|
+
setState(patch) {
|
|
1423
|
+
var _a, _b;
|
|
1424
|
+
this.state = { ...this.state, ...patch };
|
|
1425
|
+
(_b = (_a = this.options).onChange) == null ? void 0 : _b.call(_a, this.state);
|
|
1426
|
+
this.listeners.forEach((listener) => listener(this.state));
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
function messageOf(error) {
|
|
1430
|
+
return error instanceof Error && error.message ? error.message : UNAVAILABLE;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// src/core/helpers/oceanpayment.ts
|
|
1434
|
+
var OCEAN_SDK_URL = "https://secure.oceanpayment.com/pages/js/oceanpayment.js";
|
|
1435
|
+
var OCEAN_CONTAINER_ID = "oceanpayment-element";
|
|
1436
|
+
var OCEAN_CALLBACK_TIMEOUT_MS = 12e4;
|
|
1437
|
+
var OCEAN_LANGUAGE_OVERRIDES = { fi: "en" };
|
|
1438
|
+
function oceanLanguage(locale) {
|
|
1439
|
+
var _a;
|
|
1440
|
+
const primary = (_a = locale == null ? void 0 : locale.split(/[-_]/)[0]) == null ? void 0 : _a.toLowerCase();
|
|
1441
|
+
if (!primary)
|
|
1442
|
+
return "en";
|
|
1443
|
+
return OCEAN_LANGUAGE_OVERRIDES[primary] ?? primary;
|
|
1444
|
+
}
|
|
1445
|
+
var OCEAN_SDK_LOAD_TIMEOUT_MS = 3e4;
|
|
1446
|
+
var oceanSdkLoading = null;
|
|
1447
|
+
function loadOceanSdk() {
|
|
1448
|
+
const w = window;
|
|
1449
|
+
if (w.Oceanpayment)
|
|
1450
|
+
return Promise.resolve(w.Oceanpayment);
|
|
1451
|
+
if (oceanSdkLoading)
|
|
1452
|
+
return oceanSdkLoading;
|
|
1453
|
+
oceanSdkLoading = new Promise((resolve, reject) => {
|
|
1454
|
+
const script2 = document.createElement("script");
|
|
1455
|
+
const fail = (message) => {
|
|
1456
|
+
clearTimeout(timer);
|
|
1457
|
+
script2.remove();
|
|
1458
|
+
oceanSdkLoading = null;
|
|
1459
|
+
reject(new Error(message));
|
|
1460
|
+
};
|
|
1461
|
+
const timer = setTimeout(() => fail("The Ocean Payment card form took too long to load"), OCEAN_SDK_LOAD_TIMEOUT_MS);
|
|
1462
|
+
script2.addEventListener("load", () => {
|
|
1463
|
+
if (!w.Oceanpayment)
|
|
1464
|
+
return fail("Ocean Payment script loaded without window.Oceanpayment");
|
|
1465
|
+
clearTimeout(timer);
|
|
1466
|
+
resolve(w.Oceanpayment);
|
|
1467
|
+
});
|
|
1468
|
+
script2.addEventListener("error", () => fail("Failed to load the Ocean Payment card form"));
|
|
1469
|
+
script2.src = OCEAN_SDK_URL;
|
|
1470
|
+
document.head.appendChild(script2);
|
|
1471
|
+
});
|
|
1472
|
+
return oceanSdkLoading;
|
|
1473
|
+
}
|
|
1474
|
+
var OCEAN_PENDING_KEY = "tgd:headless:oceanpayment:pending";
|
|
1475
|
+
var OCEAN_PENDING_TTL_MS = 30 * 60 * 1e3;
|
|
1476
|
+
function parkOceanPayment(paymentId) {
|
|
1477
|
+
try {
|
|
1478
|
+
sessionStorage.setItem(OCEAN_PENDING_KEY, JSON.stringify({ paymentId, at: Date.now() }));
|
|
1479
|
+
} catch {
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
function takeParkedOceanPayment() {
|
|
1483
|
+
try {
|
|
1484
|
+
const raw = sessionStorage.getItem(OCEAN_PENDING_KEY);
|
|
1485
|
+
if (!raw)
|
|
1486
|
+
return void 0;
|
|
1487
|
+
sessionStorage.removeItem(OCEAN_PENDING_KEY);
|
|
1488
|
+
const { paymentId, at } = JSON.parse(raw);
|
|
1489
|
+
if (!paymentId || !at || Date.now() - at > OCEAN_PENDING_TTL_MS)
|
|
1490
|
+
return void 0;
|
|
1491
|
+
return paymentId;
|
|
1492
|
+
} catch {
|
|
1493
|
+
return void 0;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
function restoreOceanPaymentReturn() {
|
|
1497
|
+
if (typeof window === "undefined")
|
|
1498
|
+
return;
|
|
1499
|
+
const paymentId = takeParkedOceanPayment();
|
|
1500
|
+
if (!paymentId)
|
|
1501
|
+
return;
|
|
1502
|
+
try {
|
|
1503
|
+
const url2 = new URL(window.location.href);
|
|
1504
|
+
if (url2.searchParams.get("paymentId"))
|
|
1505
|
+
return;
|
|
1506
|
+
url2.searchParams.set("paymentId", paymentId);
|
|
1507
|
+
url2.searchParams.set("paymentAction", "requireAction");
|
|
1508
|
+
url2.searchParams.set("paymentActionStatus", "completed");
|
|
1509
|
+
window.history.replaceState(window.history.state, "", url2.toString());
|
|
1510
|
+
} catch {
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1191
1514
|
// src/core/modules/payment.ts
|
|
1515
|
+
var FAILED_PAYMENT_STATUSES = /* @__PURE__ */ new Set(["failed", "declined", "rejected", "error"]);
|
|
1516
|
+
var oceanSlot = null;
|
|
1517
|
+
var oceanSlotTaken = () => !!oceanSlot && (!oceanSlot.attached || oceanSlot.element.isConnected);
|
|
1518
|
+
var OCEAN_TIMED_OUT = Symbol("ocean-timed-out");
|
|
1519
|
+
var OCEAN_UNMOUNTED = Symbol("ocean-unmounted");
|
|
1192
1520
|
var EXPRESS_METHOD_TYPES = /* @__PURE__ */ new Set(["apple_pay", "google_pay", "paypal", "klarna", "klarna_express", "link"]);
|
|
1193
1521
|
function paymentMethodListEntryToConfig(entry) {
|
|
1194
1522
|
var _a;
|
|
@@ -1211,6 +1539,28 @@ var TagadaHeadlessBundle = (() => {
|
|
|
1211
1539
|
metadata: entry.metadata ?? void 0
|
|
1212
1540
|
};
|
|
1213
1541
|
}
|
|
1542
|
+
function loadStripeJs() {
|
|
1543
|
+
if (typeof window === "undefined")
|
|
1544
|
+
return Promise.reject(new Error("Stripe Radar needs a browser"));
|
|
1545
|
+
const fromPage = () => window.Stripe;
|
|
1546
|
+
const loaded = fromPage();
|
|
1547
|
+
if (loaded)
|
|
1548
|
+
return Promise.resolve(loaded);
|
|
1549
|
+
return new Promise((resolve, reject) => {
|
|
1550
|
+
const script2 = document.createElement("script");
|
|
1551
|
+
script2.src = "https://js.stripe.com/v3/";
|
|
1552
|
+
script2.async = true;
|
|
1553
|
+
script2.onload = () => {
|
|
1554
|
+
const stripe = fromPage();
|
|
1555
|
+
if (stripe)
|
|
1556
|
+
resolve(stripe);
|
|
1557
|
+
else
|
|
1558
|
+
reject(new Error("Stripe.js loaded without window.Stripe"));
|
|
1559
|
+
};
|
|
1560
|
+
script2.onerror = () => reject(new Error("Failed to load Stripe.js"));
|
|
1561
|
+
document.head.appendChild(script2);
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1214
1564
|
var PaymentModule = class {
|
|
1215
1565
|
constructor(http, storeId, environment, checkout, onPaid, onSignal) {
|
|
1216
1566
|
this.http = http;
|
|
@@ -1679,6 +2029,228 @@ var TagadaHeadlessBundle = (() => {
|
|
|
1679
2029
|
});
|
|
1680
2030
|
}
|
|
1681
2031
|
// ---------------------------------------------------------------------------
|
|
2032
|
+
// On-chain crypto (`crypto_onchain`)
|
|
2033
|
+
// ---------------------------------------------------------------------------
|
|
2034
|
+
/**
|
|
2035
|
+
* Networks and coins the store accepts for on-chain crypto on this checkout session. On-chain
|
|
2036
|
+
* crypto is paid from your page (address, exact amount, QR) and never through `pay()` /
|
|
2037
|
+
* `processApm()`: pay-v2 refuses it with `422 CRYPTO_ONCHAIN_INLINE_ONLY`.
|
|
2038
|
+
*/
|
|
2039
|
+
async getCryptoOnchainOptions(checkoutSessionId) {
|
|
2040
|
+
return this.http.get(
|
|
2041
|
+
`/api/v1/checkout-sessions/${encodeURIComponent(checkoutSessionId)}/crypto-onchain/options`
|
|
2042
|
+
);
|
|
2043
|
+
}
|
|
2044
|
+
/**
|
|
2045
|
+
* Create the order's on-chain payment for a network and a coin, or re-quote it (another coin,
|
|
2046
|
+
* an expired quote): the deposit address, exact amount and wallet URI for the QR code. Save the
|
|
2047
|
+
* customer and address on the session first.
|
|
2048
|
+
*/
|
|
2049
|
+
async createCryptoOnchainPayment(opts) {
|
|
2050
|
+
return this.http.post(
|
|
2051
|
+
`/api/v1/checkout-sessions/${encodeURIComponent(opts.checkoutSessionId)}/crypto-onchain/session`,
|
|
2052
|
+
{ chainId: opts.chainId, asset: opts.asset }
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
/** The payment's state; poll it every few seconds until `paid` (or underpaid / expired / failed). */
|
|
2056
|
+
async getCryptoOnchainStatus(opts) {
|
|
2057
|
+
return this.http.get(
|
|
2058
|
+
`/api/v1/checkout-sessions/${encodeURIComponent(opts.checkoutSessionId)}/crypto-onchain/status`,
|
|
2059
|
+
{ params: { cryptoOnchainPaymentId: opts.cryptoOnchainPaymentId } }
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* The whole inline crypto payment of a checkout session, the way the hosted checkout runs it.
|
|
2064
|
+
*
|
|
2065
|
+
* @example
|
|
2066
|
+
* ```ts
|
|
2067
|
+
* const crypto = tagada.payment.createCryptoOnchainCheckout({
|
|
2068
|
+
* checkoutSessionId: session.id,
|
|
2069
|
+
* onPaid: ({ orderId }) => showThankYou(orderId),
|
|
2070
|
+
* });
|
|
2071
|
+
* crypto.subscribe(render); // networks, selection, payment (address, amount, paymentUri), status
|
|
2072
|
+
* await crypto.loadOptions();
|
|
2073
|
+
* payButton.onclick = () => crypto.pay(); // after saving the customer and address
|
|
2074
|
+
* ```
|
|
2075
|
+
*/
|
|
2076
|
+
createCryptoOnchainCheckout(opts) {
|
|
2077
|
+
return new CryptoOnchainCheckout(this, opts);
|
|
2078
|
+
}
|
|
2079
|
+
// ---------------------------------------------------------------------------
|
|
2080
|
+
// Ocean Payment
|
|
2081
|
+
// ---------------------------------------------------------------------------
|
|
2082
|
+
/** Whether the store's Ocean Payment integration renders its inline card form or redirects. */
|
|
2083
|
+
async getOceanPaymentConfig(checkoutSessionId) {
|
|
2084
|
+
const data = await this.http.post(
|
|
2085
|
+
`/api/v1/checkout-sessions/${checkoutSessionId}/oceanpayment-embedded-config`,
|
|
2086
|
+
{}
|
|
2087
|
+
);
|
|
2088
|
+
return { embedded: !!(data == null ? void 0 : data.embedded), sandboxed: !!(data == null ? void 0 : data.sandboxed) };
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* Set up Ocean Payment for a checkout session, the way the hosted checkout
|
|
2092
|
+
* does: Ocean's card fields render inside `container` when the integration
|
|
2093
|
+
* has Ocean's public key (`embedded`), otherwise `pay()` sends the shopper
|
|
2094
|
+
* to Ocean's page and back to `returnUrl` (`redirect`).
|
|
2095
|
+
*
|
|
2096
|
+
* @example
|
|
2097
|
+
* ```ts
|
|
2098
|
+
* const ocean = await tagada.payment.mountOceanPayment({
|
|
2099
|
+
* checkoutSessionId: session.id,
|
|
2100
|
+
* container: '#ocean-card',
|
|
2101
|
+
* });
|
|
2102
|
+
* payButton.onclick = async () => {
|
|
2103
|
+
* const result = await ocean.pay();
|
|
2104
|
+
* if (result.status === 'requires_redirect') window.location.href = result.redirectUrl;
|
|
2105
|
+
* else if (result.status === 'succeeded') showThankYou(result.order);
|
|
2106
|
+
* else if (result.status === 'failed') showError(result.error);
|
|
2107
|
+
* };
|
|
2108
|
+
* ```
|
|
2109
|
+
*/
|
|
2110
|
+
async mountOceanPayment(opts) {
|
|
2111
|
+
if (typeof window === "undefined")
|
|
2112
|
+
throw new Error("Ocean Payment needs a browser");
|
|
2113
|
+
const { checkoutSessionId, signal } = opts;
|
|
2114
|
+
const aborted = () => Object.assign(new Error("Ocean Payment mount aborted"), { name: "AbortError" });
|
|
2115
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
2116
|
+
throw aborted();
|
|
2117
|
+
const host = typeof opts.container === "string" ? document.querySelector(opts.container) : opts.container;
|
|
2118
|
+
if (!host && opts.mode !== "redirect") {
|
|
2119
|
+
throw new Error(`Ocean Payment container not found: ${String(opts.container)}`);
|
|
2120
|
+
}
|
|
2121
|
+
if (oceanSlotTaken() || document.getElementById(OCEAN_CONTAINER_ID)) {
|
|
2122
|
+
throw new Error("An Ocean Payment card form is already mounted on this page");
|
|
2123
|
+
}
|
|
2124
|
+
const w = window;
|
|
2125
|
+
const element = document.createElement("div");
|
|
2126
|
+
element.id = OCEAN_CONTAINER_ID;
|
|
2127
|
+
const slot = { element, attached: false };
|
|
2128
|
+
oceanSlot = slot;
|
|
2129
|
+
const releaseSlot = () => {
|
|
2130
|
+
if (oceanSlot === slot)
|
|
2131
|
+
oceanSlot = null;
|
|
2132
|
+
};
|
|
2133
|
+
let mounted = true;
|
|
2134
|
+
let pendingCallback = null;
|
|
2135
|
+
const callback = (data) => {
|
|
2136
|
+
const resolve = pendingCallback;
|
|
2137
|
+
pendingCallback = null;
|
|
2138
|
+
resolve == null ? void 0 : resolve(data);
|
|
2139
|
+
};
|
|
2140
|
+
const unmount = () => {
|
|
2141
|
+
if (!mounted)
|
|
2142
|
+
return;
|
|
2143
|
+
mounted = false;
|
|
2144
|
+
if (w.oceanpaymentCallBack === callback)
|
|
2145
|
+
delete w.oceanpaymentCallBack;
|
|
2146
|
+
element.remove();
|
|
2147
|
+
releaseSlot();
|
|
2148
|
+
callback(OCEAN_UNMOUNTED);
|
|
2149
|
+
};
|
|
2150
|
+
signal == null ? void 0 : signal.addEventListener("abort", unmount, { once: true });
|
|
2151
|
+
let config;
|
|
2152
|
+
let ocean;
|
|
2153
|
+
try {
|
|
2154
|
+
config = opts.mode === "redirect" ? { embedded: false, sandboxed: false } : await this.getOceanPaymentConfig(checkoutSessionId);
|
|
2155
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
2156
|
+
throw aborted();
|
|
2157
|
+
if (config.embedded) {
|
|
2158
|
+
ocean = await loadOceanSdk();
|
|
2159
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
2160
|
+
throw aborted();
|
|
2161
|
+
}
|
|
2162
|
+
} catch (error) {
|
|
2163
|
+
unmount();
|
|
2164
|
+
throw error;
|
|
2165
|
+
}
|
|
2166
|
+
let submitting = false;
|
|
2167
|
+
if (!ocean) {
|
|
2168
|
+
releaseSlot();
|
|
2169
|
+
return {
|
|
2170
|
+
mode: "redirect",
|
|
2171
|
+
pay: async () => {
|
|
2172
|
+
if (!mounted)
|
|
2173
|
+
return { status: "failed", error: "The Ocean Payment form was removed" };
|
|
2174
|
+
if (submitting)
|
|
2175
|
+
return { status: "failed", error: "Payment already in progress" };
|
|
2176
|
+
submitting = true;
|
|
2177
|
+
try {
|
|
2178
|
+
const data = await this.http.post(
|
|
2179
|
+
`/api/v1/checkout-sessions/${checkoutSessionId}/oceanpayment-link`,
|
|
2180
|
+
{ returnUrl: opts.returnUrl ?? window.location.href }
|
|
2181
|
+
);
|
|
2182
|
+
if (!(data == null ? void 0 : data.url) || !data.paymentId) {
|
|
2183
|
+
return { status: "failed", error: "Ocean Payment is not available for this store" };
|
|
2184
|
+
}
|
|
2185
|
+
return { status: "requires_redirect", redirectUrl: data.url, method: "GET", paymentId: data.paymentId };
|
|
2186
|
+
} catch (error) {
|
|
2187
|
+
return { status: "failed", error: error instanceof Error ? error.message : String(error) };
|
|
2188
|
+
} finally {
|
|
2189
|
+
submitting = false;
|
|
2190
|
+
}
|
|
2191
|
+
},
|
|
2192
|
+
unmount
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
const sdk = ocean;
|
|
2196
|
+
host.appendChild(element);
|
|
2197
|
+
slot.attached = true;
|
|
2198
|
+
w.oceanpaymentCallBack = callback;
|
|
2199
|
+
const backUrl = window.location.href;
|
|
2200
|
+
sdk.init(config.sandboxed, "", oceanLanguage(opts.locale), { showCardName: false });
|
|
2201
|
+
const pay = async () => {
|
|
2202
|
+
if (!mounted)
|
|
2203
|
+
return { status: "failed", error: "The Ocean Payment card form was removed" };
|
|
2204
|
+
if (submitting)
|
|
2205
|
+
return { status: "failed", error: "Payment already in progress" };
|
|
2206
|
+
submitting = true;
|
|
2207
|
+
try {
|
|
2208
|
+
const created = await this.http.post(
|
|
2209
|
+
`/api/v1/checkout-sessions/${checkoutSessionId}/oceanpayment-embedded-payment`,
|
|
2210
|
+
{ backUrl }
|
|
2211
|
+
);
|
|
2212
|
+
if (!(created == null ? void 0 : created.paymentId) || !created.params) {
|
|
2213
|
+
return { status: "failed", error: "Failed to start the payment" };
|
|
2214
|
+
}
|
|
2215
|
+
const paymentId = created.paymentId;
|
|
2216
|
+
const answer = await new Promise((resolve) => {
|
|
2217
|
+
const timer = setTimeout(() => {
|
|
2218
|
+
pendingCallback = null;
|
|
2219
|
+
resolve(OCEAN_TIMED_OUT);
|
|
2220
|
+
}, OCEAN_CALLBACK_TIMEOUT_MS);
|
|
2221
|
+
pendingCallback = (data) => {
|
|
2222
|
+
clearTimeout(timer);
|
|
2223
|
+
resolve(data);
|
|
2224
|
+
};
|
|
2225
|
+
sdk.checkout(created.params);
|
|
2226
|
+
});
|
|
2227
|
+
if (answer === OCEAN_TIMED_OUT || answer === OCEAN_UNMOUNTED) {
|
|
2228
|
+
return this.pollUntilDone(paymentId);
|
|
2229
|
+
}
|
|
2230
|
+
if (answer && typeof answer === "object" && "msg" in answer) {
|
|
2231
|
+
return { status: "failed", error: String(answer.msg || "Please check your card details.") };
|
|
2232
|
+
}
|
|
2233
|
+
const result = await this.http.post(
|
|
2234
|
+
`/api/v1/checkout-sessions/${checkoutSessionId}/oceanpayment-embedded-result`,
|
|
2235
|
+
{ paymentId, response: String(answer) }
|
|
2236
|
+
);
|
|
2237
|
+
if ((result == null ? void 0 : result.status) === "requires_action" && result.payUrl) {
|
|
2238
|
+
parkOceanPayment(paymentId);
|
|
2239
|
+
return { status: "requires_redirect", redirectUrl: result.payUrl, method: "GET", paymentId };
|
|
2240
|
+
}
|
|
2241
|
+
if ((result == null ? void 0 : result.status) === "succeeded" || (result == null ? void 0 : result.status) === "pending") {
|
|
2242
|
+
return this.pollUntilDone(paymentId);
|
|
2243
|
+
}
|
|
2244
|
+
return { status: "failed", error: (result == null ? void 0 : result.message) || "Payment declined" };
|
|
2245
|
+
} catch (error) {
|
|
2246
|
+
return { status: "failed", error: error instanceof Error ? error.message : String(error) };
|
|
2247
|
+
} finally {
|
|
2248
|
+
submitting = false;
|
|
2249
|
+
}
|
|
2250
|
+
};
|
|
2251
|
+
return { mode: "embedded", pay, unmount };
|
|
2252
|
+
}
|
|
2253
|
+
// ---------------------------------------------------------------------------
|
|
1682
2254
|
// High-level orchestrator — the ONE function beginners should call
|
|
1683
2255
|
// ---------------------------------------------------------------------------
|
|
1684
2256
|
/**
|
|
@@ -1790,6 +2362,8 @@ var TagadaHeadlessBundle = (() => {
|
|
|
1790
2362
|
async maybeResumeFromUrl(opts) {
|
|
1791
2363
|
if (typeof window === "undefined" && !(opts == null ? void 0 : opts.url))
|
|
1792
2364
|
return null;
|
|
2365
|
+
if (!(opts == null ? void 0 : opts.url))
|
|
2366
|
+
restoreOceanPaymentReturn();
|
|
1793
2367
|
const href = (opts == null ? void 0 : opts.url) ?? window.location.href;
|
|
1794
2368
|
let url2;
|
|
1795
2369
|
try {
|
|
@@ -1845,7 +2419,7 @@ var TagadaHeadlessBundle = (() => {
|
|
|
1845
2419
|
const start = Date.now();
|
|
1846
2420
|
while (Date.now() - start < timeout) {
|
|
1847
2421
|
const status = await this.getPaymentStatus(paymentId);
|
|
1848
|
-
if (status.status === "succeeded" ||
|
|
2422
|
+
if (status.status === "succeeded" || FAILED_PAYMENT_STATUSES.has(status.status)) {
|
|
1849
2423
|
return status;
|
|
1850
2424
|
}
|
|
1851
2425
|
await new Promise((r) => setTimeout(r, interval));
|
|
@@ -1887,6 +2461,8 @@ var TagadaHeadlessBundle = (() => {
|
|
|
1887
2461
|
return this.extractRedirect(payment, actionData);
|
|
1888
2462
|
case "redirect":
|
|
1889
2463
|
return this.extractRedirect(payment, actionData);
|
|
2464
|
+
case "stripe_radar":
|
|
2465
|
+
return this.handleStripeRadar(payment);
|
|
1890
2466
|
case "error":
|
|
1891
2467
|
return {
|
|
1892
2468
|
status: "failed",
|
|
@@ -1969,6 +2545,34 @@ var TagadaHeadlessBundle = (() => {
|
|
|
1969
2545
|
};
|
|
1970
2546
|
}
|
|
1971
2547
|
}
|
|
2548
|
+
/**
|
|
2549
|
+
* Stripe Radar: the processor holds the charge until the browser sends a Radar
|
|
2550
|
+
* session. Create it with Stripe.js, save it, resume the payment, then poll.
|
|
2551
|
+
*/
|
|
2552
|
+
async handleStripeRadar(payment) {
|
|
2553
|
+
var _a, _b;
|
|
2554
|
+
const radar = (_b = (_a = payment.requireActionData) == null ? void 0 : _a.metadata) == null ? void 0 : _b.radar;
|
|
2555
|
+
if (!(radar == null ? void 0 : radar.publishableKey)) {
|
|
2556
|
+
return { status: "failed", error: "Stripe Radar configuration missing", payment };
|
|
2557
|
+
}
|
|
2558
|
+
try {
|
|
2559
|
+
const Stripe = await loadStripeJs();
|
|
2560
|
+
const { radarSession, error } = await Stripe(radar.publishableKey).createRadarSession();
|
|
2561
|
+
if (error || !radarSession) {
|
|
2562
|
+
throw new Error((error == null ? void 0 : error.message) || "No Radar session returned by Stripe");
|
|
2563
|
+
}
|
|
2564
|
+
await this.http.post("/api/v1/radar-sessions", {
|
|
2565
|
+
orderId: radar.orderId,
|
|
2566
|
+
paymentId: payment.id,
|
|
2567
|
+
stripeRadarSessionId: radarSession.id,
|
|
2568
|
+
stripeRadarSessionData: radarSession
|
|
2569
|
+
});
|
|
2570
|
+
await this.completeAfter3ds(payment.id);
|
|
2571
|
+
return this.pollUntilDone(payment.id);
|
|
2572
|
+
} catch (e) {
|
|
2573
|
+
return { status: "failed", error: e instanceof Error ? e.message : "Stripe Radar failed", payment };
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
1972
2576
|
/**
|
|
1973
2577
|
* Poll until the payment reaches a terminal status, then return a ProcessPaymentResult.
|
|
1974
2578
|
* Also handles new requireAction that may appear during polling.
|
|
@@ -1998,7 +2602,7 @@ var TagadaHeadlessBundle = (() => {
|
|
|
1998
2602
|
}
|
|
1999
2603
|
return { status: "succeeded", payment: status, order: status.order };
|
|
2000
2604
|
}
|
|
2001
|
-
if (
|
|
2605
|
+
if (FAILED_PAYMENT_STATUSES.has(status.status)) {
|
|
2002
2606
|
return { status: "failed", error: status.status, payment: status };
|
|
2003
2607
|
}
|
|
2004
2608
|
if (status.requireAction && status.requireAction !== "none" && status.requireActionData && !status.requireActionData.processed) {
|
|
@@ -2063,7 +2667,7 @@ var TagadaHeadlessBundle = (() => {
|
|
|
2063
2667
|
|
|
2064
2668
|
// src/core/modules/lazyCommerce.ts
|
|
2065
2669
|
var script = typeof document === "undefined" ? null : document.currentScript;
|
|
2066
|
-
var url = (script == null ? void 0 : script.src) ? new URL("tagada-headless-commerce.min.js", script.src).href : "https://cdn.jsdelivr.net/npm/@tagadapay/headless-sdk@1.
|
|
2670
|
+
var url = (script == null ? void 0 : script.src) ? new URL("tagada-headless-commerce.min.js", script.src).href : "https://cdn.jsdelivr.net/npm/@tagadapay/headless-sdk@1.20.1/dist/tagada-headless-commerce.min.js";
|
|
2067
2671
|
var loading;
|
|
2068
2672
|
function loadCommerce() {
|
|
2069
2673
|
if (window.TagadaHeadlessCommerce)
|
|
@@ -2422,6 +3026,19 @@ var TagadaHeadlessBundle = (() => {
|
|
|
2422
3026
|
input
|
|
2423
3027
|
);
|
|
2424
3028
|
}
|
|
3029
|
+
/**
|
|
3030
|
+
* Resume an unfinished intake from `(orderId, checkoutToken)` alone: the
|
|
3031
|
+
* patient paid (card on hold) but the identity collected at checkout is
|
|
3032
|
+
* gone (tab closed, recovery email link, other device). Returns the Rx
|
|
3033
|
+
* line (`productId`, `variantId`) and the basics on the order; ask date of
|
|
3034
|
+
* birth and sex at birth again, render the questionnaire, then
|
|
3035
|
+
* `submitCase`. `{ caseExists: true }` means a case is already in: show
|
|
3036
|
+
* `getCasesForOrder`. Fails with 401 on a wrong token, 404 when the order
|
|
3037
|
+
* has no Rx line.
|
|
3038
|
+
*/
|
|
3039
|
+
async getIntakeContext(input) {
|
|
3040
|
+
return this.http.post("/api/public/v1/rx/intake/context", input);
|
|
3041
|
+
}
|
|
2425
3042
|
/**
|
|
2426
3043
|
* Submit a fresh clinical case for a freshly-paid order. PHI is
|
|
2427
3044
|
* forwarded to the clinical network and never persisted on
|