@ozura/elements 1.0.2-next.16 → 1.0.2-next.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -0
- package/dist/oz-elements.esm.js +138 -11
- package/dist/oz-elements.esm.js.map +1 -1
- package/dist/oz-elements.umd.js +138 -11
- package/dist/oz-elements.umd.js.map +1 -1
- package/dist/react/index.cjs.js +141 -14
- package/dist/react/index.cjs.js.map +1 -1
- package/dist/react/index.esm.js +141 -14
- package/dist/react/index.esm.js.map +1 -1
- package/dist/react/react/index.d.ts +12 -1
- package/dist/react/sdk/OzVault.d.ts +26 -0
- package/dist/react/types/index.d.ts +18 -0
- package/dist/server/sdk/OzVault.d.ts +26 -0
- package/dist/server/types/index.d.ts +18 -0
- package/dist/types/sdk/OzVault.d.ts +26 -0
- package/dist/types/types/index.d.ts +18 -0
- package/package.json +1 -1
package/dist/react/index.cjs.js
CHANGED
|
@@ -879,7 +879,7 @@ class OzVault {
|
|
|
879
879
|
* @internal
|
|
880
880
|
*/
|
|
881
881
|
constructor(options, waxKey, tokenizationSessionId) {
|
|
882
|
-
var _a, _b, _c;
|
|
882
|
+
var _a, _b, _c, _d;
|
|
883
883
|
this.elements = new Map();
|
|
884
884
|
this.elementsByType = new Map();
|
|
885
885
|
this.bankElementsByType = new Map();
|
|
@@ -892,6 +892,9 @@ class OzVault {
|
|
|
892
892
|
this.tokenizerReady = false;
|
|
893
893
|
this._tokenizing = null;
|
|
894
894
|
this._destroyed = false;
|
|
895
|
+
// Incremented every time reset() cancels an active tokenization so that
|
|
896
|
+
// any in-flight wax-key refresh retry can detect it was superseded.
|
|
897
|
+
this._resetCount = 0;
|
|
895
898
|
// Tracks successful tokenizations against the per-key call budget so the SDK
|
|
896
899
|
// can proactively refresh the wax key after it has been consumed rather than
|
|
897
900
|
// waiting for the next createToken() call to fail.
|
|
@@ -911,13 +914,14 @@ class OzVault {
|
|
|
911
914
|
this.resolvedAppearance = resolveAppearance(options.appearance);
|
|
912
915
|
this.vaultId = `vault-${uuid()}`;
|
|
913
916
|
this._maxTokenizeCalls = (_b = options.maxTokenizeCalls) !== null && _b !== void 0 ? _b : 3;
|
|
917
|
+
this._debug = (_c = options.debug) !== null && _c !== void 0 ? _c : false;
|
|
914
918
|
this.boundHandleMessage = this.handleMessage.bind(this);
|
|
915
919
|
window.addEventListener('message', this.boundHandleMessage);
|
|
916
920
|
this.boundHandleVisibility = this.handleVisibilityChange.bind(this);
|
|
917
921
|
document.addEventListener('visibilitychange', this.boundHandleVisibility);
|
|
918
922
|
this.mountTokenizerFrame();
|
|
919
923
|
if (options.onLoadError) {
|
|
920
|
-
const timeout = (
|
|
924
|
+
const timeout = (_d = options.loadTimeoutMs) !== null && _d !== void 0 ? _d : 10000;
|
|
921
925
|
this.loadErrorTimeoutId = setTimeout(() => {
|
|
922
926
|
this.loadErrorTimeoutId = null;
|
|
923
927
|
if (!this._destroyed && !this.tokenizerReady) {
|
|
@@ -927,6 +931,7 @@ class OzVault {
|
|
|
927
931
|
}
|
|
928
932
|
this._onWaxRefresh = options.onWaxRefresh;
|
|
929
933
|
this._onReady = options.onReady;
|
|
934
|
+
this.log('vault created', { vaultId: this.vaultId, frameBaseUrl: this.frameBaseUrl, maxTokenizeCalls: this._maxTokenizeCalls });
|
|
930
935
|
}
|
|
931
936
|
/**
|
|
932
937
|
* Creates and returns a ready `OzVault` instance.
|
|
@@ -996,6 +1001,7 @@ class OzVault {
|
|
|
996
1001
|
if (vault.tokenizerReady) {
|
|
997
1002
|
vault.sendToTokenizer({ type: 'OZ_INIT', frameId: '__tokenizer__', waxKey });
|
|
998
1003
|
}
|
|
1004
|
+
vault.log('wax key received — vault ready');
|
|
999
1005
|
return vault;
|
|
1000
1006
|
}
|
|
1001
1007
|
/**
|
|
@@ -1137,8 +1143,13 @@ class OzVault {
|
|
|
1137
1143
|
const readyBankElements = [accountEl, routingEl];
|
|
1138
1144
|
this._tokenizing = 'bank';
|
|
1139
1145
|
const requestId = `req-${uuid()}`;
|
|
1146
|
+
this.log('createBankToken() called');
|
|
1140
1147
|
return new Promise((resolve, reject) => {
|
|
1141
|
-
const
|
|
1148
|
+
const resetCountAtStart = this._resetCount;
|
|
1149
|
+
const cleanup = () => {
|
|
1150
|
+
if (this._resetCount === resetCountAtStart)
|
|
1151
|
+
this._tokenizing = null;
|
|
1152
|
+
};
|
|
1142
1153
|
this.bankTokenizeResolvers.set(requestId, {
|
|
1143
1154
|
resolve: (v) => { cleanup(); resolve(v); },
|
|
1144
1155
|
reject: (e) => { cleanup(); reject(e); },
|
|
@@ -1149,6 +1160,7 @@ class OzVault {
|
|
|
1149
1160
|
});
|
|
1150
1161
|
try {
|
|
1151
1162
|
const bankChannels = readyBankElements.map(() => new MessageChannel());
|
|
1163
|
+
const bankTokenizeStartMs = Date.now();
|
|
1152
1164
|
this.sendToTokenizer({
|
|
1153
1165
|
type: 'OZ_BANK_TOKENIZE',
|
|
1154
1166
|
requestId,
|
|
@@ -1159,6 +1171,7 @@ class OzVault {
|
|
|
1159
1171
|
lastName: options.lastName.trim(),
|
|
1160
1172
|
fieldCount: readyBankElements.length,
|
|
1161
1173
|
}, bankChannels.map(ch => ch.port1));
|
|
1174
|
+
this.log('OZ_BANK_TOKENIZE sent', { requestIdPrefix: `${requestId.slice(0, 12)}...`, fieldCount: readyBankElements.length });
|
|
1162
1175
|
readyBankElements.forEach((el, i) => el.beginCollect(requestId, bankChannels[i].port2));
|
|
1163
1176
|
const bankTimeoutId = setTimeout(() => {
|
|
1164
1177
|
if (this.bankTokenizeResolvers.has(requestId)) {
|
|
@@ -1169,8 +1182,10 @@ class OzVault {
|
|
|
1169
1182
|
}
|
|
1170
1183
|
}, 30000);
|
|
1171
1184
|
const bankPendingEntry = this.bankTokenizeResolvers.get(requestId);
|
|
1172
|
-
if (bankPendingEntry)
|
|
1185
|
+
if (bankPendingEntry) {
|
|
1173
1186
|
bankPendingEntry.timeoutId = bankTimeoutId;
|
|
1187
|
+
bankPendingEntry.tokenizeStartMs = bankTokenizeStartMs;
|
|
1188
|
+
}
|
|
1174
1189
|
}
|
|
1175
1190
|
catch (err) {
|
|
1176
1191
|
this.bankTokenizeResolvers.delete(requestId);
|
|
@@ -1241,8 +1256,15 @@ class OzVault {
|
|
|
1241
1256
|
}
|
|
1242
1257
|
this._tokenizing = 'card';
|
|
1243
1258
|
const requestId = `req-${uuid()}`;
|
|
1259
|
+
this.log('createToken() called');
|
|
1244
1260
|
return new Promise((resolve, reject) => {
|
|
1245
|
-
|
|
1261
|
+
// Capture the reset generation so cleanup() only zeros _tokenizing when it
|
|
1262
|
+
// still belongs to this invocation — not a newer one that started after a reset.
|
|
1263
|
+
const resetCountAtStart = this._resetCount;
|
|
1264
|
+
const cleanup = () => {
|
|
1265
|
+
if (this._resetCount === resetCountAtStart)
|
|
1266
|
+
this._tokenizing = null;
|
|
1267
|
+
};
|
|
1246
1268
|
this.tokenizeResolvers.set(requestId, {
|
|
1247
1269
|
resolve: (v) => { cleanup(); resolve(v); },
|
|
1248
1270
|
reject: (e) => { cleanup(); reject(e); },
|
|
@@ -1255,6 +1277,7 @@ class OzVault {
|
|
|
1255
1277
|
try {
|
|
1256
1278
|
// Tell tokenizer frame to expect N field values, then tokenize
|
|
1257
1279
|
const cardChannels = readyElements.map(() => new MessageChannel());
|
|
1280
|
+
const tokenizeStartMs = Date.now();
|
|
1258
1281
|
this.sendToTokenizer({
|
|
1259
1282
|
type: 'OZ_TOKENIZE',
|
|
1260
1283
|
requestId,
|
|
@@ -1265,6 +1288,11 @@ class OzVault {
|
|
|
1265
1288
|
lastName,
|
|
1266
1289
|
fieldCount: readyElements.length,
|
|
1267
1290
|
}, cardChannels.map(ch => ch.port1));
|
|
1291
|
+
this.log('OZ_TOKENIZE sent', { requestIdPrefix: `${requestId.slice(0, 12)}...`, fieldCount: readyElements.length });
|
|
1292
|
+
// Store start time for elapsed-ms logging on result
|
|
1293
|
+
const cardEntry = this.tokenizeResolvers.get(requestId);
|
|
1294
|
+
if (cardEntry)
|
|
1295
|
+
cardEntry.tokenizeStartMs = tokenizeStartMs;
|
|
1268
1296
|
// Tell each ready element frame to send its raw value to the tokenizer
|
|
1269
1297
|
readyElements.forEach((el, i) => el.beginCollect(requestId, cardChannels[i].port2));
|
|
1270
1298
|
const cardTimeoutId = setTimeout(() => {
|
|
@@ -1311,8 +1339,11 @@ class OzVault {
|
|
|
1311
1339
|
reset() {
|
|
1312
1340
|
if (this._destroyed)
|
|
1313
1341
|
return;
|
|
1342
|
+
const cancelling = Boolean(this._tokenizing);
|
|
1343
|
+
this.log('reset() called', { tokenizing: this._tokenizing, cancelling });
|
|
1314
1344
|
if (this._tokenizing) {
|
|
1315
1345
|
this._tokenizing = null;
|
|
1346
|
+
this._resetCount++;
|
|
1316
1347
|
this.tokenizeResolvers.forEach(({ reject, timeoutId }, requestId) => {
|
|
1317
1348
|
if (timeoutId != null)
|
|
1318
1349
|
clearTimeout(timeoutId);
|
|
@@ -1350,6 +1381,7 @@ class OzVault {
|
|
|
1350
1381
|
if (this._destroyed)
|
|
1351
1382
|
return;
|
|
1352
1383
|
this._destroyed = true;
|
|
1384
|
+
this.log('destroy() called');
|
|
1353
1385
|
window.removeEventListener('message', this.boundHandleMessage);
|
|
1354
1386
|
document.removeEventListener('visibilitychange', this.boundHandleVisibility);
|
|
1355
1387
|
if (this._pendingMount) {
|
|
@@ -1404,13 +1436,17 @@ class OzVault {
|
|
|
1404
1436
|
const REFRESH_THRESHOLD_MS = 20 * 60 * 1000; // 20 minutes
|
|
1405
1437
|
if (document.hidden) {
|
|
1406
1438
|
this._hiddenAt = Date.now();
|
|
1439
|
+
this.log('tab hidden');
|
|
1407
1440
|
}
|
|
1408
1441
|
else {
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1442
|
+
const hiddenMs = this._hiddenAt !== null ? Date.now() - this._hiddenAt : 0;
|
|
1443
|
+
const willRefresh = (this._hiddenAt !== null &&
|
|
1444
|
+
hiddenMs >= REFRESH_THRESHOLD_MS &&
|
|
1445
|
+
Boolean(this._storedFetchWaxKey) &&
|
|
1412
1446
|
!this._tokenizing &&
|
|
1413
|
-
!this._waxRefreshing)
|
|
1447
|
+
!this._waxRefreshing);
|
|
1448
|
+
this.log('tab visible', { hiddenMs, willRefresh });
|
|
1449
|
+
if (willRefresh) {
|
|
1414
1450
|
this.refreshWaxKey().catch((err) => {
|
|
1415
1451
|
// Proactive refresh failure is non-fatal — the reactive path on the
|
|
1416
1452
|
// next createToken() call will handle it, including the auth retry.
|
|
@@ -1420,6 +1456,56 @@ class OzVault {
|
|
|
1420
1456
|
this._hiddenAt = null;
|
|
1421
1457
|
}
|
|
1422
1458
|
}
|
|
1459
|
+
// ─── Debug ───────────────────────────────────────────────────────────────
|
|
1460
|
+
/**
|
|
1461
|
+
* Emits a `[OzVault]`-prefixed entry to `console.log`. No-op when `debug` is
|
|
1462
|
+
* not set. Never called with sensitive values — callers use presence flags only.
|
|
1463
|
+
*/
|
|
1464
|
+
log(message, data) {
|
|
1465
|
+
if (!this._debug)
|
|
1466
|
+
return;
|
|
1467
|
+
if (data !== undefined) {
|
|
1468
|
+
console.log(`[OzVault] ${message}`, data);
|
|
1469
|
+
}
|
|
1470
|
+
else {
|
|
1471
|
+
console.log(`[OzVault] ${message}`);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Returns a plain-object snapshot of the vault's current internal state.
|
|
1476
|
+
* Safe to attach to bug reports — no wax keys, tokens, or billing data included.
|
|
1477
|
+
*
|
|
1478
|
+
* Available on all vault instances regardless of whether `debug` was enabled.
|
|
1479
|
+
*
|
|
1480
|
+
* @example
|
|
1481
|
+
* console.log(vault.debugState());
|
|
1482
|
+
* // {
|
|
1483
|
+
* // vaultId: 'vault-abc123',
|
|
1484
|
+
* // isReady: true,
|
|
1485
|
+
* // tokenizing: null,
|
|
1486
|
+
* // destroyed: false,
|
|
1487
|
+
* // waxKeyPresent: true,
|
|
1488
|
+
* // elements: ['cardNumber', 'expirationDate', 'cvv'],
|
|
1489
|
+
* // ...
|
|
1490
|
+
* // }
|
|
1491
|
+
*/
|
|
1492
|
+
debugState() {
|
|
1493
|
+
return {
|
|
1494
|
+
vaultId: this.vaultId,
|
|
1495
|
+
isReady: this.tokenizerReady,
|
|
1496
|
+
tokenizing: this._tokenizing,
|
|
1497
|
+
destroyed: this._destroyed,
|
|
1498
|
+
waxKeyPresent: Boolean(this.waxKey),
|
|
1499
|
+
tokenizeSuccessCount: this._tokenizeSuccessCount,
|
|
1500
|
+
maxTokenizeCalls: this._maxTokenizeCalls,
|
|
1501
|
+
resetCount: this._resetCount,
|
|
1502
|
+
elements: [...this.elementsByType.keys()],
|
|
1503
|
+
bankElements: [...this.bankElementsByType.keys()],
|
|
1504
|
+
completionState: Object.fromEntries([...this.completionState.entries()].map(([id, v]) => [id.slice(0, 8), v])),
|
|
1505
|
+
pendingTokenizations: this.tokenizeResolvers.size,
|
|
1506
|
+
pendingBankTokenizations: this.bankTokenizeResolvers.size,
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1423
1509
|
mountTokenizerFrame() {
|
|
1424
1510
|
const mount = () => {
|
|
1425
1511
|
this._pendingMount = null;
|
|
@@ -1431,6 +1517,7 @@ class OzVault {
|
|
|
1431
1517
|
iframe.src = `${this.frameBaseUrl}/frame/tokenizer-frame.html#vaultId=${encodeURIComponent(this.vaultId)}${parentOrigin ? `&parentOrigin=${encodeURIComponent(parentOrigin)}` : ''}`;
|
|
1432
1518
|
document.body.appendChild(iframe);
|
|
1433
1519
|
this.tokenizerFrame = iframe;
|
|
1520
|
+
this.log('mounting tokenizer iframe');
|
|
1434
1521
|
};
|
|
1435
1522
|
if (document.readyState === 'loading') {
|
|
1436
1523
|
this._pendingMount = mount;
|
|
@@ -1471,6 +1558,7 @@ class OzVault {
|
|
|
1471
1558
|
`SDK expects v${PROTOCOL_VERSION}, frame reported v${typeof msg.__ozVersion === 'number' ? msg.__ozVersion : '(none)'}. ` +
|
|
1472
1559
|
'Deploy the matching frame assets to elements.ozura.com and purge the Azure CDN cache.');
|
|
1473
1560
|
}
|
|
1561
|
+
this.log('element iframe ready', { type: el.type, frameIdPrefix: frameId.slice(0, 8) });
|
|
1474
1562
|
}
|
|
1475
1563
|
// Intercept OZ_CHANGE before forwarding — handle auto-advance and CVV sync
|
|
1476
1564
|
if (msg.type === 'OZ_CHANGE') {
|
|
@@ -1494,6 +1582,7 @@ class OzVault {
|
|
|
1494
1582
|
// Require valid too — avoids advancing at 13 digits for unknown-brand cards
|
|
1495
1583
|
// where isComplete() fires before the user has finished typing.
|
|
1496
1584
|
const justCompleted = complete && valid && !wasComplete;
|
|
1585
|
+
this.log('field changed', { type: el.type, complete, valid, justCompleted });
|
|
1497
1586
|
// Sync CVV length when card brand changes
|
|
1498
1587
|
if (el.type === 'cardNumber') {
|
|
1499
1588
|
const brand = msg.cardBrand;
|
|
@@ -1505,15 +1594,17 @@ class OzVault {
|
|
|
1505
1594
|
// Auto-advance focus on completion
|
|
1506
1595
|
if (justCompleted) {
|
|
1507
1596
|
if (el.type === 'cardNumber') {
|
|
1597
|
+
this.log('auto-advance', { from: 'cardNumber', to: 'expirationDate' });
|
|
1508
1598
|
(_b = this.elementsByType.get('expirationDate')) === null || _b === void 0 ? void 0 : _b.focus();
|
|
1509
1599
|
}
|
|
1510
1600
|
else if (el.type === 'expirationDate') {
|
|
1601
|
+
this.log('auto-advance', { from: 'expirationDate', to: 'cvv' });
|
|
1511
1602
|
(_c = this.elementsByType.get('cvv')) === null || _c === void 0 ? void 0 : _c.focus();
|
|
1512
1603
|
}
|
|
1513
1604
|
}
|
|
1514
1605
|
}
|
|
1515
1606
|
handleTokenizerMessage(msg) {
|
|
1516
|
-
var _a, _b, _c;
|
|
1607
|
+
var _a, _b, _c, _d;
|
|
1517
1608
|
switch (msg.type) {
|
|
1518
1609
|
case 'OZ_FRAME_READY':
|
|
1519
1610
|
if (msg.__ozVersion !== PROTOCOL_VERSION) {
|
|
@@ -1533,6 +1624,7 @@ class OzVault {
|
|
|
1533
1624
|
// sent again from create() once the key is available.
|
|
1534
1625
|
this.sendToTokenizer(Object.assign({ type: 'OZ_INIT', frameId: '__tokenizer__' }, (this.waxKey ? { waxKey: this.waxKey } : {})));
|
|
1535
1626
|
(_c = this._onReady) === null || _c === void 0 ? void 0 : _c.call(this);
|
|
1627
|
+
this.log('tokenizer iframe ready', { protocolVersion: (_d = msg.__ozVersion) !== null && _d !== void 0 ? _d : null });
|
|
1536
1628
|
break;
|
|
1537
1629
|
case 'OZ_TOKEN_RESULT': {
|
|
1538
1630
|
if (typeof msg.requestId !== 'string' || !msg.requestId) {
|
|
@@ -1557,11 +1649,18 @@ class OzVault {
|
|
|
1557
1649
|
}
|
|
1558
1650
|
pending.resolve(Object.assign(Object.assign({ token,
|
|
1559
1651
|
cvcSession }, (card ? { card } : {})), (pending.billing ? { billing: pending.billing } : {})));
|
|
1652
|
+
this.log('token received', {
|
|
1653
|
+
elapsedMs: pending.tokenizeStartMs != null ? Date.now() - pending.tokenizeStartMs : null,
|
|
1654
|
+
tokenPresent: true,
|
|
1655
|
+
cvcSessionPresent: true,
|
|
1656
|
+
cardMetadataPresent: Boolean(card),
|
|
1657
|
+
});
|
|
1560
1658
|
// Increment the per-key success counter and proactively refresh once
|
|
1561
1659
|
// the budget is exhausted so the next createToken() call uses a fresh
|
|
1562
1660
|
// key without waiting for a vault rejection.
|
|
1563
1661
|
this._tokenizeSuccessCount++;
|
|
1564
1662
|
if (this._tokenizeSuccessCount >= this._maxTokenizeCalls) {
|
|
1663
|
+
this.log('proactive wax key refresh triggered', { tokenizeSuccessCount: this._tokenizeSuccessCount, maxTokenizeCalls: this._maxTokenizeCalls });
|
|
1565
1664
|
this.refreshWaxKey().catch((err) => {
|
|
1566
1665
|
console.warn('[OzVault] Post-budget wax key refresh failed:', err instanceof Error ? err.message : err);
|
|
1567
1666
|
});
|
|
@@ -1581,14 +1680,25 @@ class OzVault {
|
|
|
1581
1680
|
this.tokenizeResolvers.delete(msg.requestId);
|
|
1582
1681
|
if (pending.timeoutId != null)
|
|
1583
1682
|
clearTimeout(pending.timeoutId);
|
|
1683
|
+
const willRefresh = this.isRefreshableAuthError(errorCode, raw) && !pending.retried && Boolean(this._storedFetchWaxKey);
|
|
1684
|
+
this.log('token error', { errorCode, willRefresh });
|
|
1584
1685
|
// Auto-refresh: if the wax key expired or was consumed and we haven't
|
|
1585
1686
|
// already retried for this request, transparently re-mint and retry.
|
|
1586
|
-
if (
|
|
1687
|
+
if (willRefresh) {
|
|
1688
|
+
const resetCountAtRetry = this._resetCount;
|
|
1587
1689
|
this.refreshWaxKey().then(() => {
|
|
1588
1690
|
if (this._destroyed) {
|
|
1589
1691
|
pending.reject(new OzError('Vault destroyed during wax key refresh.'));
|
|
1590
1692
|
return;
|
|
1591
1693
|
}
|
|
1694
|
+
if (this._resetCount !== resetCountAtRetry) {
|
|
1695
|
+
// reset() was called while the wax key was refreshing — the fields
|
|
1696
|
+
// have been cleared and _tokenizing was zeroed. Reject the original
|
|
1697
|
+
// promise so it doesn't stay pending, and bail out without starting
|
|
1698
|
+
// a new retry (which would tokenize against empty fields).
|
|
1699
|
+
pending.reject(new OzError('Vault was reset while tokenization was in progress.'));
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1592
1702
|
const newRequestId = `req-${uuid()}`;
|
|
1593
1703
|
// _tokenizing is still 'card' (cleanup() hasn't been called yet)
|
|
1594
1704
|
this.tokenizeResolvers.set(newRequestId, Object.assign(Object.assign({}, pending), { retried: true }));
|
|
@@ -1635,11 +1745,16 @@ class OzVault {
|
|
|
1635
1745
|
if (bankPending.timeoutId != null)
|
|
1636
1746
|
clearTimeout(bankPending.timeoutId);
|
|
1637
1747
|
if (this.isRefreshableAuthError(errorCode, raw) && !bankPending.retried && this._storedFetchWaxKey) {
|
|
1748
|
+
const resetCountAtRetry = this._resetCount;
|
|
1638
1749
|
this.refreshWaxKey().then(() => {
|
|
1639
1750
|
if (this._destroyed) {
|
|
1640
1751
|
bankPending.reject(new OzError('Vault destroyed during wax key refresh.'));
|
|
1641
1752
|
return;
|
|
1642
1753
|
}
|
|
1754
|
+
if (this._resetCount !== resetCountAtRetry) {
|
|
1755
|
+
bankPending.reject(new OzError('Vault was reset while tokenization was in progress.'));
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1643
1758
|
const newRequestId = `req-${uuid()}`;
|
|
1644
1759
|
this.bankTokenizeResolvers.set(newRequestId, Object.assign(Object.assign({}, bankPending), { retried: true }));
|
|
1645
1760
|
try {
|
|
@@ -1697,9 +1812,15 @@ class OzVault {
|
|
|
1697
1812
|
}
|
|
1698
1813
|
const bank = isBankAccountMetadata(msg.bank) ? msg.bank : undefined;
|
|
1699
1814
|
pending.resolve(Object.assign({ token }, (bank ? { bank } : {})));
|
|
1815
|
+
this.log('bank token received', {
|
|
1816
|
+
elapsedMs: pending.tokenizeStartMs != null ? Date.now() - pending.tokenizeStartMs : null,
|
|
1817
|
+
tokenPresent: true,
|
|
1818
|
+
bankMetadataPresent: Boolean(bank),
|
|
1819
|
+
});
|
|
1700
1820
|
// Same proactive refresh logic as card tokenization.
|
|
1701
1821
|
this._tokenizeSuccessCount++;
|
|
1702
1822
|
if (this._tokenizeSuccessCount >= this._maxTokenizeCalls) {
|
|
1823
|
+
this.log('proactive wax key refresh triggered', { tokenizeSuccessCount: this._tokenizeSuccessCount, maxTokenizeCalls: this._maxTokenizeCalls });
|
|
1703
1824
|
this.refreshWaxKey().catch((err) => {
|
|
1704
1825
|
console.warn('[OzVault] Post-budget wax key refresh failed:', err instanceof Error ? err.message : err);
|
|
1705
1826
|
});
|
|
@@ -1755,6 +1876,7 @@ class OzVault {
|
|
|
1755
1876
|
}
|
|
1756
1877
|
const newSessionId = uuid();
|
|
1757
1878
|
(_a = this._onWaxRefresh) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
1879
|
+
this.log('wax key refresh started');
|
|
1758
1880
|
this._waxRefreshing = this._storedFetchWaxKey(newSessionId)
|
|
1759
1881
|
.then(newWaxKey => {
|
|
1760
1882
|
if (typeof newWaxKey !== 'string' || !newWaxKey.trim()) {
|
|
@@ -1768,6 +1890,11 @@ class OzVault {
|
|
|
1768
1890
|
if (!this._destroyed && this.tokenizerReady) {
|
|
1769
1891
|
this.sendToTokenizer({ type: 'OZ_INIT', frameId: '__tokenizer__', waxKey: newWaxKey });
|
|
1770
1892
|
}
|
|
1893
|
+
this.log('wax key refresh succeeded');
|
|
1894
|
+
})
|
|
1895
|
+
.catch((err) => {
|
|
1896
|
+
this.log('wax key refresh failed', { error: err instanceof Error ? err.message : String(err) });
|
|
1897
|
+
throw err;
|
|
1771
1898
|
})
|
|
1772
1899
|
.finally(() => {
|
|
1773
1900
|
this._waxRefreshing = null;
|
|
@@ -1875,7 +2002,7 @@ const OzContext = react.createContext({
|
|
|
1875
2002
|
* All `<OzCardNumber />`, `<OzExpiry />`, and `<OzCvv />` children must be
|
|
1876
2003
|
* rendered inside this provider.
|
|
1877
2004
|
*/
|
|
1878
|
-
function OzElements({ fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loadTimeoutMs, onWaxRefresh, onReady, appearance, maxTokenizeCalls, children }) {
|
|
2005
|
+
function OzElements({ fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loadTimeoutMs, onWaxRefresh, onReady, appearance, maxTokenizeCalls, debug, children }) {
|
|
1879
2006
|
const [vault, setVault] = react.useState(null);
|
|
1880
2007
|
const [initError, setInitError] = react.useState(null);
|
|
1881
2008
|
const [mountedCount, setMountedCount] = react.useState(0);
|
|
@@ -1916,7 +2043,7 @@ function OzElements({ fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loa
|
|
|
1916
2043
|
// synchronously rather than waiting for the promise to settle. Without this,
|
|
1917
2044
|
// two hidden iframes and two window listeners briefly coexist.
|
|
1918
2045
|
const abortController = new AbortController();
|
|
1919
|
-
OzVault.create(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ pubKey, fetchWaxKey: (sessionId) => fetchWaxKeyRef.current(sessionId) }, (frameBaseUrl ? { frameBaseUrl } : {})), (parsedFonts ? { fonts: parsedFonts } : {})), (parsedAppearance ? { appearance: parsedAppearance } : {})), (onLoadErrorRef.current ? { onLoadError: fireLoadError, loadTimeoutMs } : {})), {
|
|
2046
|
+
OzVault.create(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ pubKey, fetchWaxKey: (sessionId) => fetchWaxKeyRef.current(sessionId) }, (frameBaseUrl ? { frameBaseUrl } : {})), (parsedFonts ? { fonts: parsedFonts } : {})), (parsedAppearance ? { appearance: parsedAppearance } : {})), (onLoadErrorRef.current ? { onLoadError: fireLoadError, loadTimeoutMs } : {})), {
|
|
1920
2047
|
// Always install onWaxRefresh internally so we can reset tokenizeCount
|
|
1921
2048
|
// when any wax key refresh occurs (reactive TTL expiry, post-budget
|
|
1922
2049
|
// proactive, or visibility-change proactive). Without this the React
|
|
@@ -1942,7 +2069,7 @@ function OzElements({ fetchWaxKey, pubKey, frameBaseUrl, fonts, onLoadError, loa
|
|
|
1942
2069
|
var _a;
|
|
1943
2070
|
Promise.resolve().then(() => setTokenizeCount(0));
|
|
1944
2071
|
(_a = onWaxRefreshRef.current) === null || _a === void 0 ? void 0 : _a.call(onWaxRefreshRef);
|
|
1945
|
-
}, onReady: () => { var _a; return (_a = onReadyRef.current) === null || _a === void 0 ? void 0 : _a.call(onReadyRef); } }), (maxTokenizeCalls !== undefined ? { maxTokenizeCalls } : {})), abortController.signal).then(v => {
|
|
2072
|
+
}, onReady: () => { var _a; return (_a = onReadyRef.current) === null || _a === void 0 ? void 0 : _a.call(onReadyRef); } }), (maxTokenizeCalls !== undefined ? { maxTokenizeCalls } : {})), (debug ? { debug: true } : {})), abortController.signal).then(v => {
|
|
1946
2073
|
if (cancelled) {
|
|
1947
2074
|
v.destroy();
|
|
1948
2075
|
return;
|