@ozura/elements 1.0.2-next.17 → 1.0.2-next.19
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 +108 -9
- package/dist/oz-elements.esm.js.map +1 -1
- package/dist/oz-elements.umd.js +108 -9
- package/dist/oz-elements.umd.js.map +1 -1
- package/dist/react/index.cjs.js +112 -13
- package/dist/react/index.cjs.js.map +1 -1
- package/dist/react/index.esm.js +112 -13
- 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 +25 -0
- package/dist/react/types/index.d.ts +18 -0
- package/dist/server/sdk/OzVault.d.ts +25 -0
- package/dist/server/types/index.d.ts +18 -0
- package/dist/types/sdk/OzVault.d.ts +25 -0
- package/dist/types/types/index.d.ts +18 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ Card data is collected inside Ozura-hosted iframes so raw numbers never touch yo
|
|
|
28
28
|
- [vault.createBankToken()](#vaultcreatebanktokenoptions)
|
|
29
29
|
- [vault.reset()](#vaultreset)
|
|
30
30
|
- [vault.destroy()](#vaultdestroy)
|
|
31
|
+
- [vault.debugState()](#vaultdebugstate)
|
|
31
32
|
- [OzElement events](#ozelement-events)
|
|
32
33
|
- [React API](#react-api)
|
|
33
34
|
- [OzElements provider](#ozelements-provider)
|
|
@@ -41,6 +42,7 @@ Card data is collected inside Ozura-hosted iframes so raw numbers never touch yo
|
|
|
41
42
|
- [Custom fonts](#custom-fonts)
|
|
42
43
|
- [Billing details](#billing-details)
|
|
43
44
|
- [Error handling](#error-handling)
|
|
45
|
+
- [Debug mode](#debug-mode)
|
|
44
46
|
- [Server utilities](#server-utilities)
|
|
45
47
|
- [Ozura class](#ozura-class)
|
|
46
48
|
- [Route handler factories](#route-handler-factories)
|
|
@@ -361,6 +363,7 @@ Mounts the hidden tokenizer iframe and fetches the wax key concurrently. Both ha
|
|
|
361
363
|
| `onWaxRefresh` | `() => void` | — | Called when the SDK silently re-mints an expired wax key mid-tokenization. |
|
|
362
364
|
| `onReady` | `() => void` | — | Called once when the tokenizer iframe has loaded and is ready. Use in vanilla JS to re-check submit-button readiness when the tokenizer becomes ready after all element iframes have already fired. In React, `useOzElements().ready` handles this automatically. |
|
|
363
365
|
| `maxTokenizeCalls` | `number` | — | Maximum successful `createToken` calls per wax key before the key is considered consumed. Default: `3`. Must match `maxTokenizeCalls` in your server-side `mintWaxKey` call. |
|
|
366
|
+
| `debug` | `boolean` | — | Enables structured `[OzVault]`-prefixed `console.log` output at every lifecycle event. Safe to use in production — no sensitive data is ever logged. Default: `false`. See [Debug mode](#debug-mode) for details. |
|
|
364
367
|
|
|
365
368
|
Throws `OzError` if `fetchWaxKey` rejects, returns an empty string, or returns a non-string value.
|
|
366
369
|
|
|
@@ -580,6 +583,37 @@ try {
|
|
|
580
583
|
|
|
581
584
|
---
|
|
582
585
|
|
|
586
|
+
### vault.debugState()
|
|
587
|
+
|
|
588
|
+
```ts
|
|
589
|
+
vault.debugState(): Record<string, unknown>
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Returns a structured snapshot of the vault's internal state. Always available regardless of whether `debug: true` is set. Useful for attaching to support tickets or dumping on error.
|
|
593
|
+
|
|
594
|
+
```ts
|
|
595
|
+
console.log(vault.debugState());
|
|
596
|
+
// {
|
|
597
|
+
// vaultId: 'vault_abc12...',
|
|
598
|
+
// isReady: true,
|
|
599
|
+
// tokenizing: null,
|
|
600
|
+
// destroyed: false,
|
|
601
|
+
// waxKeyPresent: true,
|
|
602
|
+
// tokenizeSuccessCount: 1,
|
|
603
|
+
// maxTokenizeCalls: 3,
|
|
604
|
+
// resetCount: 0,
|
|
605
|
+
// elements: ['cardNumber', 'expirationDate', 'cvv'],
|
|
606
|
+
// bankElements: [],
|
|
607
|
+
// completionState: { 'a1b2c3d4': true, 'e5f6a7b8': true, '...' : false },
|
|
608
|
+
// pendingTokenizations: 0,
|
|
609
|
+
// pendingBankTokenizations: 0,
|
|
610
|
+
// }
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
No sensitive data is returned: wax keys, tokens, CVC sessions, and billing fields are never included.
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
583
617
|
### OzElement events
|
|
584
618
|
|
|
585
619
|
```ts
|
|
@@ -940,6 +974,78 @@ const display = normalizeCardSaleError(err.message); // cardSale API errors
|
|
|
940
974
|
|
|
941
975
|
---
|
|
942
976
|
|
|
977
|
+
## Debug mode
|
|
978
|
+
|
|
979
|
+
Pass `debug: true` in `VaultOptions` (or as a prop on `<OzElements>`) to activate structured console logging at every SDK lifecycle event.
|
|
980
|
+
|
|
981
|
+
```ts
|
|
982
|
+
const vault = await OzVault.create({
|
|
983
|
+
pubKey: 'pk_live_...',
|
|
984
|
+
fetchWaxKey: createFetchWaxKey('/api/mint-wax'),
|
|
985
|
+
debug: true, // enables [OzVault] console.log output
|
|
986
|
+
});
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
```tsx
|
|
990
|
+
// React
|
|
991
|
+
<OzElements pubKey="pk_live_..." fetchWaxKey={...} debug>
|
|
992
|
+
...
|
|
993
|
+
</OzElements>
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
Each log entry is a `[OzVault] <message>` prefixed `console.log` call. Events logged include:
|
|
997
|
+
|
|
998
|
+
| Event | When it fires |
|
|
999
|
+
|---|---|
|
|
1000
|
+
| `vault created` | Constructor completes |
|
|
1001
|
+
| `wax key received` | `fetchWaxKey` resolves |
|
|
1002
|
+
| `mounting tokenizer iframe` | Tokenizer iframe creation begins |
|
|
1003
|
+
| `tokenizer iframe ready` | Tokenizer iframe handshake complete |
|
|
1004
|
+
| `element iframe ready` | Each card/bank input iframe loads |
|
|
1005
|
+
| `field changed` | Per-field `change` event (empty/complete/valid/auto-advance state) |
|
|
1006
|
+
| `auto-advance` | Focus moves automatically between card fields |
|
|
1007
|
+
| `createToken() called` | Entry to `createToken()` |
|
|
1008
|
+
| `OZ_TOKENIZE sent` | Tokenize request dispatched to iframe |
|
|
1009
|
+
| `token received` | Token result returned (with elapsed ms) |
|
|
1010
|
+
| `token error` | Vault or network error during tokenize |
|
|
1011
|
+
| `proactive wax key refresh triggered` | Budget exhausted; refresh starting |
|
|
1012
|
+
| `wax key refresh started/succeeded/failed` | Refresh lifecycle |
|
|
1013
|
+
| `tab hidden` / `tab visible` | `visibilitychange` events |
|
|
1014
|
+
| `reset() called` | `vault.reset()` entry |
|
|
1015
|
+
| `destroy() called` | `vault.destroy()` entry |
|
|
1016
|
+
|
|
1017
|
+
**Security:** No sensitive data is ever logged. Wax keys, tokens, CVC sessions, and billing fields appear only as boolean presence flags (`waxKeyPresent: true`). Frame IDs and request IDs are truncated.
|
|
1018
|
+
|
|
1019
|
+
### vault.debugState()
|
|
1020
|
+
|
|
1021
|
+
`vault.debugState()` is always available — regardless of whether `debug: true` was set — and returns a one-time snapshot for attaching to bug reports:
|
|
1022
|
+
|
|
1023
|
+
```ts
|
|
1024
|
+
console.log(vault.debugState());
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
Sample output:
|
|
1028
|
+
|
|
1029
|
+
```json
|
|
1030
|
+
{
|
|
1031
|
+
"vaultId": "vault_abc12...",
|
|
1032
|
+
"isReady": true,
|
|
1033
|
+
"tokenizing": null,
|
|
1034
|
+
"destroyed": false,
|
|
1035
|
+
"waxKeyPresent": true,
|
|
1036
|
+
"tokenizeSuccessCount": 1,
|
|
1037
|
+
"maxTokenizeCalls": 3,
|
|
1038
|
+
"resetCount": 0,
|
|
1039
|
+
"elements": ["cardNumber", "expirationDate", "cvv"],
|
|
1040
|
+
"bankElements": [],
|
|
1041
|
+
"completionState": { "a1b2c3d4": true, "e5f6a7b8": true, "c9d0e1f2": false },
|
|
1042
|
+
"pendingTokenizations": 0,
|
|
1043
|
+
"pendingBankTokenizations": 0
|
|
1044
|
+
}
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
---
|
|
1048
|
+
|
|
943
1049
|
## Server utilities
|
|
944
1050
|
|
|
945
1051
|
> 📖 [Server SDK guide](https://docs.ozura.com/sdks/elements/server) — `Ozura` class methods, route handler factories, `getClientIp`, error types, and rate limits.
|
package/dist/oz-elements.esm.js
CHANGED
|
@@ -929,7 +929,7 @@ class OzVault {
|
|
|
929
929
|
* @internal
|
|
930
930
|
*/
|
|
931
931
|
constructor(options, waxKey, tokenizationSessionId) {
|
|
932
|
-
var _a, _b, _c;
|
|
932
|
+
var _a, _b, _c, _d;
|
|
933
933
|
this.elements = new Map();
|
|
934
934
|
this.elementsByType = new Map();
|
|
935
935
|
this.bankElementsByType = new Map();
|
|
@@ -964,13 +964,14 @@ class OzVault {
|
|
|
964
964
|
this.resolvedAppearance = resolveAppearance(options.appearance);
|
|
965
965
|
this.vaultId = `vault-${uuid()}`;
|
|
966
966
|
this._maxTokenizeCalls = (_b = options.maxTokenizeCalls) !== null && _b !== void 0 ? _b : 3;
|
|
967
|
+
this._debug = (_c = options.debug) !== null && _c !== void 0 ? _c : false;
|
|
967
968
|
this.boundHandleMessage = this.handleMessage.bind(this);
|
|
968
969
|
window.addEventListener('message', this.boundHandleMessage);
|
|
969
970
|
this.boundHandleVisibility = this.handleVisibilityChange.bind(this);
|
|
970
971
|
document.addEventListener('visibilitychange', this.boundHandleVisibility);
|
|
971
972
|
this.mountTokenizerFrame();
|
|
972
973
|
if (options.onLoadError) {
|
|
973
|
-
const timeout = (
|
|
974
|
+
const timeout = (_d = options.loadTimeoutMs) !== null && _d !== void 0 ? _d : 10000;
|
|
974
975
|
this.loadErrorTimeoutId = setTimeout(() => {
|
|
975
976
|
this.loadErrorTimeoutId = null;
|
|
976
977
|
if (!this._destroyed && !this.tokenizerReady) {
|
|
@@ -980,6 +981,7 @@ class OzVault {
|
|
|
980
981
|
}
|
|
981
982
|
this._onWaxRefresh = options.onWaxRefresh;
|
|
982
983
|
this._onReady = options.onReady;
|
|
984
|
+
this.log('vault created', { vaultId: this.vaultId, frameBaseUrl: this.frameBaseUrl, maxTokenizeCalls: this._maxTokenizeCalls });
|
|
983
985
|
}
|
|
984
986
|
/**
|
|
985
987
|
* Creates and returns a ready `OzVault` instance.
|
|
@@ -1049,6 +1051,7 @@ class OzVault {
|
|
|
1049
1051
|
if (vault.tokenizerReady) {
|
|
1050
1052
|
vault.sendToTokenizer({ type: 'OZ_INIT', frameId: '__tokenizer__', waxKey });
|
|
1051
1053
|
}
|
|
1054
|
+
vault.log('wax key received — vault ready');
|
|
1052
1055
|
return vault;
|
|
1053
1056
|
}
|
|
1054
1057
|
/**
|
|
@@ -1190,6 +1193,7 @@ class OzVault {
|
|
|
1190
1193
|
const readyBankElements = [accountEl, routingEl];
|
|
1191
1194
|
this._tokenizing = 'bank';
|
|
1192
1195
|
const requestId = `req-${uuid()}`;
|
|
1196
|
+
this.log('createBankToken() called');
|
|
1193
1197
|
return new Promise((resolve, reject) => {
|
|
1194
1198
|
const resetCountAtStart = this._resetCount;
|
|
1195
1199
|
const cleanup = () => {
|
|
@@ -1206,6 +1210,7 @@ class OzVault {
|
|
|
1206
1210
|
});
|
|
1207
1211
|
try {
|
|
1208
1212
|
const bankChannels = readyBankElements.map(() => new MessageChannel());
|
|
1213
|
+
const bankTokenizeStartMs = Date.now();
|
|
1209
1214
|
this.sendToTokenizer({
|
|
1210
1215
|
type: 'OZ_BANK_TOKENIZE',
|
|
1211
1216
|
requestId,
|
|
@@ -1216,6 +1221,7 @@ class OzVault {
|
|
|
1216
1221
|
lastName: options.lastName.trim(),
|
|
1217
1222
|
fieldCount: readyBankElements.length,
|
|
1218
1223
|
}, bankChannels.map(ch => ch.port1));
|
|
1224
|
+
this.log('OZ_BANK_TOKENIZE sent', { requestIdPrefix: `${requestId.slice(0, 12)}...`, fieldCount: readyBankElements.length });
|
|
1219
1225
|
readyBankElements.forEach((el, i) => el.beginCollect(requestId, bankChannels[i].port2));
|
|
1220
1226
|
const bankTimeoutId = setTimeout(() => {
|
|
1221
1227
|
if (this.bankTokenizeResolvers.has(requestId)) {
|
|
@@ -1226,8 +1232,10 @@ class OzVault {
|
|
|
1226
1232
|
}
|
|
1227
1233
|
}, 30000);
|
|
1228
1234
|
const bankPendingEntry = this.bankTokenizeResolvers.get(requestId);
|
|
1229
|
-
if (bankPendingEntry)
|
|
1235
|
+
if (bankPendingEntry) {
|
|
1230
1236
|
bankPendingEntry.timeoutId = bankTimeoutId;
|
|
1237
|
+
bankPendingEntry.tokenizeStartMs = bankTokenizeStartMs;
|
|
1238
|
+
}
|
|
1231
1239
|
}
|
|
1232
1240
|
catch (err) {
|
|
1233
1241
|
this.bankTokenizeResolvers.delete(requestId);
|
|
@@ -1298,6 +1306,7 @@ class OzVault {
|
|
|
1298
1306
|
}
|
|
1299
1307
|
this._tokenizing = 'card';
|
|
1300
1308
|
const requestId = `req-${uuid()}`;
|
|
1309
|
+
this.log('createToken() called');
|
|
1301
1310
|
return new Promise((resolve, reject) => {
|
|
1302
1311
|
// Capture the reset generation so cleanup() only zeros _tokenizing when it
|
|
1303
1312
|
// still belongs to this invocation — not a newer one that started after a reset.
|
|
@@ -1318,6 +1327,7 @@ class OzVault {
|
|
|
1318
1327
|
try {
|
|
1319
1328
|
// Tell tokenizer frame to expect N field values, then tokenize
|
|
1320
1329
|
const cardChannels = readyElements.map(() => new MessageChannel());
|
|
1330
|
+
const tokenizeStartMs = Date.now();
|
|
1321
1331
|
this.sendToTokenizer({
|
|
1322
1332
|
type: 'OZ_TOKENIZE',
|
|
1323
1333
|
requestId,
|
|
@@ -1328,6 +1338,11 @@ class OzVault {
|
|
|
1328
1338
|
lastName,
|
|
1329
1339
|
fieldCount: readyElements.length,
|
|
1330
1340
|
}, cardChannels.map(ch => ch.port1));
|
|
1341
|
+
this.log('OZ_TOKENIZE sent', { requestIdPrefix: `${requestId.slice(0, 12)}...`, fieldCount: readyElements.length });
|
|
1342
|
+
// Store start time for elapsed-ms logging on result
|
|
1343
|
+
const cardEntry = this.tokenizeResolvers.get(requestId);
|
|
1344
|
+
if (cardEntry)
|
|
1345
|
+
cardEntry.tokenizeStartMs = tokenizeStartMs;
|
|
1331
1346
|
// Tell each ready element frame to send its raw value to the tokenizer
|
|
1332
1347
|
readyElements.forEach((el, i) => el.beginCollect(requestId, cardChannels[i].port2));
|
|
1333
1348
|
const cardTimeoutId = setTimeout(() => {
|
|
@@ -1374,6 +1389,8 @@ class OzVault {
|
|
|
1374
1389
|
reset() {
|
|
1375
1390
|
if (this._destroyed)
|
|
1376
1391
|
return;
|
|
1392
|
+
const cancelling = Boolean(this._tokenizing);
|
|
1393
|
+
this.log('reset() called', { tokenizing: this._tokenizing, cancelling });
|
|
1377
1394
|
if (this._tokenizing) {
|
|
1378
1395
|
this._tokenizing = null;
|
|
1379
1396
|
this._resetCount++;
|
|
@@ -1414,6 +1431,7 @@ class OzVault {
|
|
|
1414
1431
|
if (this._destroyed)
|
|
1415
1432
|
return;
|
|
1416
1433
|
this._destroyed = true;
|
|
1434
|
+
this.log('destroy() called');
|
|
1417
1435
|
window.removeEventListener('message', this.boundHandleMessage);
|
|
1418
1436
|
document.removeEventListener('visibilitychange', this.boundHandleVisibility);
|
|
1419
1437
|
if (this._pendingMount) {
|
|
@@ -1468,13 +1486,17 @@ class OzVault {
|
|
|
1468
1486
|
const REFRESH_THRESHOLD_MS = 20 * 60 * 1000; // 20 minutes
|
|
1469
1487
|
if (document.hidden) {
|
|
1470
1488
|
this._hiddenAt = Date.now();
|
|
1489
|
+
this.log('tab hidden');
|
|
1471
1490
|
}
|
|
1472
1491
|
else {
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1492
|
+
const hiddenMs = this._hiddenAt !== null ? Date.now() - this._hiddenAt : 0;
|
|
1493
|
+
const willRefresh = (this._hiddenAt !== null &&
|
|
1494
|
+
hiddenMs >= REFRESH_THRESHOLD_MS &&
|
|
1495
|
+
Boolean(this._storedFetchWaxKey) &&
|
|
1476
1496
|
!this._tokenizing &&
|
|
1477
|
-
!this._waxRefreshing)
|
|
1497
|
+
!this._waxRefreshing);
|
|
1498
|
+
this.log('tab visible', { hiddenMs, willRefresh });
|
|
1499
|
+
if (willRefresh) {
|
|
1478
1500
|
this.refreshWaxKey().catch((err) => {
|
|
1479
1501
|
// Proactive refresh failure is non-fatal — the reactive path on the
|
|
1480
1502
|
// next createToken() call will handle it, including the auth retry.
|
|
@@ -1484,6 +1506,56 @@ class OzVault {
|
|
|
1484
1506
|
this._hiddenAt = null;
|
|
1485
1507
|
}
|
|
1486
1508
|
}
|
|
1509
|
+
// ─── Debug ───────────────────────────────────────────────────────────────
|
|
1510
|
+
/**
|
|
1511
|
+
* Emits a `[OzVault]`-prefixed entry to `console.log`. No-op when `debug` is
|
|
1512
|
+
* not set. Never called with sensitive values — callers use presence flags only.
|
|
1513
|
+
*/
|
|
1514
|
+
log(message, data) {
|
|
1515
|
+
if (!this._debug)
|
|
1516
|
+
return;
|
|
1517
|
+
if (data !== undefined) {
|
|
1518
|
+
console.log(`[OzVault] ${message}`, data);
|
|
1519
|
+
}
|
|
1520
|
+
else {
|
|
1521
|
+
console.log(`[OzVault] ${message}`);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Returns a plain-object snapshot of the vault's current internal state.
|
|
1526
|
+
* Safe to attach to bug reports — no wax keys, tokens, or billing data included.
|
|
1527
|
+
*
|
|
1528
|
+
* Available on all vault instances regardless of whether `debug` was enabled.
|
|
1529
|
+
*
|
|
1530
|
+
* @example
|
|
1531
|
+
* console.log(vault.debugState());
|
|
1532
|
+
* // {
|
|
1533
|
+
* // vaultId: 'vault-abc123',
|
|
1534
|
+
* // isReady: true,
|
|
1535
|
+
* // tokenizing: null,
|
|
1536
|
+
* // destroyed: false,
|
|
1537
|
+
* // waxKeyPresent: true,
|
|
1538
|
+
* // elements: ['cardNumber', 'expirationDate', 'cvv'],
|
|
1539
|
+
* // ...
|
|
1540
|
+
* // }
|
|
1541
|
+
*/
|
|
1542
|
+
debugState() {
|
|
1543
|
+
return {
|
|
1544
|
+
vaultId: this.vaultId,
|
|
1545
|
+
isReady: this.tokenizerReady,
|
|
1546
|
+
tokenizing: this._tokenizing,
|
|
1547
|
+
destroyed: this._destroyed,
|
|
1548
|
+
waxKeyPresent: Boolean(this.waxKey),
|
|
1549
|
+
tokenizeSuccessCount: this._tokenizeSuccessCount,
|
|
1550
|
+
maxTokenizeCalls: this._maxTokenizeCalls,
|
|
1551
|
+
resetCount: this._resetCount,
|
|
1552
|
+
elements: [...this.elementsByType.keys()],
|
|
1553
|
+
bankElements: [...this.bankElementsByType.keys()],
|
|
1554
|
+
completionState: Object.fromEntries([...this.completionState.entries()].map(([id, v]) => [id.slice(0, 8), v])),
|
|
1555
|
+
pendingTokenizations: this.tokenizeResolvers.size,
|
|
1556
|
+
pendingBankTokenizations: this.bankTokenizeResolvers.size,
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1487
1559
|
mountTokenizerFrame() {
|
|
1488
1560
|
const mount = () => {
|
|
1489
1561
|
this._pendingMount = null;
|
|
@@ -1495,6 +1567,7 @@ class OzVault {
|
|
|
1495
1567
|
iframe.src = `${this.frameBaseUrl}/frame/tokenizer-frame.html#vaultId=${encodeURIComponent(this.vaultId)}${parentOrigin ? `&parentOrigin=${encodeURIComponent(parentOrigin)}` : ''}`;
|
|
1496
1568
|
document.body.appendChild(iframe);
|
|
1497
1569
|
this.tokenizerFrame = iframe;
|
|
1570
|
+
this.log('mounting tokenizer iframe');
|
|
1498
1571
|
};
|
|
1499
1572
|
if (document.readyState === 'loading') {
|
|
1500
1573
|
this._pendingMount = mount;
|
|
@@ -1535,6 +1608,7 @@ class OzVault {
|
|
|
1535
1608
|
`SDK expects v${PROTOCOL_VERSION}, frame reported v${typeof msg.__ozVersion === 'number' ? msg.__ozVersion : '(none)'}. ` +
|
|
1536
1609
|
'Deploy the matching frame assets to elements.ozura.com and purge the Azure CDN cache.');
|
|
1537
1610
|
}
|
|
1611
|
+
this.log('element iframe ready', { type: el.type, frameIdPrefix: frameId.slice(0, 8) });
|
|
1538
1612
|
}
|
|
1539
1613
|
// Intercept OZ_CHANGE before forwarding — handle auto-advance and CVV sync
|
|
1540
1614
|
if (msg.type === 'OZ_CHANGE') {
|
|
@@ -1558,6 +1632,7 @@ class OzVault {
|
|
|
1558
1632
|
// Require valid too — avoids advancing at 13 digits for unknown-brand cards
|
|
1559
1633
|
// where isComplete() fires before the user has finished typing.
|
|
1560
1634
|
const justCompleted = complete && valid && !wasComplete;
|
|
1635
|
+
this.log('field changed', { type: el.type, complete, valid, justCompleted });
|
|
1561
1636
|
// Sync CVV length when card brand changes
|
|
1562
1637
|
if (el.type === 'cardNumber') {
|
|
1563
1638
|
const brand = msg.cardBrand;
|
|
@@ -1569,15 +1644,17 @@ class OzVault {
|
|
|
1569
1644
|
// Auto-advance focus on completion
|
|
1570
1645
|
if (justCompleted) {
|
|
1571
1646
|
if (el.type === 'cardNumber') {
|
|
1647
|
+
this.log('auto-advance', { from: 'cardNumber', to: 'expirationDate' });
|
|
1572
1648
|
(_b = this.elementsByType.get('expirationDate')) === null || _b === void 0 ? void 0 : _b.focus();
|
|
1573
1649
|
}
|
|
1574
1650
|
else if (el.type === 'expirationDate') {
|
|
1651
|
+
this.log('auto-advance', { from: 'expirationDate', to: 'cvv' });
|
|
1575
1652
|
(_c = this.elementsByType.get('cvv')) === null || _c === void 0 ? void 0 : _c.focus();
|
|
1576
1653
|
}
|
|
1577
1654
|
}
|
|
1578
1655
|
}
|
|
1579
1656
|
handleTokenizerMessage(msg) {
|
|
1580
|
-
var _a, _b, _c;
|
|
1657
|
+
var _a, _b, _c, _d;
|
|
1581
1658
|
switch (msg.type) {
|
|
1582
1659
|
case 'OZ_FRAME_READY':
|
|
1583
1660
|
if (msg.__ozVersion !== PROTOCOL_VERSION) {
|
|
@@ -1597,6 +1674,7 @@ class OzVault {
|
|
|
1597
1674
|
// sent again from create() once the key is available.
|
|
1598
1675
|
this.sendToTokenizer(Object.assign({ type: 'OZ_INIT', frameId: '__tokenizer__' }, (this.waxKey ? { waxKey: this.waxKey } : {})));
|
|
1599
1676
|
(_c = this._onReady) === null || _c === void 0 ? void 0 : _c.call(this);
|
|
1677
|
+
this.log('tokenizer iframe ready', { protocolVersion: (_d = msg.__ozVersion) !== null && _d !== void 0 ? _d : null });
|
|
1600
1678
|
break;
|
|
1601
1679
|
case 'OZ_TOKEN_RESULT': {
|
|
1602
1680
|
if (typeof msg.requestId !== 'string' || !msg.requestId) {
|
|
@@ -1621,11 +1699,18 @@ class OzVault {
|
|
|
1621
1699
|
}
|
|
1622
1700
|
pending.resolve(Object.assign(Object.assign({ token,
|
|
1623
1701
|
cvcSession }, (card ? { card } : {})), (pending.billing ? { billing: pending.billing } : {})));
|
|
1702
|
+
this.log('token received', {
|
|
1703
|
+
elapsedMs: pending.tokenizeStartMs != null ? Date.now() - pending.tokenizeStartMs : null,
|
|
1704
|
+
tokenPresent: true,
|
|
1705
|
+
cvcSessionPresent: true,
|
|
1706
|
+
cardMetadataPresent: Boolean(card),
|
|
1707
|
+
});
|
|
1624
1708
|
// Increment the per-key success counter and proactively refresh once
|
|
1625
1709
|
// the budget is exhausted so the next createToken() call uses a fresh
|
|
1626
1710
|
// key without waiting for a vault rejection.
|
|
1627
1711
|
this._tokenizeSuccessCount++;
|
|
1628
1712
|
if (this._tokenizeSuccessCount >= this._maxTokenizeCalls) {
|
|
1713
|
+
this.log('proactive wax key refresh triggered', { tokenizeSuccessCount: this._tokenizeSuccessCount, maxTokenizeCalls: this._maxTokenizeCalls });
|
|
1629
1714
|
this.refreshWaxKey().catch((err) => {
|
|
1630
1715
|
console.warn('[OzVault] Post-budget wax key refresh failed:', err instanceof Error ? err.message : err);
|
|
1631
1716
|
});
|
|
@@ -1645,9 +1730,11 @@ class OzVault {
|
|
|
1645
1730
|
this.tokenizeResolvers.delete(msg.requestId);
|
|
1646
1731
|
if (pending.timeoutId != null)
|
|
1647
1732
|
clearTimeout(pending.timeoutId);
|
|
1733
|
+
const willRefresh = this.isRefreshableAuthError(errorCode, raw) && !pending.retried && Boolean(this._storedFetchWaxKey);
|
|
1734
|
+
this.log('token error', { errorCode, willRefresh });
|
|
1648
1735
|
// Auto-refresh: if the wax key expired or was consumed and we haven't
|
|
1649
1736
|
// already retried for this request, transparently re-mint and retry.
|
|
1650
|
-
if (
|
|
1737
|
+
if (willRefresh) {
|
|
1651
1738
|
const resetCountAtRetry = this._resetCount;
|
|
1652
1739
|
this.refreshWaxKey().then(() => {
|
|
1653
1740
|
if (this._destroyed) {
|
|
@@ -1775,9 +1862,15 @@ class OzVault {
|
|
|
1775
1862
|
}
|
|
1776
1863
|
const bank = isBankAccountMetadata(msg.bank) ? msg.bank : undefined;
|
|
1777
1864
|
pending.resolve(Object.assign({ token }, (bank ? { bank } : {})));
|
|
1865
|
+
this.log('bank token received', {
|
|
1866
|
+
elapsedMs: pending.tokenizeStartMs != null ? Date.now() - pending.tokenizeStartMs : null,
|
|
1867
|
+
tokenPresent: true,
|
|
1868
|
+
bankMetadataPresent: Boolean(bank),
|
|
1869
|
+
});
|
|
1778
1870
|
// Same proactive refresh logic as card tokenization.
|
|
1779
1871
|
this._tokenizeSuccessCount++;
|
|
1780
1872
|
if (this._tokenizeSuccessCount >= this._maxTokenizeCalls) {
|
|
1873
|
+
this.log('proactive wax key refresh triggered', { tokenizeSuccessCount: this._tokenizeSuccessCount, maxTokenizeCalls: this._maxTokenizeCalls });
|
|
1781
1874
|
this.refreshWaxKey().catch((err) => {
|
|
1782
1875
|
console.warn('[OzVault] Post-budget wax key refresh failed:', err instanceof Error ? err.message : err);
|
|
1783
1876
|
});
|
|
@@ -1833,6 +1926,7 @@ class OzVault {
|
|
|
1833
1926
|
}
|
|
1834
1927
|
const newSessionId = uuid();
|
|
1835
1928
|
(_a = this._onWaxRefresh) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
1929
|
+
this.log('wax key refresh started');
|
|
1836
1930
|
this._waxRefreshing = this._storedFetchWaxKey(newSessionId)
|
|
1837
1931
|
.then(newWaxKey => {
|
|
1838
1932
|
if (typeof newWaxKey !== 'string' || !newWaxKey.trim()) {
|
|
@@ -1846,6 +1940,11 @@ class OzVault {
|
|
|
1846
1940
|
if (!this._destroyed && this.tokenizerReady) {
|
|
1847
1941
|
this.sendToTokenizer({ type: 'OZ_INIT', frameId: '__tokenizer__', waxKey: newWaxKey });
|
|
1848
1942
|
}
|
|
1943
|
+
this.log('wax key refresh succeeded');
|
|
1944
|
+
})
|
|
1945
|
+
.catch((err) => {
|
|
1946
|
+
this.log('wax key refresh failed', { error: err instanceof Error ? err.message : String(err) });
|
|
1947
|
+
throw err;
|
|
1849
1948
|
})
|
|
1850
1949
|
.finally(() => {
|
|
1851
1950
|
this._waxRefreshing = null;
|