@ozura/elements 0.1.0-beta.1

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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +717 -0
  3. package/dist/frame/element-frame.html +22 -0
  4. package/dist/frame/element-frame.js +731 -0
  5. package/dist/frame/element-frame.js.map +1 -0
  6. package/dist/frame/tokenizer-frame.html +11 -0
  7. package/dist/frame/tokenizer-frame.js +328 -0
  8. package/dist/frame/tokenizer-frame.js.map +1 -0
  9. package/dist/oz-elements.esm.js +1190 -0
  10. package/dist/oz-elements.esm.js.map +1 -0
  11. package/dist/oz-elements.umd.js +1202 -0
  12. package/dist/oz-elements.umd.js.map +1 -0
  13. package/dist/react/frame/elementFrame.d.ts +8 -0
  14. package/dist/react/frame/tokenizerFrame.d.ts +13 -0
  15. package/dist/react/index.cjs.js +1407 -0
  16. package/dist/react/index.cjs.js.map +1 -0
  17. package/dist/react/index.esm.js +1400 -0
  18. package/dist/react/index.esm.js.map +1 -0
  19. package/dist/react/react/index.d.ts +214 -0
  20. package/dist/react/sdk/OzElement.d.ts +65 -0
  21. package/dist/react/sdk/OzVault.d.ts +106 -0
  22. package/dist/react/sdk/errors.d.ts +55 -0
  23. package/dist/react/sdk/index.d.ts +5 -0
  24. package/dist/react/server/index.d.ts +140 -0
  25. package/dist/react/types/index.d.ts +432 -0
  26. package/dist/react/utils/appearance.d.ts +13 -0
  27. package/dist/react/utils/billingUtils.d.ts +60 -0
  28. package/dist/react/utils/cardUtils.d.ts +37 -0
  29. package/dist/server/frame/elementFrame.d.ts +8 -0
  30. package/dist/server/frame/tokenizerFrame.d.ts +13 -0
  31. package/dist/server/index.cjs.js +294 -0
  32. package/dist/server/index.cjs.js.map +1 -0
  33. package/dist/server/index.esm.js +290 -0
  34. package/dist/server/index.esm.js.map +1 -0
  35. package/dist/server/sdk/OzElement.d.ts +65 -0
  36. package/dist/server/sdk/OzVault.d.ts +106 -0
  37. package/dist/server/sdk/errors.d.ts +55 -0
  38. package/dist/server/sdk/index.d.ts +5 -0
  39. package/dist/server/server/index.d.ts +140 -0
  40. package/dist/server/types/index.d.ts +432 -0
  41. package/dist/server/utils/appearance.d.ts +13 -0
  42. package/dist/server/utils/billingUtils.d.ts +60 -0
  43. package/dist/server/utils/cardUtils.d.ts +37 -0
  44. package/dist/types/frame/elementFrame.d.ts +8 -0
  45. package/dist/types/frame/tokenizerFrame.d.ts +13 -0
  46. package/dist/types/sdk/OzElement.d.ts +65 -0
  47. package/dist/types/sdk/OzVault.d.ts +106 -0
  48. package/dist/types/sdk/errors.d.ts +55 -0
  49. package/dist/types/sdk/index.d.ts +5 -0
  50. package/dist/types/server/index.d.ts +140 -0
  51. package/dist/types/types/index.d.ts +432 -0
  52. package/dist/types/utils/appearance.d.ts +13 -0
  53. package/dist/types/utils/billingUtils.d.ts +60 -0
  54. package/dist/types/utils/cardUtils.d.ts +37 -0
  55. package/package.json +97 -0
