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