@ozura/elements 0.1.0-beta.6 → 1.0.0
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 +1121 -720
- package/dist/frame/element-frame.js +77 -57
- package/dist/frame/element-frame.js.map +1 -1
- package/dist/frame/tokenizer-frame.html +1 -1
- package/dist/frame/tokenizer-frame.js +221 -74
- package/dist/frame/tokenizer-frame.js.map +1 -1
- package/dist/oz-elements.esm.js +870 -231
- package/dist/oz-elements.esm.js.map +1 -1
- package/dist/oz-elements.umd.js +870 -230
- package/dist/oz-elements.umd.js.map +1 -1
- package/dist/react/frame/tokenizerFrame.d.ts +32 -0
- package/dist/react/index.cjs.js +1045 -220
- package/dist/react/index.cjs.js.map +1 -1
- package/dist/react/index.esm.js +1042 -221
- package/dist/react/index.esm.js.map +1 -1
- package/dist/react/react/index.d.ts +165 -8
- package/dist/react/sdk/OzElement.d.ts +34 -3
- package/dist/react/sdk/OzVault.d.ts +104 -4
- package/dist/react/sdk/errors.d.ts +9 -0
- package/dist/react/sdk/index.d.ts +29 -0
- package/dist/react/server/index.d.ts +266 -2
- package/dist/react/types/index.d.ts +94 -16
- package/dist/react/utils/appearance.d.ts +9 -0
- package/dist/react/utils/cardUtils.d.ts +14 -0
- package/dist/react/utils/uuid.d.ts +12 -0
- package/dist/server/frame/tokenizerFrame.d.ts +32 -0
- package/dist/server/index.cjs.js +761 -30
- package/dist/server/index.cjs.js.map +1 -1
- package/dist/server/index.esm.js +757 -31
- package/dist/server/index.esm.js.map +1 -1
- package/dist/server/sdk/OzElement.d.ts +34 -3
- package/dist/server/sdk/OzVault.d.ts +104 -4
- package/dist/server/sdk/errors.d.ts +9 -0
- package/dist/server/sdk/index.d.ts +29 -0
- package/dist/server/server/index.d.ts +266 -2
- package/dist/server/types/index.d.ts +94 -16
- package/dist/server/utils/appearance.d.ts +9 -0
- package/dist/server/utils/cardUtils.d.ts +14 -0
- package/dist/server/utils/uuid.d.ts +12 -0
- package/dist/types/frame/tokenizerFrame.d.ts +32 -0
- package/dist/types/sdk/OzElement.d.ts +34 -3
- package/dist/types/sdk/OzVault.d.ts +104 -4
- package/dist/types/sdk/errors.d.ts +9 -0
- package/dist/types/sdk/index.d.ts +29 -0
- package/dist/types/server/index.d.ts +266 -2
- package/dist/types/types/index.d.ts +94 -16
- package/dist/types/utils/appearance.d.ts +9 -0
- package/dist/types/utils/cardUtils.d.ts +14 -0
- package/dist/types/utils/uuid.d.ts +12 -0
- package/package.json +7 -4
|
@@ -4,8 +4,20 @@ import { ElementType, BankElementType, ElementOptions, VaultOptions, TokenizeOpt
|
|
|
4
4
|
* The main entry point for OzElements. Creates and manages iframe-based
|
|
5
5
|
* card input elements that keep raw card data isolated from the merchant page.
|
|
6
6
|
*
|
|
7
|
+
* Use the static `OzVault.create()` factory — do not call `new OzVault()` directly.
|
|
8
|
+
*
|
|
7
9
|
* @example
|
|
8
|
-
* const vault =
|
|
10
|
+
* const vault = await OzVault.create({
|
|
11
|
+
* pubKey: 'pk_live_...',
|
|
12
|
+
* fetchWaxKey: async (sessionId) => {
|
|
13
|
+
* // Call your backend — which calls ozura.mintWaxKey() from @ozura/elements/server
|
|
14
|
+
* const { waxKey } = await fetch('/api/mint-wax', {
|
|
15
|
+
* method: 'POST',
|
|
16
|
+
* body: JSON.stringify({ sessionId }),
|
|
17
|
+
* }).then(r => r.json());
|
|
18
|
+
* return waxKey;
|
|
19
|
+
* },
|
|
20
|
+
* });
|
|
9
21
|
* const cardNum = vault.createElement('cardNumber');
|
|
10
22
|
* cardNum.mount('#card-number');
|
|
11
23
|
* const { token, cvcSession } = await vault.createToken({
|
|
@@ -13,7 +25,8 @@ import { ElementType, BankElementType, ElementOptions, VaultOptions, TokenizeOpt
|
|
|
13
25
|
* });
|
|
14
26
|
*/
|
|
15
27
|
export declare class OzVault {
|
|
16
|
-
private
|
|
28
|
+
private waxKey;
|
|
29
|
+
private tokenizationSessionId;
|
|
17
30
|
private pubKey;
|
|
18
31
|
private frameBaseUrl;
|
|
19
32
|
private frameOrigin;
|
|
@@ -28,20 +41,75 @@ export declare class OzVault {
|
|
|
28
41
|
private completionState;
|
|
29
42
|
private tokenizerFrame;
|
|
30
43
|
private tokenizerWindow;
|
|
31
|
-
private tokenizerName;
|
|
32
44
|
private tokenizerReady;
|
|
33
45
|
private _tokenizing;
|
|
34
46
|
private _destroyed;
|
|
47
|
+
private _tokenizeSuccessCount;
|
|
48
|
+
private _maxTokenizeCalls;
|
|
35
49
|
private boundHandleMessage;
|
|
36
50
|
private _pendingMount;
|
|
51
|
+
private _storedFetchWaxKey;
|
|
52
|
+
private _waxRefreshing;
|
|
53
|
+
private _onWaxRefresh;
|
|
54
|
+
private _onReady;
|
|
37
55
|
private loadErrorTimeoutId;
|
|
38
|
-
|
|
56
|
+
private _hiddenAt;
|
|
57
|
+
private boundHandleVisibility;
|
|
58
|
+
/**
|
|
59
|
+
* Internal constructor — use `OzVault.create()` instead.
|
|
60
|
+
* The constructor mounts the tokenizer iframe immediately so it can start
|
|
61
|
+
* loading in parallel while `fetchWaxKey` is being awaited.
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
|
+
private constructor();
|
|
65
|
+
/**
|
|
66
|
+
* Creates and returns a ready `OzVault` instance.
|
|
67
|
+
*
|
|
68
|
+
* Internally this:
|
|
69
|
+
* 1. Generates a `tokenizationSessionId` (UUID).
|
|
70
|
+
* 2. Starts loading the hidden tokenizer iframe immediately.
|
|
71
|
+
* 3. Calls `options.fetchWaxKey(tokenizationSessionId)` concurrently — your
|
|
72
|
+
* backend mints a session-bound wax key from the vault and returns it.
|
|
73
|
+
* 4. Resolves with the vault instance once the wax key is stored. The iframe
|
|
74
|
+
* has been loading the whole time, so `isReady` may already be true or
|
|
75
|
+
* will fire shortly after.
|
|
76
|
+
*
|
|
77
|
+
* The returned vault is ready to create elements immediately. `createToken()`
|
|
78
|
+
* additionally requires `vault.isReady` (tokenizer iframe loaded).
|
|
79
|
+
*
|
|
80
|
+
* @throws {OzError} if `fetchWaxKey` throws, returns a non-string value, or returns an empty/whitespace-only string.
|
|
81
|
+
*/
|
|
82
|
+
static create(options: VaultOptions, signal?: AbortSignal): Promise<OzVault>;
|
|
39
83
|
/**
|
|
40
84
|
* True once the hidden tokenizer iframe has loaded and signalled ready.
|
|
41
85
|
* Use this to gate the pay button when building custom UIs without React.
|
|
42
86
|
* React consumers should use the `ready` value returned by `useOzElements()`.
|
|
87
|
+
*
|
|
88
|
+
* Once `true`, remains `true` for the lifetime of this vault instance.
|
|
89
|
+
* It only reverts to `false` after `vault.destroy()` is called, at which
|
|
90
|
+
* point the vault is unusable and a new instance must be created.
|
|
91
|
+
*
|
|
92
|
+
* @remarks
|
|
93
|
+
* This tracks **tokenizer readiness only** — it says nothing about whether
|
|
94
|
+
* the individual element iframes (card number, CVV, etc.) have loaded.
|
|
95
|
+
* A vault can be `isReady === true` while elements are still mounting.
|
|
96
|
+
* To gate a submit button correctly in vanilla JS, wait for every element's
|
|
97
|
+
* `'ready'` event in addition to this flag. In React, use the `ready` value
|
|
98
|
+
* from `useOzElements()` instead, which combines both checks automatically.
|
|
43
99
|
*/
|
|
44
100
|
get isReady(): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Number of successful tokenize calls made against the current wax key.
|
|
103
|
+
*
|
|
104
|
+
* Resets to `0` each time the wax key is refreshed (proactively or reactively).
|
|
105
|
+
* Useful in vanilla JS integrations to display "attempts remaining" UI.
|
|
106
|
+
* In React, use `tokenizeCount` from `useOzElements()` instead.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* const remaining = 3 - vault.tokenizeCount;
|
|
110
|
+
* payButton.textContent = remaining > 0 ? `Pay (${remaining} attempts left)` : 'Pay';
|
|
111
|
+
*/
|
|
112
|
+
get tokenizeCount(): number;
|
|
45
113
|
/**
|
|
46
114
|
* Creates a new OzElement of the given type. Call `.mount(selector)` on the
|
|
47
115
|
* returned element to attach it to the DOM.
|
|
@@ -93,6 +161,17 @@ export declare class OzVault {
|
|
|
93
161
|
* unmounts (e.g. in React's useEffect cleanup or a SPA route change).
|
|
94
162
|
*/
|
|
95
163
|
destroy(): void;
|
|
164
|
+
/**
|
|
165
|
+
* Proactively re-mints the wax key when the page becomes visible again after
|
|
166
|
+
* a long idle period. Wax keys have a fixed TTL (~30 minutes); a user who
|
|
167
|
+
* leaves the tab in the background and returns could have an expired key.
|
|
168
|
+
* Rather than waiting for a failed tokenization to trigger the reactive
|
|
169
|
+
* refresh path, this pre-empts the failure when the vault is idle.
|
|
170
|
+
*
|
|
171
|
+
* Threshold: 20 minutes hidden. Chosen to be comfortably inside the ~30m TTL
|
|
172
|
+
* while avoiding spurious refreshes for brief tab-switches.
|
|
173
|
+
*/
|
|
174
|
+
private handleVisibilityChange;
|
|
96
175
|
private mountTokenizerFrame;
|
|
97
176
|
private handleMessage;
|
|
98
177
|
/**
|
|
@@ -102,5 +181,26 @@ export declare class OzVault {
|
|
|
102
181
|
*/
|
|
103
182
|
private handleElementChange;
|
|
104
183
|
private handleTokenizerMessage;
|
|
184
|
+
/**
|
|
185
|
+
* Returns true when an OZ_TOKEN_ERROR should trigger a wax key refresh.
|
|
186
|
+
*
|
|
187
|
+
* Primary path: vault returns 401/403 → errorCode 'auth'.
|
|
188
|
+
* Defensive path: vault returns 400 → errorCode 'validation', but the raw
|
|
189
|
+
* message contains wax-key-specific language (consumed, expired, invalid key,
|
|
190
|
+
* etc.). This avoids a hard dependency on the vault returning a unified HTTP
|
|
191
|
+
* status for consumed-key vs expired-key failures — both should refresh.
|
|
192
|
+
*
|
|
193
|
+
* Deliberately excludes 'network', 'timeout', and 'server' codes (transient
|
|
194
|
+
* errors are already retried in fetchWithRetry) and 'unknown' (too broad).
|
|
195
|
+
*/
|
|
196
|
+
private isRefreshableAuthError;
|
|
197
|
+
/**
|
|
198
|
+
* Re-mints the wax key using the stored fetchWaxKey callback and updates
|
|
199
|
+
* the tokenizer with the new key. Used for transparent auto-refresh when
|
|
200
|
+
* the vault returns an auth error on tokenization.
|
|
201
|
+
*
|
|
202
|
+
* Only one refresh runs at a time — concurrent retries share the same promise.
|
|
203
|
+
*/
|
|
204
|
+
private refreshWaxKey;
|
|
105
205
|
private sendToTokenizer;
|
|
106
206
|
}
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* errorMapping.ts so the same error strings produce the same user-facing copy.
|
|
11
11
|
*/
|
|
12
12
|
export type OzErrorCode = 'network' | 'timeout' | 'auth' | 'validation' | 'server' | 'config' | 'unknown';
|
|
13
|
+
/** Returns true and narrows to OzErrorCode when `value` is a valid member of the union. */
|
|
14
|
+
export declare function isOzErrorCode(value: unknown): value is OzErrorCode;
|
|
13
15
|
export declare class OzError extends Error {
|
|
14
16
|
/** The raw error string returned by the vault or cardSale API, if available. */
|
|
15
17
|
readonly raw: string;
|
|
@@ -52,5 +54,12 @@ export declare function normalizeBankVaultError(raw: string): string;
|
|
|
52
54
|
* Falls back to the original string when it's under 100 characters, or to a
|
|
53
55
|
* generic message for long/opaque server errors — matching checkout's fallback
|
|
54
56
|
* behaviour exactly.
|
|
57
|
+
*
|
|
58
|
+
* **Trade-off:** Short unrecognised strings (e.g. processor codes like
|
|
59
|
+
* `"PROC_TIMEOUT"`) are passed through verbatim. This intentionally mirrors
|
|
60
|
+
* checkout so the same raw Pay API errors produce the same user-facing text on
|
|
61
|
+
* both surfaces. If the Pay API ever returns internal codes that should never
|
|
62
|
+
* reach the UI, the fix belongs in the Pay API error normalisation layer rather
|
|
63
|
+
* than here.
|
|
55
64
|
*/
|
|
56
65
|
export declare function normalizeCardSaleError(raw: string): string;
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
export { OzVault } from './OzVault';
|
|
2
2
|
export { OzElement } from './OzElement';
|
|
3
3
|
export { OzError, normalizeVaultError, normalizeBankVaultError, normalizeCardSaleError } from './errors';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a ready-to-use `fetchWaxKey` callback for `OzVault.create()` and `<OzElements>`.
|
|
6
|
+
*
|
|
7
|
+
* Calls your backend mint endpoint with `{ sessionId }` and returns the wax key string.
|
|
8
|
+
* Throws on non-OK responses or a missing `waxKey` field so the vault can surface the
|
|
9
|
+
* error through its normal error path.
|
|
10
|
+
*
|
|
11
|
+
* Each call enforces a 10-second per-attempt timeout. On a pure network-level
|
|
12
|
+
* failure (connection refused, DNS failure, etc.) the call is retried once after
|
|
13
|
+
* 750ms before throwing. HTTP errors (4xx/5xx) are never retried — they indicate
|
|
14
|
+
* an endpoint misconfiguration or an invalid key, not a transient failure.
|
|
15
|
+
*
|
|
16
|
+
* The mint endpoint is typically the one-line `createMintWaxHandler` / `createMintWaxMiddleware`
|
|
17
|
+
* from `@ozura/elements/server`.
|
|
18
|
+
*
|
|
19
|
+
* @param mintUrl - Absolute or relative URL of your wax-key mint endpoint, e.g. `'/api/mint-wax'`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Vanilla JS
|
|
23
|
+
* const vault = await OzVault.create({
|
|
24
|
+
* pubKey: 'pk_live_...',
|
|
25
|
+
* fetchWaxKey: createFetchWaxKey('/api/mint-wax'),
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // React
|
|
30
|
+
* <OzElements pubKey="pk_live_..." fetchWaxKey={createFetchWaxKey('/api/mint-wax')}>
|
|
31
|
+
*/
|
|
32
|
+
export declare function createFetchWaxKey(mintUrl: string): (sessionId: string) => Promise<string>;
|
|
4
33
|
export type { OzErrorCode } from './errors';
|
|
5
34
|
export type { ElementType, BankElementType, ElementOptions, ElementStyleConfig, ElementStyle, ElementChangeEvent, VaultOptions, TokenizeOptions, BankTokenizeOptions, TokenResponse, BankTokenResponse, CardMetadata, BankAccountMetadata, FontSource, CssFontSource, CustomFontSource, BillingDetails, BillingAddress, CardSaleRequest, CardSaleResponseData, CardSaleApiResponse, Appearance, AppearanceVariables, OzTheme, TransactionQueryParams, TransactionQueryPagination, TransactionQueryResponse, TransactionType, CardTransactionType, AchTransactionType, CryptoTransactionType, TransactionBase, CardTransactionData, AchTransactionData, CryptoTransactionData, TransactionData, } from '../types';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* ```ts
|
|
9
|
-
* import { Ozura } from '
|
|
9
|
+
* import { Ozura } from '@ozura/elements/server';
|
|
10
10
|
*
|
|
11
11
|
* const ozura = new Ozura({
|
|
12
12
|
* merchantId: process.env.MERCHANT_ID!,
|
|
@@ -36,6 +36,11 @@ export interface OzuraConfig {
|
|
|
36
36
|
vaultKey: string;
|
|
37
37
|
/** Ozura Pay API base URL. Defaults to staging. */
|
|
38
38
|
apiUrl?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Ozura Vault base URL. Used for `mintWaxKey` and `revokeWaxKey`.
|
|
41
|
+
* Defaults to the build-time vault URL (staging or production).
|
|
42
|
+
*/
|
|
43
|
+
vaultUrl?: string;
|
|
39
44
|
/** Request timeout in milliseconds. Default: 30000. */
|
|
40
45
|
timeoutMs?: number;
|
|
41
46
|
/** Max retry attempts for 5xx / network errors. Default: 2 (up to 3 total attempts). Set 0 to disable. */
|
|
@@ -47,6 +52,43 @@ export declare class OzuraError extends Error {
|
|
|
47
52
|
readonly retryAfter?: number;
|
|
48
53
|
constructor(message: string, statusCode: number, raw?: string, retryAfter?: number);
|
|
49
54
|
}
|
|
55
|
+
export interface MintWaxKeyResult {
|
|
56
|
+
/** Wax key UUID — pass as the `X-Wax-Key` header on vault tokenize calls. */
|
|
57
|
+
waxKey: string;
|
|
58
|
+
/** Seconds until the wax key expires. Typically 1800 (30 min). */
|
|
59
|
+
expiresInSeconds: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Options for {@link Ozura.mintWaxKey}.
|
|
63
|
+
*/
|
|
64
|
+
export interface MintWaxKeyOptions {
|
|
65
|
+
/**
|
|
66
|
+
* SDK-generated session UUID forwarded from the `fetchWaxKey` callback.
|
|
67
|
+
* Stored by the vault for correlation and audit — not used for authentication.
|
|
68
|
+
*/
|
|
69
|
+
tokenizationSessionId?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Maximum number of tokenize calls this wax key will accept before the vault
|
|
72
|
+
* marks it as consumed. Once consumed, further tokenize attempts return an
|
|
73
|
+
* auth/validation error and the client SDK automatically re-mints a fresh key.
|
|
74
|
+
*
|
|
75
|
+
* Keep this value small (3–5) to limit the blast radius if a key is intercepted.
|
|
76
|
+
* Set the same value in `VaultOptions.maxTokenizeCalls` so the client SDK can
|
|
77
|
+
* proactively refresh before hitting the wall, avoiding a user-visible delay.
|
|
78
|
+
*
|
|
79
|
+
* @default 3
|
|
80
|
+
*/
|
|
81
|
+
maxTokenizeCalls?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Maximum number of proxy calls this wax key will accept.
|
|
84
|
+
* Proxy calls are distinct from tokenize calls — they cover non-tokenize vault
|
|
85
|
+
* operations. Leave `undefined` to use the vault's built-in default.
|
|
86
|
+
*
|
|
87
|
+
* In most integrations you only need `maxTokenizeCalls`; set this only if you
|
|
88
|
+
* are explicitly using vault proxy endpoints.
|
|
89
|
+
*/
|
|
90
|
+
maxProxyCalls?: number;
|
|
91
|
+
}
|
|
50
92
|
export interface CardSaleInput {
|
|
51
93
|
/** From TokenResponse.token (frontend SDK). */
|
|
52
94
|
token: string;
|
|
@@ -89,6 +131,7 @@ export declare class Ozura {
|
|
|
89
131
|
private apiKey;
|
|
90
132
|
private vaultKey;
|
|
91
133
|
private apiUrl;
|
|
134
|
+
private vaultUrl;
|
|
92
135
|
private timeoutMs;
|
|
93
136
|
private retries;
|
|
94
137
|
constructor(config: OzuraConfig);
|
|
@@ -105,6 +148,51 @@ export declare class Ozura {
|
|
|
105
148
|
* Rate limit: 200 requests/minute per merchant.
|
|
106
149
|
*/
|
|
107
150
|
listTransactions(input?: ListTransactionsInput): Promise<ListTransactionsResult>;
|
|
151
|
+
/**
|
|
152
|
+
* Mint a short-lived, use-limited wax key from the vault.
|
|
153
|
+
*
|
|
154
|
+
* Call this server-side to implement the `fetchWaxKey` callback required by
|
|
155
|
+
* `OzVault.create()` on the frontend. The wax key replaces the vault secret
|
|
156
|
+
* on every browser tokenize call — the secret never leaves your server.
|
|
157
|
+
*
|
|
158
|
+
* **Use limits:** by default each wax key accepts up to 3 tokenize calls
|
|
159
|
+
* (`maxTokenizeCalls: 3`). After that the vault marks the key as consumed and
|
|
160
|
+
* the client SDK transparently re-mints. Keep `maxTokenizeCalls` in sync with
|
|
161
|
+
* `VaultOptions.maxTokenizeCalls` so the SDK can proactively refresh before
|
|
162
|
+
* hitting the limit rather than waiting for a rejection.
|
|
163
|
+
*
|
|
164
|
+
* **Session correlation:** the `tokenizationSessionId` forwarded from the SDK's
|
|
165
|
+
* `fetchWaxKey` callback should be passed here so the vault can correlate the
|
|
166
|
+
* key with the checkout session in its audit log.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* // Next.js API route
|
|
170
|
+
* export async function POST(req: Request) {
|
|
171
|
+
* const { sessionId } = await req.json();
|
|
172
|
+
* const { waxKey } = await ozura.mintWaxKey({
|
|
173
|
+
* tokenizationSessionId: sessionId,
|
|
174
|
+
* maxTokenizeCalls: 3,
|
|
175
|
+
* });
|
|
176
|
+
* return Response.json({ waxKey });
|
|
177
|
+
* }
|
|
178
|
+
*/
|
|
179
|
+
mintWaxKey(options?: MintWaxKeyOptions): Promise<MintWaxKeyResult>;
|
|
180
|
+
/**
|
|
181
|
+
* Revoke a previously minted wax key.
|
|
182
|
+
*
|
|
183
|
+
* Best-effort: never throws. Call this when the user's session ends (payment
|
|
184
|
+
* complete, cancelled, or expired) to close the exposure window before the
|
|
185
|
+
* vault TTL (30 min) elapses.
|
|
186
|
+
*
|
|
187
|
+
* - 200 = revoked successfully.
|
|
188
|
+
* - 404 = key already expired or not found — treated as success.
|
|
189
|
+
* - 503 = Redis error; the wax may still be valid. Retry if needed.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* // After a successful card sale:
|
|
193
|
+
* await ozura.revokeWaxKey(waxKey);
|
|
194
|
+
*/
|
|
195
|
+
revokeWaxKey(waxKey: string): Promise<void>;
|
|
108
196
|
/**
|
|
109
197
|
* Execute a fetch with retry on 5xx / network errors.
|
|
110
198
|
* 4xx errors (including 429) are never retried — they require caller action.
|
|
@@ -113,7 +201,72 @@ export declare class Ozura {
|
|
|
113
201
|
private post;
|
|
114
202
|
private getRaw;
|
|
115
203
|
private handleResponse;
|
|
204
|
+
/**
|
|
205
|
+
* Parses a Pay API response JSON and throws `OzuraError` on HTTP errors or
|
|
206
|
+
* `success: false` payloads. Used by both `getRaw` and `handleResponse` to
|
|
207
|
+
* avoid duplicating the error-mapping logic.
|
|
208
|
+
*/
|
|
209
|
+
private parseApiJson;
|
|
210
|
+
}
|
|
211
|
+
/** Minimal response shape required by {@link createMintWaxMiddleware}. */
|
|
212
|
+
interface NodeLikeResponse {
|
|
213
|
+
json(data: unknown): void;
|
|
214
|
+
status(code: number): NodeLikeResponse;
|
|
215
|
+
/** Present on Node/Express `ServerResponse` — used for 429 `Retry-After`. */
|
|
216
|
+
setHeader?(name: string, value: string | number): void;
|
|
116
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Creates a ready-to-use Fetch API route handler for minting wax keys.
|
|
220
|
+
*
|
|
221
|
+
* Drop-in for Next.js App Router, Cloudflare Workers, Vercel Edge, and any
|
|
222
|
+
* runtime built on the standard Web API `Request` / `Response`.
|
|
223
|
+
*
|
|
224
|
+
* The handler reads `sessionId` (or `tokenizationSessionId`) from the JSON
|
|
225
|
+
* request body, calls `ozura.mintWaxKey()`, and returns `{ waxKey }`.
|
|
226
|
+
* On error it returns `{ error }` with an appropriate HTTP status.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* // app/api/mint-wax/route.ts (Next.js App Router)
|
|
230
|
+
* import { Ozura, createMintWaxHandler } from '@ozura/elements/server';
|
|
231
|
+
*
|
|
232
|
+
* const ozura = new Ozura({
|
|
233
|
+
* merchantId: process.env.MERCHANT_ID!,
|
|
234
|
+
* apiKey: process.env.MERCHANT_API_KEY!,
|
|
235
|
+
* vaultKey: process.env.VAULT_API_KEY!,
|
|
236
|
+
* });
|
|
237
|
+
*
|
|
238
|
+
* export const POST = createMintWaxHandler(ozura);
|
|
239
|
+
*/
|
|
240
|
+
export declare function createMintWaxHandler(ozura: Ozura): (req: Request) => Promise<Response>;
|
|
241
|
+
/**
|
|
242
|
+
* Creates a ready-to-use Express / Connect middleware for minting wax keys.
|
|
243
|
+
*
|
|
244
|
+
* Requires `express.json()` (or equivalent body-parser) to be registered
|
|
245
|
+
* before this middleware so `req.body` is available.
|
|
246
|
+
*
|
|
247
|
+
* The middleware reads `sessionId` (or `tokenizationSessionId`) from
|
|
248
|
+
* `req.body`, calls `ozura.mintWaxKey()`, and sends `{ waxKey }`.
|
|
249
|
+
* On error it sends `{ error }` with an appropriate HTTP status.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* // Express
|
|
253
|
+
* import express from 'express';
|
|
254
|
+
* import { Ozura, createMintWaxMiddleware } from '@ozura/elements/server';
|
|
255
|
+
*
|
|
256
|
+
* const ozura = new Ozura({
|
|
257
|
+
* merchantId: process.env.MERCHANT_ID!,
|
|
258
|
+
* apiKey: process.env.MERCHANT_API_KEY!,
|
|
259
|
+
* vaultKey: process.env.VAULT_API_KEY!,
|
|
260
|
+
* });
|
|
261
|
+
*
|
|
262
|
+
* const app = express();
|
|
263
|
+
* app.use(express.json());
|
|
264
|
+
* app.post('/api/mint-wax', createMintWaxMiddleware(ozura));
|
|
265
|
+
*/
|
|
266
|
+
export declare function createMintWaxMiddleware(ozura: Ozura): (req: {
|
|
267
|
+
body?: unknown;
|
|
268
|
+
headers?: unknown;
|
|
269
|
+
}, res: NodeLikeResponse) => Promise<void>;
|
|
117
270
|
/**
|
|
118
271
|
* Extract the client IP address from a server request object.
|
|
119
272
|
* Works with Express (`req.ip`), Fastify (`request.ip`), Next.js App Router
|
|
@@ -123,12 +276,123 @@ export declare class Ozura {
|
|
|
123
276
|
* (Cloudflare) → `x-forwarded-for` (reverse proxy) → `x-real-ip` →
|
|
124
277
|
* `socket.remoteAddress` → `"0.0.0.0"`.
|
|
125
278
|
*
|
|
279
|
+
* **Proxy trust requirements — read before deploying:**
|
|
280
|
+
*
|
|
281
|
+
* - **`x-forwarded-for` / `x-real-ip`** are HTTP headers that any client can
|
|
282
|
+
* set arbitrarily. They are only trustworthy when your server sits behind a
|
|
283
|
+
* reverse proxy (nginx, AWS ALB, Cloudflare, etc.) that strips and rewrites
|
|
284
|
+
* those headers. If your Node.js process is directly internet-accessible,
|
|
285
|
+
* an attacker can spoof any IP value and bypass payment-processor
|
|
286
|
+
* IP-based fraud checks.
|
|
287
|
+
* - **Express `req.ip`** resolves through `X-Forwarded-For` only when
|
|
288
|
+
* `app.set('trust proxy', true)` (or a specific proxy count/subnet) is
|
|
289
|
+
* configured. Without `trust proxy`, `req.ip` returns the direct socket
|
|
290
|
+
* address (your load-balancer's IP, not the client's).
|
|
291
|
+
* - **`cf-connecting-ip`** is only trustworthy when Cloudflare is genuinely
|
|
292
|
+
* in front of your server. Without Cloudflare, any client can send this
|
|
293
|
+
* header with a fabricated value.
|
|
294
|
+
*
|
|
295
|
+
* In all cases, ensure your infrastructure strips untrusted forwarding headers
|
|
296
|
+
* before they reach your application.
|
|
297
|
+
*
|
|
126
298
|
* @example
|
|
127
|
-
* // Express
|
|
299
|
+
* // Express — requires app.set('trust proxy', true) behind a proxy
|
|
128
300
|
* clientIpAddress: getClientIp(req)
|
|
129
301
|
*
|
|
130
302
|
* // Next.js App Router
|
|
131
303
|
* clientIpAddress: getClientIp(req)
|
|
132
304
|
*/
|
|
133
305
|
export declare function getClientIp(req: Record<string, unknown>): string;
|
|
306
|
+
/**
|
|
307
|
+
* Options for {@link createCardSaleHandler} and {@link createCardSaleMiddleware}.
|
|
308
|
+
*
|
|
309
|
+
* The only required option is `getAmount` — it must return the authoritative
|
|
310
|
+
* transaction amount from **your own** database or session. Never read the
|
|
311
|
+
* amount from the request body without validating it against your records;
|
|
312
|
+
* a malicious client could otherwise send an arbitrarily low amount.
|
|
313
|
+
*/
|
|
314
|
+
export interface CardSaleHandlerOptions {
|
|
315
|
+
/**
|
|
316
|
+
* Return the authoritative amount for this transaction as a decimal string
|
|
317
|
+
* (e.g. `"49.00"`). Source this from your own database or session — never
|
|
318
|
+
* trust the value the client sends in the request body.
|
|
319
|
+
*
|
|
320
|
+
* Receives the parsed request body so you can forward an `orderId` or
|
|
321
|
+
* similar identifier to look up the correct amount.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* getAmount: async (body) => {
|
|
325
|
+
* const order = await db.orders.findById(body.orderId as string);
|
|
326
|
+
* return order.total;
|
|
327
|
+
* }
|
|
328
|
+
*/
|
|
329
|
+
getAmount: (body: Record<string, unknown>) => string | Promise<string>;
|
|
330
|
+
/**
|
|
331
|
+
* Return the ISO 4217 currency code for this transaction. Default: `"USD"`.
|
|
332
|
+
*/
|
|
333
|
+
getCurrency?: (body: Record<string, unknown>) => string | Promise<string>;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Creates a ready-to-use Fetch API route handler for charging a tokenized card.
|
|
337
|
+
*
|
|
338
|
+
* Drop-in for Next.js App Router, Cloudflare Workers, Vercel Edge, and any
|
|
339
|
+
* runtime built on the standard Web API `Request` / `Response`.
|
|
340
|
+
*
|
|
341
|
+
* The handler reads `{ token, cvcSession, billing }` from the JSON request body,
|
|
342
|
+
* resolves the amount via `options.getAmount()`, calls `ozura.cardSale()`, and
|
|
343
|
+
* returns `{ transactionId, amount, cardLastFour, cardBrand }` on success.
|
|
344
|
+
* On error it returns `{ error }` with a normalized, user-facing message and
|
|
345
|
+
* an appropriate HTTP status.
|
|
346
|
+
*
|
|
347
|
+
* `clientIpAddress` is extracted automatically from the request headers.
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* // app/api/charge/route.ts (Next.js App Router)
|
|
351
|
+
* import { Ozura, createCardSaleHandler } from '@ozura/elements/server';
|
|
352
|
+
*
|
|
353
|
+
* const ozura = new Ozura({ merchantId: '...', apiKey: '...', vaultKey: '...' });
|
|
354
|
+
*
|
|
355
|
+
* export const POST = createCardSaleHandler(ozura, {
|
|
356
|
+
* getAmount: async (body) => {
|
|
357
|
+
* const order = await db.orders.findById(body.orderId as string);
|
|
358
|
+
* return order.total;
|
|
359
|
+
* },
|
|
360
|
+
* });
|
|
361
|
+
*/
|
|
362
|
+
export declare function createCardSaleHandler(ozura: Ozura, options: CardSaleHandlerOptions): (req: Request) => Promise<Response>;
|
|
363
|
+
/**
|
|
364
|
+
* Creates a ready-to-use Express / Connect middleware for charging a tokenized card.
|
|
365
|
+
*
|
|
366
|
+
* Requires `express.json()` (or equivalent body-parser) to be registered before
|
|
367
|
+
* this middleware so `req.body` is available.
|
|
368
|
+
*
|
|
369
|
+
* The middleware reads `{ token, cvcSession, billing }` from `req.body`, resolves
|
|
370
|
+
* the amount via `options.getAmount()`, calls `ozura.cardSale()`, and sends
|
|
371
|
+
* `{ transactionId, amount, cardLastFour, cardBrand }` on success.
|
|
372
|
+
* On error it sends `{ error }` with a normalized, user-facing message and an
|
|
373
|
+
* appropriate HTTP status.
|
|
374
|
+
*
|
|
375
|
+
* `clientIpAddress` is extracted automatically from the request object.
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* // Express
|
|
379
|
+
* import express from 'express';
|
|
380
|
+
* import { Ozura, createCardSaleMiddleware } from '@ozura/elements/server';
|
|
381
|
+
*
|
|
382
|
+
* const app = express();
|
|
383
|
+
* const ozura = new Ozura({ merchantId: '...', apiKey: '...', vaultKey: '...' });
|
|
384
|
+
*
|
|
385
|
+
* app.use(express.json());
|
|
386
|
+
* app.post('/api/charge', createCardSaleMiddleware(ozura, {
|
|
387
|
+
* getAmount: async (body) => {
|
|
388
|
+
* const order = await db.orders.findById(body.orderId as string);
|
|
389
|
+
* return order.total;
|
|
390
|
+
* },
|
|
391
|
+
* }));
|
|
392
|
+
*/
|
|
393
|
+
export declare function createCardSaleMiddleware(ozura: Ozura, options: CardSaleHandlerOptions): (req: {
|
|
394
|
+
body?: unknown;
|
|
395
|
+
headers?: unknown;
|
|
396
|
+
}, res: NodeLikeResponse) => Promise<void>;
|
|
134
397
|
export type { BillingDetails, CardSaleResponseData, TransactionQueryPagination, TransactionType, TransactionBase, CardTransactionData, AchTransactionData, CryptoTransactionData, TransactionData, } from '../types';
|
|
398
|
+
export { normalizeCardSaleError } from '../sdk/errors';
|