@ozura/elements 1.2.0-next.26 → 1.2.0-next.27
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/dist/frame/element-frame.js +35 -1
- package/dist/frame/element-frame.js.map +1 -1
- package/dist/frame/tokenizer-frame.js +111 -50
- package/dist/frame/tokenizer-frame.js.map +1 -1
- package/dist/oz-elements.esm.js +101 -68
- package/dist/oz-elements.esm.js.map +1 -1
- package/dist/oz-elements.umd.js +101 -68
- package/dist/oz-elements.umd.js.map +1 -1
- package/dist/react/frame/elementFrame.d.ts +7 -0
- package/dist/react/frame/tokenizerFrame.d.ts +4 -1
- package/dist/react/index.cjs.js +101 -68
- package/dist/react/index.cjs.js.map +1 -1
- package/dist/react/index.esm.js +101 -68
- package/dist/react/index.esm.js.map +1 -1
- package/dist/react/sdk/OzElement.d.ts +3 -1
- package/dist/react/sdk/OzVault.d.ts +3 -9
- package/dist/server/frame/elementFrame.d.ts +7 -0
- package/dist/server/frame/tokenizerFrame.d.ts +4 -1
- package/dist/server/sdk/OzElement.d.ts +3 -1
- package/dist/server/sdk/OzVault.d.ts +3 -9
- package/dist/types/frame/elementFrame.d.ts +7 -0
- package/dist/types/frame/tokenizerFrame.d.ts +4 -1
- package/dist/types/sdk/OzElement.d.ts +3 -1
- package/dist/types/sdk/OzVault.d.ts +3 -9
- package/package.json +1 -1
package/dist/react/index.esm.js
CHANGED
|
@@ -324,7 +324,7 @@ function sanitizeOptions(options) {
|
|
|
324
324
|
* it never holds raw card data — all sensitive values live in the iframe.
|
|
325
325
|
*/
|
|
326
326
|
class OzElement {
|
|
327
|
-
constructor(elementType, options, vaultId, frameBaseUrl, fonts = [], appearanceStyle) {
|
|
327
|
+
constructor(elementType, options, vaultId, frameBaseUrl, fonts = [], appearanceStyle, onDestroy) {
|
|
328
328
|
this.iframe = null;
|
|
329
329
|
this._frameWindow = null;
|
|
330
330
|
this._ready = false;
|
|
@@ -340,6 +340,7 @@ class OzElement {
|
|
|
340
340
|
this.fonts = fonts;
|
|
341
341
|
this.appearanceStyle = appearanceStyle;
|
|
342
342
|
this.frameId = `oz-${elementType}-${uuid()}`;
|
|
343
|
+
this._onDestroy = onDestroy;
|
|
343
344
|
}
|
|
344
345
|
/** The element type this proxy represents. */
|
|
345
346
|
get type() {
|
|
@@ -495,9 +496,14 @@ class OzElement {
|
|
|
495
496
|
* and prevents future use. Distinct from `unmount()` which allows re-mounting.
|
|
496
497
|
*/
|
|
497
498
|
destroy() {
|
|
499
|
+
var _a;
|
|
498
500
|
this.unmount();
|
|
499
501
|
this.handlers.clear();
|
|
500
502
|
this._destroyed = true;
|
|
503
|
+
// Notify OzVault so it can prune the stale frameId entry from its elements
|
|
504
|
+
// and completionState maps. Without this, manually calling el.destroy() leaks
|
|
505
|
+
// map entries that grow unboundedly in SPA scenarios with repeated mount/unmount.
|
|
506
|
+
(_a = this._onDestroy) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
501
507
|
}
|
|
502
508
|
// ─── Called by OzVault ───────────────────────────────────────────────────
|
|
503
509
|
/**
|
|
@@ -900,7 +906,22 @@ function createSessionFetcher(url) {
|
|
|
900
906
|
throw new OzError(`Could not reach session endpoint (${url}): ${msg}`, undefined, 'network');
|
|
901
907
|
}
|
|
902
908
|
}
|
|
903
|
-
|
|
909
|
+
// Parse JSON separately from the ok-check so that a non-JSON error body
|
|
910
|
+
// (HTML error page, WAF block, CDN 503) produces the right error code.
|
|
911
|
+
// Previously res.json() was attempted before res.ok was checked; a parse
|
|
912
|
+
// failure on a 5xx HTML body would fall through as {} and produce a
|
|
913
|
+
// misleading 'validation' code when the real cause is a server/network issue.
|
|
914
|
+
let data = {};
|
|
915
|
+
try {
|
|
916
|
+
data = await res.json();
|
|
917
|
+
}
|
|
918
|
+
catch (_a) {
|
|
919
|
+
if (!res.ok) {
|
|
920
|
+
throw new OzError(`Session endpoint returned HTTP ${res.status} with a non-JSON body`, undefined, res.status >= 500 ? 'server' : res.status === 401 || res.status === 403 ? 'auth' : 'validation');
|
|
921
|
+
}
|
|
922
|
+
// HTTP 200 but body isn't JSON — this is a misconfigured session endpoint.
|
|
923
|
+
throw new OzError('Session endpoint returned HTTP 200 but the response body is not valid JSON. Check your /api/oz-session implementation.', undefined, 'validation');
|
|
924
|
+
}
|
|
904
925
|
if (!res.ok) {
|
|
905
926
|
throw new OzError(typeof data.error === 'string' && data.error
|
|
906
927
|
? data.error
|
|
@@ -918,10 +939,19 @@ function createSessionFetcher(url) {
|
|
|
918
939
|
}
|
|
919
940
|
|
|
920
941
|
function isCardMetadata(v) {
|
|
921
|
-
|
|
942
|
+
if (!v || typeof v !== 'object')
|
|
943
|
+
return false;
|
|
944
|
+
const r = v;
|
|
945
|
+
return (typeof r.last4 === 'string' &&
|
|
946
|
+
typeof r.brand === 'string' &&
|
|
947
|
+
typeof r.expMonth === 'string' &&
|
|
948
|
+
typeof r.expYear === 'string');
|
|
922
949
|
}
|
|
923
950
|
function isBankAccountMetadata(v) {
|
|
924
|
-
|
|
951
|
+
if (!v || typeof v !== 'object')
|
|
952
|
+
return false;
|
|
953
|
+
const r = v;
|
|
954
|
+
return typeof r.last4 === 'string' && typeof r.routingNumberLast4 === 'string';
|
|
925
955
|
}
|
|
926
956
|
const DEFAULT_FRAME_BASE_URL = "https://lively-hill-097170c0f.4.azurestaticapps.net";
|
|
927
957
|
/**
|
|
@@ -931,16 +961,10 @@ const DEFAULT_FRAME_BASE_URL = "https://lively-hill-097170c0f.4.azurestaticapps.
|
|
|
931
961
|
* Use the static `OzVault.create()` factory — do not call `new OzVault()` directly.
|
|
932
962
|
*
|
|
933
963
|
* @example
|
|
964
|
+
* // Recommended — pass sessionUrl and let the SDK call your backend automatically
|
|
934
965
|
* const vault = await OzVault.create({
|
|
935
|
-
* pubKey: '
|
|
936
|
-
*
|
|
937
|
-
* // Call your backend — which calls ozura.mintWaxKey() from @ozura/elements/server
|
|
938
|
-
* const { waxKey } = await fetch('/api/mint-wax', {
|
|
939
|
-
* method: 'POST',
|
|
940
|
-
* body: JSON.stringify({ sessionId }),
|
|
941
|
-
* }).then(r => r.json());
|
|
942
|
-
* return waxKey;
|
|
943
|
-
* },
|
|
966
|
+
* pubKey: 'pk_prod_...', // or 'pk_test_...' for test mode
|
|
967
|
+
* sessionUrl: '/api/oz-session', // backend endpoint that calls ozura.createSession()
|
|
944
968
|
* });
|
|
945
969
|
* const cardNum = vault.createElement('cardNumber');
|
|
946
970
|
* cardNum.mount('#card-number');
|
|
@@ -1176,7 +1200,12 @@ class OzVault {
|
|
|
1176
1200
|
this.completionState.delete(existing.frameId);
|
|
1177
1201
|
existing.destroy();
|
|
1178
1202
|
}
|
|
1179
|
-
const el = new OzElement(type, options, this.vaultId, this.frameBaseUrl, this.fonts, this.resolvedAppearance)
|
|
1203
|
+
const el = new OzElement(type, options, this.vaultId, this.frameBaseUrl, this.fonts, this.resolvedAppearance, () => {
|
|
1204
|
+
// Prune vault-level maps when the element is manually destroyed so they
|
|
1205
|
+
// don't grow unboundedly in SPA scenarios with repeated mount/unmount cycles.
|
|
1206
|
+
this.elements.delete(el.frameId);
|
|
1207
|
+
this.completionState.delete(el.frameId);
|
|
1208
|
+
});
|
|
1180
1209
|
this.elements.set(el.frameId, el);
|
|
1181
1210
|
typeMap.set(type, el);
|
|
1182
1211
|
return el;
|
|
@@ -1833,61 +1862,65 @@ class OzVault {
|
|
|
1833
1862
|
}
|
|
1834
1863
|
pending.reject(new OzError(normalizeVaultError(raw), raw, errorCode));
|
|
1835
1864
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
const
|
|
1857
|
-
this.
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1865
|
+
else {
|
|
1866
|
+
// Also check bank resolvers — both card and bank errors use OZ_TOKEN_ERROR.
|
|
1867
|
+
// Use else-if rather than sequential checks so a UUID collision (however
|
|
1868
|
+
// improbable) can never trigger double-rejection of two unrelated resolvers.
|
|
1869
|
+
const bankPending = this.bankTokenizeResolvers.get(msg.requestId);
|
|
1870
|
+
if (bankPending) {
|
|
1871
|
+
this.bankTokenizeResolvers.delete(msg.requestId);
|
|
1872
|
+
if (bankPending.timeoutId != null)
|
|
1873
|
+
clearTimeout(bankPending.timeoutId);
|
|
1874
|
+
if (this.isRefreshableAuthError(errorCode, raw) && !bankPending.retried && this._storedFetchWaxKey) {
|
|
1875
|
+
const resetCountAtRetry = this._resetCount;
|
|
1876
|
+
this.refreshWaxKey().then(() => {
|
|
1877
|
+
if (this._destroyed) {
|
|
1878
|
+
bankPending.reject(new OzError('Vault destroyed during wax key refresh.'));
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
if (this._resetCount !== resetCountAtRetry) {
|
|
1882
|
+
bankPending.reject(new OzError('Vault was reset while tokenization was in progress.'));
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
const newRequestId = `req-${uuid()}`;
|
|
1886
|
+
this.bankTokenizeResolvers.set(newRequestId, Object.assign(Object.assign({}, bankPending), { retried: true }));
|
|
1887
|
+
try {
|
|
1888
|
+
const retryBankChannels = bankPending.readyElements.map(() => new MessageChannel());
|
|
1889
|
+
this.sendToTokenizer({
|
|
1890
|
+
type: 'OZ_BANK_TOKENIZE',
|
|
1891
|
+
requestId: newRequestId,
|
|
1892
|
+
waxKey: this.waxKey,
|
|
1893
|
+
tokenizationSessionId: this.tokenizationSessionId,
|
|
1894
|
+
pubKey: this.pubKey,
|
|
1895
|
+
firstName: bankPending.firstName,
|
|
1896
|
+
lastName: bankPending.lastName,
|
|
1897
|
+
fieldCount: bankPending.fieldCount,
|
|
1898
|
+
}, retryBankChannels.map(ch => ch.port1));
|
|
1899
|
+
bankPending.readyElements.forEach((el, i) => el.beginCollect(newRequestId, retryBankChannels[i].port2));
|
|
1900
|
+
const retryBankTimeoutId = setTimeout(() => {
|
|
1901
|
+
if (this.bankTokenizeResolvers.has(newRequestId)) {
|
|
1902
|
+
this.bankTokenizeResolvers.delete(newRequestId);
|
|
1903
|
+
this.sendToTokenizer({ type: 'OZ_TOKENIZE_CANCEL', requestId: newRequestId });
|
|
1904
|
+
bankPending.reject(new OzError('Bank tokenization timed out after wax key refresh.', undefined, 'timeout'));
|
|
1905
|
+
}
|
|
1906
|
+
}, 30000);
|
|
1907
|
+
const retryBankEntry = this.bankTokenizeResolvers.get(newRequestId);
|
|
1908
|
+
if (retryBankEntry)
|
|
1909
|
+
retryBankEntry.timeoutId = retryBankTimeoutId;
|
|
1910
|
+
}
|
|
1911
|
+
catch (setupErr) {
|
|
1912
|
+
this.bankTokenizeResolvers.delete(newRequestId);
|
|
1913
|
+
bankPending.reject(setupErr instanceof OzError ? setupErr : new OzError('Retry bank tokenization failed to start'));
|
|
1914
|
+
}
|
|
1915
|
+
}).catch((refreshErr) => {
|
|
1916
|
+
const msg = refreshErr instanceof Error ? refreshErr.message : 'Wax key refresh failed';
|
|
1917
|
+
bankPending.reject(new OzError(msg, undefined, 'auth'));
|
|
1918
|
+
});
|
|
1919
|
+
break;
|
|
1920
|
+
}
|
|
1921
|
+
bankPending.reject(new OzError(normalizeBankVaultError(raw), raw, errorCode));
|
|
1888
1922
|
}
|
|
1889
|
-
|
|
1890
|
-
}
|
|
1923
|
+
} // end else (bank path)
|
|
1891
1924
|
break;
|
|
1892
1925
|
}
|
|
1893
1926
|
case 'OZ_BANK_TOKEN_RESULT': {
|