@@ -0,0 +1,65 @@
1
+ import { ElementType, BankElementType, ElementOptions, ElementStyleConfig, FontSource, OzMessage } from '../types';
2
+ type AnyElementType = ElementType | BankElementType;
3
+ type ElementEvent = 'ready' | 'change' | 'focus' | 'blur' | 'loaderror';
4
+ type ElementCallback = (payload?: unknown) => void;
5
+ /**
6
+ * A proxy for one Ozura iframe element. Merchants interact with this object;
7
+ * it never holds raw card data — all sensitive values live in the iframe.
8
+ */
9
+ export declare class OzElement {
10
+ private elementType;
11
+ private options;
12
+ private vaultId;
13
+ readonly frameId: string;
14
+ private frameBaseUrl;
15
+ private frameOrigin;
16
+ private fonts;
17
+ private appearanceStyle;
18
+ private iframe;
19
+ private _frameWindow;
20
+ private _ready;
21
+ private _destroyed;
22
+ private _loadTimer;
23
+ private pendingMessages;
24
+ private handlers;
25
+ constructor(elementType: AnyElementType, options: ElementOptions, vaultId: string, frameBaseUrl: string, fonts?: FontSource[], appearanceStyle?: ElementStyleConfig);
26
+ /** The element type this proxy represents. */
27
+ get type(): AnyElementType;
28
+ /** True once the element iframe has loaded and signalled ready. */
29
+ get isReady(): boolean;
30
+ /**
31
+ * Mounts the element iframe into a container.
32
+ * Accepts either a CSS selector string or a direct HTMLElement reference
33
+ * (useful when integrating with React refs).
34
+ */
35
+ mount(target: string | HTMLElement): void;
36
+ on(event: ElementEvent, callback: ElementCallback): this;
37
+ off(event: ElementEvent, callback: ElementCallback): this;
38
+ once(event: ElementEvent, callback: ElementCallback): this;
39
+ update(options: Partial<ElementOptions>): void;
40
+ clear(): void;
41
+ /**
42
+ * Removes the iframe from the DOM and resets internal state.
43
+ * Called automatically by `OzVault.destroy()`. Safe to call manually
44
+ * for partial teardown (e.g. swapping payment method in a SPA).
45
+ */
46
+ unmount(): void;
47
+ /** Programmatically focus this element's input. Used internally for auto-advance. */
48
+ focus(): void;
49
+ /** Programmatically blur this element's input. */
50
+ blur(): void;
51
+ /**
52
+ * Permanently destroys this element: unmounts it, clears all event handlers,
53
+ * and prevents future use. Distinct from `unmount()` which allows re-mounting.
54
+ */
55
+ destroy(): void;
56
+ setTokenizerName(tokenizerName: string): void;
57
+ beginCollect(requestId: string): void;
58
+ /** Tell a CVV element how many digits to expect. Called automatically when card brand changes. */
59
+ setCvvLength(length: number): void;
60
+ handleMessage(msg: OzMessage): void;
61
+ private emit;
62
+ private post;
63
+ private send;
64
+ }
65
+ export {};
@@ -0,0 +1,106 @@
1
+ import { OzElement } from './OzElement';
2
+ import { ElementType, BankElementType, ElementOptions, VaultOptions, TokenizeOptions, TokenResponse, BankTokenizeOptions, BankTokenResponse } from '../types';
3
+ /**
4
+ * The main entry point for OzElements. Creates and manages iframe-based
5
+ * card input elements that keep raw card data isolated from the merchant page.
6
+ *
7
+ * @example
8
+ * const vault = new OzVault('your_vault_api_key');
9
+ * const cardNum = vault.createElement('cardNumber');
10
+ * cardNum.mount('#card-number');
11
+ * const { token, cvcSession } = await vault.createToken({
12
+ * billing: { firstName: 'Jane', lastName: 'Doe' },
13
+ * });
14
+ */
15
+ export declare class OzVault {
16
+ private apiKey;
17
+ private pubKey;
18
+ private apiUrl;
19
+ private frameBaseUrl;
20
+ private frameOrigin;
21
+ private fonts;
22
+ private resolvedAppearance;
23
+ readonly vaultId: string;
24
+ private elements;
25
+ private elementsByType;
26
+ private bankElementsByType;
27
+ private tokenizeResolvers;
28
+ private bankTokenizeResolvers;
29
+ private completionState;
30
+ private tokenizerFrame;
31
+ private tokenizerWindow;
32
+ private tokenizerName;
33
+ private tokenizerReady;
34
+ private _tokenizing;
35
+ private _destroyed;
36
+ private boundHandleMessage;
37
+ private _pendingMount;
38
+ private loadErrorTimeoutId;
39
+ constructor(apiKey: string, options: VaultOptions);
40
+ /**
41
+ * True once the hidden tokenizer iframe has loaded and signalled ready.
42
+ * Use this to gate the pay button when building custom UIs without React.
43
+ * React consumers should use the `ready` value returned by `useOzElements()`.
44
+ */
45
+ get isReady(): boolean;
46
+ /**
47
+ * Creates a new OzElement of the given type. Call `.mount(selector)` on the
48
+ * returned element to attach it to the DOM.
49
+ */
50
+ createElement(type: ElementType, options?: ElementOptions): OzElement;
51
+ /** Returns the existing element of the given type, or null if none has been created. */
52
+ getElement(type: ElementType): OzElement | null;
53
+ /**
54
+ * Creates a bank account input element (accountNumber or routingNumber).
55
+ * Call `.mount(selector)` on the returned element to attach it to the DOM.
56
+ *
57
+ * @example
58
+ * const accountEl = vault.createBankElement('accountNumber');
59
+ * const routingEl = vault.createBankElement('routingNumber');
60
+ * accountEl.mount('#account-number');
61
+ * routingEl.mount('#routing-number');
62
+ */
63
+ createBankElement(type: BankElementType, options?: ElementOptions): OzElement;
64
+ /** Returns the existing bank element of the given type, or null if none has been created. */
65
+ getBankElement(type: BankElementType): OzElement | null;
66
+ /**
67
+ * Tokenizes mounted bank account elements. Raw account and routing numbers
68
+ * never leave the Ozura-origin iframes — the tokenizer iframe POSTs directly
69
+ * to the vault API.
70
+ *
71
+ * Returns a token that can be used with any ACH-capable payment processor.
72
+ *
73
+ * **Note:** OzuraPay does not currently support bank account payments.
74
+ * Use this token with your own ACH processor backend.
75
+ *
76
+ * @example
77
+ * const { token, bank } = await vault.createBankToken({
78
+ * firstName: 'Jane',
79
+ * lastName: 'Smith',
80
+ * });
81
+ */
82
+ createBankToken(options: BankTokenizeOptions): Promise<BankTokenResponse>;
83
+ /**
84
+ * Tokenizes all mounted elements. Raw card data never leaves the Ozura-origin
85
+ * iframes — the tokenizer iframe POSTs directly to the vault API.
86
+ *
87
+ * Returns a token and cvcSession that can be passed to the Ozura Pay API.
88
+ */
89
+ createToken(options?: TokenizeOptions): Promise<TokenResponse>;
90
+ /**
91
+ * Tears down the vault: removes all element iframes, the tokenizer iframe,
92
+ * and the global message listener. Call this when the checkout component
93
+ * unmounts (e.g. in React's useEffect cleanup or a SPA route change).
94
+ */
95
+ destroy(): void;
96
+ private mountTokenizerFrame;
97
+ private handleMessage;
98
+ /**
99
+ * Handles side-effects that the SDK manages internally when a field changes:
100
+ * - CVV length sync when card brand changes
101
+ * - Auto-advance focus when a field completes
102
+ */
103
+ private handleElementChange;
104
+ private handleTokenizerMessage;
105
+ private sendToTokenizer;
106
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * errors.ts — error types and normalisation for OzElements.
3
+ *
4
+ * Two normalisation functions:
5
+ * - normalizeVaultError — maps raw vault /tokenize errors to user-facing messages
6
+ * - normalizeCardSaleError — maps raw cardSale API errors to user-facing messages
7
+ *
8
+ * Error keys in normalizeCardSaleError are taken directly from checkout's
9
+ * errorMapping.ts so the same error strings produce the same user-facing copy.
10
+ */
11
+ export type OzErrorCode = 'network' | 'timeout' | 'auth' | 'validation' | 'server' | 'config' | 'unknown';
12
+ export declare class OzError extends Error {
13
+ /** The raw error string returned by the vault or cardSale API, if available. */
14
+ readonly raw: string;
15
+ /**
16
+ * Structured error code classifying the failure.
17
+ * - `network` — connection failure or DNS resolution error
18
+ * - `timeout` — the request exceeded the 30s deadline
19
+ * - `auth` — 401/403 from the vault (bad API key or pub key)
20
+ * - `validation` — 400 from the vault (bad card data, missing fields)
21
+ * - `server` — 5xx from the vault (transient backend issue)
22
+ * - `config` — vault API URL is not in the permitted allowlist (SDK misconfiguration)
23
+ * - `unknown` — unclassifiable error
24
+ */
25
+ readonly errorCode: OzErrorCode;
26
+ /**
27
+ * `true` when the error is likely transient and worth retrying
28
+ * (`network`, `timeout`, `server`). `false` for permanent failures
29
+ * (`auth`, `validation`) where retrying with the same input won't help.
30
+ */
31
+ readonly retryable: boolean;
32
+ constructor(message: string, raw?: string, errorCode?: OzErrorCode);
33
+ }
34
+ /**
35
+ * Maps a raw vault /tokenize error string to a user-facing message for card flows.
36
+ * Falls back to the original string if no pattern matches.
37
+ */
38
+ export declare function normalizeVaultError(raw: string): string;
39
+ /**
40
+ * Maps a raw vault /tokenize error string to a user-facing message for bank (ACH) flows.
41
+ * Uses bank-specific pattern matching so card-specific messages are never shown for
42
+ * bank errors. Falls back to the original string if no pattern matches.
43
+ */
44
+ export declare function normalizeBankVaultError(raw: string): string;
45
+ /**
46
+ * Maps a raw Ozura Pay API cardSale error string to a user-facing message.
47
+ *
48
+ * Uses the exact same error key table as checkout's `getUserFriendlyError()` in
49
+ * errorMapping.ts so both surfaces produce identical copy for the same errors.
50
+ *
51
+ * Falls back to the original string when it's under 100 characters, or to a
52
+ * generic message for long/opaque server errors — matching checkout's fallback
53
+ * behaviour exactly.
54
+ */
55
+ export declare function normalizeCardSaleError(raw: string): string;
@@ -0,0 +1,5 @@
1
+ export { OzVault } from './OzVault';
2
+ export { OzElement } from './OzElement';
3
+ export { OzError, normalizeVaultError, normalizeCardSaleError } from './errors';
4
+ export type { OzErrorCode } from './errors';
5
+ 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, } from '../types';
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @ozura/server — Server-side SDK for the Ozura Pay API.
3
+ *
4
+ * Wraps cardSale and transaction query endpoints with typed methods,
5
+ * automatic field mapping, retry logic, and error handling.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { Ozura } from 'oz-elements/server';
10
+ *
11
+ * const ozura = new Ozura({
12
+ * merchantId: process.env.MERCHANT_ID!,
13
+ * apiKey: process.env.MERCHANT_API_KEY!,
14
+ * vaultKey: process.env.VAULT_API_KEY!,
15
+ * });
16
+ *
17
+ * const result = await ozura.cardSale({
18
+ * token: tokenResponse.token,
19
+ * cvcSession: tokenResponse.cvcSession,
20
+ * amount: '49.00',
21
+ * currency: 'USD',
22
+ * billing: tokenResponse.billing,
23
+ * clientIpAddress: req.ip,
24
+ * });
25
+ *
26
+ * console.log(result.transactionId);
27
+ * ```
28
+ */
29
+ import type { BillingDetails, CardSaleResponseData, TransactionQueryPagination } from '../types';
30
+ export interface OzuraConfig {
31
+ /** Your Ozura merchant ID (e.g. "ozu_..."). */
32
+ merchantId: string;
33
+ /** Merchant Pay API key — sent as x-api-key header. */
34
+ apiKey: string;
35
+ /** Vault API key — same key used in OzVault on the frontend. */
36
+ vaultKey: string;
37
+ /** Ozura Pay API base URL. Defaults to staging. */
38
+ apiUrl?: string;
39
+ /** Request timeout in milliseconds. Default: 30000. */
40
+ timeoutMs?: number;
41
+ /** Max retry attempts for 5xx / network errors. Default: 2 (up to 3 total attempts). Set 0 to disable. */
42
+ retries?: number;
43
+ }
44
+ export declare class OzuraError extends Error {
45
+ readonly statusCode: number;
46
+ readonly raw: string;
47
+ readonly retryAfter?: number;
48
+ constructor(message: string, statusCode: number, raw?: string, retryAfter?: number);
49
+ }
50
+ export interface CardSaleInput {
51
+ /** From TokenResponse.token (frontend SDK). */
52
+ token: string;
53
+ /** From TokenResponse.cvcSession (frontend SDK). */
54
+ cvcSession: string;
55
+ /** Decimal string, e.g. "49.00". */
56
+ amount: string;
57
+ /** ISO 4217, e.g. "USD". Default: "USD". */
58
+ currency?: string;
59
+ /** Billing from TokenResponse.billing, or assemble manually. */
60
+ billing: BillingDetails;
61
+ /** Client IP address — fetch server-side, never from the browser. */
62
+ clientIpAddress: string;
63
+ salesTaxExempt?: boolean;
64
+ /** Processor to use. If omitted, the Pay API auto-selects. */
65
+ processor?: 'elavon' | 'nuvei' | 'worldpay';
66
+ /** Defaults to "0.00". */
67
+ surchargePercent?: string;
68
+ /** Defaults to "0.00". */
69
+ tipAmount?: string;
70
+ }
71
+ export interface ListTransactionsInput {
72
+ dateFrom?: string;
73
+ dateTo?: string;
74
+ transactionType?: string;
75
+ page?: number;
76
+ limit?: number;
77
+ fields?: string;
78
+ sortBy?: string;
79
+ sortOrder?: 'asc' | 'desc';
80
+ }
81
+ export interface ListTransactionsResult {
82
+ transactions: CardSaleResponseData[];
83
+ pagination: TransactionQueryPagination;
84
+ }
85
+ export declare class Ozura {
86
+ private merchantId;
87
+ private apiKey;
88
+ private vaultKey;
89
+ private apiUrl;
90
+ private timeoutMs;
91
+ private retries;
92
+ constructor(config: OzuraConfig);
93
+ /**
94
+ * Charge a tokenized card. Maps the friendly `CardSaleInput` shape to the
95
+ * Pay API's flat field format and handles headers, defaults, and errors.
96
+ *
97
+ * Rate limit: 100 requests/minute per merchant.
98
+ */
99
+ cardSale(input: CardSaleInput): Promise<CardSaleResponseData>;
100
+ /**
101
+ * Retrieve a single transaction by ID.
102
+ *
103
+ * **Known issue:** The Pay API requires the API key as a query parameter
104
+ * (`access_token`), which means it can appear in server logs, CDN caches,
105
+ * and HTTP Referer headers. This is a Pay API design issue — the SDK will
106
+ * switch to header-based auth once the backend supports it.
107
+ *
108
+ * Rate limit: 100 requests/minute per merchant.
109
+ */
110
+ getTransaction(transactionId: string): Promise<CardSaleResponseData>;
111
+ /**
112
+ * List transactions by date range with pagination.
113
+ *
114
+ * Rate limit: 200 requests/minute per merchant.
115
+ */
116
+ listTransactions(input?: ListTransactionsInput): Promise<ListTransactionsResult>;
117
+ /**
118
+ * Execute a fetch with retry on 5xx / network errors.
119
+ * 4xx errors (including 429) are never retried — they require caller action.
120
+ */
121
+ private fetchWithRetry;
122
+ private post;
123
+ private get;
124
+ private getRaw;
125
+ private handleResponse;
126
+ }
127
+ /**
128
+ * Extract the client IP address from a server request object.
129
+ * Works with Express (`req.ip`), Fastify (`request.ip`), Next.js App Router
130
+ * (`req.headers.get('x-forwarded-for')`), and raw Node.js `IncomingMessage`.
131
+ *
132
+ * @example
133
+ * // Express / Fastify
134
+ * clientIpAddress: getClientIp(req)
135
+ *
136
+ * // Next.js App Router
137
+ * clientIpAddress: getClientIp(req)
138
+ */
139
+ export declare function getClientIp(req: Record<string, unknown>): string;
140
+ export type { BillingDetails, CardSaleResponseData, TransactionQueryPagination, } from '../types';