@ozura/elements 1.2.1 → 1.2.4-next.47
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/LICENSE +21 -21
- package/README.md +1445 -1340
- package/dist/frame/element-frame.html +22 -22
- package/dist/frame/element-frame.js +49 -4
- package/dist/frame/element-frame.js.map +1 -1
- package/dist/frame/tokenizer-frame.html +11 -11
- package/dist/frame/tokenizer-frame.js +147 -63
- package/dist/frame/tokenizer-frame.js.map +1 -1
- package/dist/oz-elements.esm.js +222 -98
- package/dist/oz-elements.esm.js.map +1 -1
- package/dist/oz-elements.umd.js +222 -98
- package/dist/oz-elements.umd.js.map +1 -1
- package/dist/react/frame/elementFrame.d.ts +9 -0
- package/dist/react/frame/tokenizerFrame.d.ts +17 -1
- package/dist/react/index.cjs.js +222 -98
- package/dist/react/index.cjs.js.map +1 -1
- package/dist/react/index.esm.js +222 -98
- package/dist/react/index.esm.js.map +1 -1
- package/dist/react/sdk/OzElement.d.ts +4 -1
- package/dist/react/sdk/OzVault.d.ts +3 -9
- package/dist/react/sdk/createSessionFetcher.d.ts +2 -2
- package/dist/react/server/index.d.ts +26 -0
- package/dist/react/types/index.d.ts +30 -11
- package/dist/react/utils/billingUtils.d.ts +2 -1
- package/dist/react/vue/index.d.ts +256 -0
- package/dist/server/frame/elementFrame.d.ts +9 -0
- package/dist/server/frame/tokenizerFrame.d.ts +17 -1
- package/dist/server/index.cjs.js +67 -26
- package/dist/server/index.cjs.js.map +1 -1
- package/dist/server/index.esm.js +67 -26
- package/dist/server/index.esm.js.map +1 -1
- package/dist/server/sdk/OzElement.d.ts +4 -1
- package/dist/server/sdk/OzVault.d.ts +3 -9
- package/dist/server/sdk/createSessionFetcher.d.ts +2 -2
- package/dist/server/server/index.d.ts +26 -0
- package/dist/server/types/index.d.ts +30 -11
- package/dist/server/utils/billingUtils.d.ts +2 -1
- package/dist/server/vue/index.d.ts +256 -0
- package/dist/types/frame/elementFrame.d.ts +9 -0
- package/dist/types/frame/tokenizerFrame.d.ts +17 -1
- package/dist/types/sdk/OzElement.d.ts +4 -1
- package/dist/types/sdk/OzVault.d.ts +3 -9
- package/dist/types/sdk/createSessionFetcher.d.ts +2 -2
- package/dist/types/server/index.d.ts +26 -0
- package/dist/types/types/index.d.ts +30 -11
- package/dist/types/utils/billingUtils.d.ts +2 -1
- package/dist/types/vue/index.d.ts +256 -0
- package/dist/vue/frame/protocol.d.ts +12 -0
- package/dist/vue/index.cjs.js +2335 -0
- package/dist/vue/index.cjs.js.map +1 -0
- package/dist/vue/index.esm.js +2327 -0
- package/dist/vue/index.esm.js.map +1 -0
- package/dist/vue/sdk/OzElement.d.ts +99 -0
- package/dist/vue/sdk/OzVault.d.ts +250 -0
- package/dist/vue/sdk/createSessionFetcher.d.ts +29 -0
- package/dist/vue/sdk/errors.d.ts +65 -0
- package/dist/vue/sdk/index.d.ts +14 -0
- package/dist/vue/types/index.d.ts +667 -0
- package/dist/vue/utils/appearance.d.ts +22 -0
- package/dist/vue/utils/billingUtils.d.ts +61 -0
- package/dist/vue/utils/uuid.d.ts +12 -0
- package/dist/vue/vue/index.d.ts +256 -0
- package/package.json +15 -3
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ozura/elements/vue — Vue 3 wrapper for OzElements.
|
|
3
|
+
*
|
|
4
|
+
* Provides a context-based provider that creates and manages an OzVault instance,
|
|
5
|
+
* five individual field components, and a composable to access createToken +
|
|
6
|
+
* readiness state from anywhere inside the provider tree.
|
|
7
|
+
*
|
|
8
|
+
* Quick start:
|
|
9
|
+
* @example
|
|
10
|
+
* ```vue
|
|
11
|
+
* <script setup lang="ts">
|
|
12
|
+
* import { OzElements, OzCardNumber, OzExpiry, OzCvv, useOzElements } from '@ozura/elements/vue';
|
|
13
|
+
* const { createToken, ready } = useOzElements();
|
|
14
|
+
* </script>
|
|
15
|
+
*
|
|
16
|
+
* <template>
|
|
17
|
+
* <OzElements pub-key="pk_live_..." session-url="/api/oz-session">
|
|
18
|
+
* <OzCardNumber />
|
|
19
|
+
* <OzExpiry />
|
|
20
|
+
* <OzCvv />
|
|
21
|
+
* <button :disabled="!ready" @click="createToken()">Pay</button>
|
|
22
|
+
* </OzElements>
|
|
23
|
+
* </template>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import { type Ref, type PropType, type ComputedRef } from 'vue';
|
|
27
|
+
import type { TokenizeOptions, TokenResponse, BankTokenizeOptions, BankTokenResponse, FontSource, Appearance } from '../types';
|
|
28
|
+
export interface OzElementsProps {
|
|
29
|
+
pubKey: string;
|
|
30
|
+
sessionUrl?: string;
|
|
31
|
+
getSessionKey?: (sessionId: string) => Promise<string>;
|
|
32
|
+
frameBaseUrl?: string;
|
|
33
|
+
fonts?: FontSource[];
|
|
34
|
+
appearance?: Appearance;
|
|
35
|
+
loadTimeoutMs?: number;
|
|
36
|
+
debug?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Creates and owns an OzVault instance for the lifetime of this component.
|
|
40
|
+
* All field components must be rendered inside this provider.
|
|
41
|
+
*/
|
|
42
|
+
export declare const OzElements: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
43
|
+
pubKey: {
|
|
44
|
+
type: StringConstructor;
|
|
45
|
+
required: true;
|
|
46
|
+
};
|
|
47
|
+
sessionUrl: {
|
|
48
|
+
type: StringConstructor;
|
|
49
|
+
default: undefined;
|
|
50
|
+
};
|
|
51
|
+
getSessionKey: {
|
|
52
|
+
type: PropType<(sessionId: string) => Promise<string>>;
|
|
53
|
+
default: undefined;
|
|
54
|
+
};
|
|
55
|
+
frameBaseUrl: {
|
|
56
|
+
type: StringConstructor;
|
|
57
|
+
default: undefined;
|
|
58
|
+
};
|
|
59
|
+
fonts: {
|
|
60
|
+
type: PropType<FontSource[]>;
|
|
61
|
+
default: undefined;
|
|
62
|
+
};
|
|
63
|
+
appearance: {
|
|
64
|
+
type: PropType<Appearance>;
|
|
65
|
+
default: undefined;
|
|
66
|
+
};
|
|
67
|
+
loadTimeoutMs: {
|
|
68
|
+
type: NumberConstructor;
|
|
69
|
+
default: undefined;
|
|
70
|
+
};
|
|
71
|
+
debug: {
|
|
72
|
+
type: BooleanConstructor;
|
|
73
|
+
default: undefined;
|
|
74
|
+
};
|
|
75
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
76
|
+
[key: string]: any;
|
|
77
|
+
}>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "ready"[], "ready", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
78
|
+
pubKey: {
|
|
79
|
+
type: StringConstructor;
|
|
80
|
+
required: true;
|
|
81
|
+
};
|
|
82
|
+
sessionUrl: {
|
|
83
|
+
type: StringConstructor;
|
|
84
|
+
default: undefined;
|
|
85
|
+
};
|
|
86
|
+
getSessionKey: {
|
|
87
|
+
type: PropType<(sessionId: string) => Promise<string>>;
|
|
88
|
+
default: undefined;
|
|
89
|
+
};
|
|
90
|
+
frameBaseUrl: {
|
|
91
|
+
type: StringConstructor;
|
|
92
|
+
default: undefined;
|
|
93
|
+
};
|
|
94
|
+
fonts: {
|
|
95
|
+
type: PropType<FontSource[]>;
|
|
96
|
+
default: undefined;
|
|
97
|
+
};
|
|
98
|
+
appearance: {
|
|
99
|
+
type: PropType<Appearance>;
|
|
100
|
+
default: undefined;
|
|
101
|
+
};
|
|
102
|
+
loadTimeoutMs: {
|
|
103
|
+
type: NumberConstructor;
|
|
104
|
+
default: undefined;
|
|
105
|
+
};
|
|
106
|
+
debug: {
|
|
107
|
+
type: BooleanConstructor;
|
|
108
|
+
default: undefined;
|
|
109
|
+
};
|
|
110
|
+
}>> & Readonly<{
|
|
111
|
+
onReady?: ((...args: any[]) => any) | undefined;
|
|
112
|
+
}>, {
|
|
113
|
+
debug: boolean;
|
|
114
|
+
fonts: FontSource[];
|
|
115
|
+
loadTimeoutMs: number;
|
|
116
|
+
frameBaseUrl: string;
|
|
117
|
+
sessionUrl: string;
|
|
118
|
+
getSessionKey: (sessionId: string) => Promise<string>;
|
|
119
|
+
appearance: Appearance;
|
|
120
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
121
|
+
export interface UseOzElementsReturn {
|
|
122
|
+
/**
|
|
123
|
+
* Tokenizes all mounted card fields. Resolves with a token and cvcSession
|
|
124
|
+
* that can be passed to the Ozura Pay API endpoint.
|
|
125
|
+
*/
|
|
126
|
+
createToken: (options?: TokenizeOptions) => Promise<TokenResponse>;
|
|
127
|
+
/**
|
|
128
|
+
* Tokenizes mounted bank account fields (accountNumber + routingNumber).
|
|
129
|
+
*/
|
|
130
|
+
createBankToken: (options: BankTokenizeOptions) => Promise<BankTokenResponse>;
|
|
131
|
+
/**
|
|
132
|
+
* True when the vault tokenizer and all mounted field iframes are ready.
|
|
133
|
+
* Gate your submit button on this.
|
|
134
|
+
*/
|
|
135
|
+
ready: ComputedRef<boolean>;
|
|
136
|
+
/**
|
|
137
|
+
* Non-null when OzVault.create() rejected during initialization.
|
|
138
|
+
*/
|
|
139
|
+
initError: Ref<Error | null>;
|
|
140
|
+
/**
|
|
141
|
+
* Number of successful tokenizations since the last session refresh.
|
|
142
|
+
*/
|
|
143
|
+
tokenizeCount: Ref<number>;
|
|
144
|
+
/**
|
|
145
|
+
* Clears all mounted element fields without destroying the vault.
|
|
146
|
+
*/
|
|
147
|
+
reset: () => void;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Returns createToken, createBankToken, ready, initError, tokenizeCount, and reset.
|
|
151
|
+
* Must be called from inside an <OzElements> provider tree.
|
|
152
|
+
*
|
|
153
|
+
* @throws {Error} if called outside an <OzElements> provider
|
|
154
|
+
*/
|
|
155
|
+
export declare function useOzElements(): UseOzElementsReturn;
|
|
156
|
+
/** Card number field. Emits `change` (ElementChangeEvent), `focus`, `blur`. */
|
|
157
|
+
export declare const OzCardNumber: import("vue").DefineComponent<{
|
|
158
|
+
placeholder?: string | undefined;
|
|
159
|
+
disabled?: boolean | undefined;
|
|
160
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
161
|
+
}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
162
|
+
[key: string]: any;
|
|
163
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("blur" | "change" | "focus")[], "blur" | "change" | "focus", import("vue").PublicProps, Readonly<{
|
|
164
|
+
placeholder?: string | undefined;
|
|
165
|
+
disabled?: boolean | undefined;
|
|
166
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
167
|
+
}> & Readonly<{
|
|
168
|
+
onChange?: ((...args: any[]) => any) | undefined;
|
|
169
|
+
onFocus?: ((...args: any[]) => any) | undefined;
|
|
170
|
+
onBlur?: ((...args: any[]) => any) | undefined;
|
|
171
|
+
}>, {
|
|
172
|
+
style: import("../types").ElementStyleConfig | undefined;
|
|
173
|
+
placeholder: string;
|
|
174
|
+
disabled: boolean;
|
|
175
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
176
|
+
/** Expiry date field. Emits `change` (ElementChangeEvent), `focus`, `blur`. */
|
|
177
|
+
export declare const OzExpiry: import("vue").DefineComponent<{
|
|
178
|
+
placeholder?: string | undefined;
|
|
179
|
+
disabled?: boolean | undefined;
|
|
180
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
181
|
+
}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
182
|
+
[key: string]: any;
|
|
183
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("blur" | "change" | "focus")[], "blur" | "change" | "focus", import("vue").PublicProps, Readonly<{
|
|
184
|
+
placeholder?: string | undefined;
|
|
185
|
+
disabled?: boolean | undefined;
|
|
186
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
187
|
+
}> & Readonly<{
|
|
188
|
+
onChange?: ((...args: any[]) => any) | undefined;
|
|
189
|
+
onFocus?: ((...args: any[]) => any) | undefined;
|
|
190
|
+
onBlur?: ((...args: any[]) => any) | undefined;
|
|
191
|
+
}>, {
|
|
192
|
+
style: import("../types").ElementStyleConfig | undefined;
|
|
193
|
+
placeholder: string;
|
|
194
|
+
disabled: boolean;
|
|
195
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
196
|
+
/** CVV / CVC field. Emits `change` (ElementChangeEvent), `focus`, `blur`. */
|
|
197
|
+
export declare const OzCvv: import("vue").DefineComponent<{
|
|
198
|
+
placeholder?: string | undefined;
|
|
199
|
+
disabled?: boolean | undefined;
|
|
200
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
201
|
+
}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
202
|
+
[key: string]: any;
|
|
203
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("blur" | "change" | "focus")[], "blur" | "change" | "focus", import("vue").PublicProps, Readonly<{
|
|
204
|
+
placeholder?: string | undefined;
|
|
205
|
+
disabled?: boolean | undefined;
|
|
206
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
207
|
+
}> & Readonly<{
|
|
208
|
+
onChange?: ((...args: any[]) => any) | undefined;
|
|
209
|
+
onFocus?: ((...args: any[]) => any) | undefined;
|
|
210
|
+
onBlur?: ((...args: any[]) => any) | undefined;
|
|
211
|
+
}>, {
|
|
212
|
+
style: import("../types").ElementStyleConfig | undefined;
|
|
213
|
+
placeholder: string;
|
|
214
|
+
disabled: boolean;
|
|
215
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
216
|
+
/** Bank account number field. Emits `change` (ElementChangeEvent), `focus`, `blur`. */
|
|
217
|
+
export declare const OzBankAccountNumber: import("vue").DefineComponent<{
|
|
218
|
+
placeholder?: string | undefined;
|
|
219
|
+
disabled?: boolean | undefined;
|
|
220
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
221
|
+
}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
222
|
+
[key: string]: any;
|
|
223
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("blur" | "change" | "focus")[], "blur" | "change" | "focus", import("vue").PublicProps, Readonly<{
|
|
224
|
+
placeholder?: string | undefined;
|
|
225
|
+
disabled?: boolean | undefined;
|
|
226
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
227
|
+
}> & Readonly<{
|
|
228
|
+
onChange?: ((...args: any[]) => any) | undefined;
|
|
229
|
+
onFocus?: ((...args: any[]) => any) | undefined;
|
|
230
|
+
onBlur?: ((...args: any[]) => any) | undefined;
|
|
231
|
+
}>, {
|
|
232
|
+
style: import("../types").ElementStyleConfig | undefined;
|
|
233
|
+
placeholder: string;
|
|
234
|
+
disabled: boolean;
|
|
235
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
236
|
+
/** Bank routing number field. Emits `change` (ElementChangeEvent), `focus`, `blur`. */
|
|
237
|
+
export declare const OzBankRoutingNumber: import("vue").DefineComponent<{
|
|
238
|
+
placeholder?: string | undefined;
|
|
239
|
+
disabled?: boolean | undefined;
|
|
240
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
241
|
+
}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
242
|
+
[key: string]: any;
|
|
243
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("blur" | "change" | "focus")[], "blur" | "change" | "focus", import("vue").PublicProps, Readonly<{
|
|
244
|
+
placeholder?: string | undefined;
|
|
245
|
+
disabled?: boolean | undefined;
|
|
246
|
+
style?: import("../types").ElementStyleConfig | undefined;
|
|
247
|
+
}> & Readonly<{
|
|
248
|
+
onChange?: ((...args: any[]) => any) | undefined;
|
|
249
|
+
onFocus?: ((...args: any[]) => any) | undefined;
|
|
250
|
+
onBlur?: ((...args: any[]) => any) | undefined;
|
|
251
|
+
}>, {
|
|
252
|
+
style: import("../types").ElementStyleConfig | undefined;
|
|
253
|
+
placeholder: string;
|
|
254
|
+
disabled: boolean;
|
|
255
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
256
|
+
export type { ElementChangeEvent, TokenizeOptions, TokenResponse, BankTokenizeOptions, BankTokenResponse, Appearance, FontSource, } from '../types';
|
|
@@ -22,6 +22,8 @@ export declare class ElementFrame {
|
|
|
22
22
|
private vaultId;
|
|
23
23
|
private frameId;
|
|
24
24
|
private hostOrigin;
|
|
25
|
+
/** Mirrors the parent vault's debug flag — set via OZ_INIT. */
|
|
26
|
+
private debug;
|
|
25
27
|
private rawValue;
|
|
26
28
|
private cardBrand;
|
|
27
29
|
private cvvMaxLength;
|
|
@@ -64,6 +66,13 @@ export declare class ElementFrame {
|
|
|
64
66
|
private reapplyStateStyles;
|
|
65
67
|
private applyPlaceholderStyles;
|
|
66
68
|
private fontsInjected;
|
|
69
|
+
/**
|
|
70
|
+
* Allowed hostnames for cssSrc font stylesheets. Restricting to known font
|
|
71
|
+
* CDNs prevents a merchant from loading arbitrary third-party stylesheets
|
|
72
|
+
* inside the PCI-isolated iframe, which could otherwise be used to load
|
|
73
|
+
* tracking scripts via @import or attempt CSS-based data extraction.
|
|
74
|
+
*/
|
|
75
|
+
private static readonly ALLOWED_FONT_HOSTS;
|
|
67
76
|
private injectFonts;
|
|
68
77
|
/**
|
|
69
78
|
* Delivers this field's raw value to the tokenizer iframe via the MessagePort
|
|
@@ -23,11 +23,27 @@ export declare function isAllowedVaultOrigin(apiUrl: string): boolean;
|
|
|
23
23
|
export declare class TokenizerFrame {
|
|
24
24
|
private vaultId;
|
|
25
25
|
private hostOrigin;
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Session key — delivered via OZ_INIT on first ready and again after each
|
|
28
|
+
* auto-refresh. The latest value is always used for tokenize calls.
|
|
29
|
+
*/
|
|
27
30
|
private waxKey;
|
|
28
31
|
private pending;
|
|
29
32
|
private pendingBank;
|
|
33
|
+
/** Mirrors the parent vault's debug flag — set via OZ_INIT. When true, the frame relays debug-level info in addition to errors/warnings. */
|
|
34
|
+
private debug;
|
|
30
35
|
constructor();
|
|
36
|
+
/**
|
|
37
|
+
* Relays a message to the parent window as OZ_DEBUG_LOG so it surfaces in
|
|
38
|
+
* the merchant page's DevTools console rather than the hidden tokenizer-frame
|
|
39
|
+
* context.
|
|
40
|
+
*
|
|
41
|
+
* Level semantics:
|
|
42
|
+
* 'error' — always relayed (genuine failure)
|
|
43
|
+
* 'warn' — always relayed (developer misconfiguration)
|
|
44
|
+
* 'log' — only relayed when debug mode is enabled (lifecycle / timing info)
|
|
45
|
+
*/
|
|
46
|
+
private postDebugLog;
|
|
31
47
|
private onMessage;
|
|
32
48
|
/**
|
|
33
49
|
* Shared vault POST helper. Handles origin guard, header construction,
|
package/dist/server/index.cjs.js
CHANGED
|
@@ -87,13 +87,14 @@ function validateEmail(email) {
|
|
|
87
87
|
// ─── Phone ───────────────────────────────────────────────────────────────────
|
|
88
88
|
/**
|
|
89
89
|
* Validates E.164 phone format: starts with +, 1–3 digit country code,
|
|
90
|
-
* followed by 7–12 digits, total
|
|
90
|
+
* followed by 7–12 digits, max 15 digits total (E.164 spec cap = 16 chars
|
|
91
|
+
* including the leading +).
|
|
91
92
|
*
|
|
92
93
|
* Matches the output of checkout's formatPhoneForAPI() function.
|
|
93
94
|
* Examples: "+15551234567", "+447911123456", "+61412345678"
|
|
94
95
|
*/
|
|
95
96
|
function validateE164Phone(phone) {
|
|
96
|
-
return /^\+[1-9]\d{6,
|
|
97
|
+
return /^\+[1-9]\d{6,14}$/.test(phone);
|
|
97
98
|
}
|
|
98
99
|
// ─── Field length ─────────────────────────────────────────────────────────────
|
|
99
100
|
/** Returns true when the string is non-empty and ≤50 characters (cardSale schema). */
|
|
@@ -293,8 +294,8 @@ function validateBilling(billing) {
|
|
|
293
294
|
* ```
|
|
294
295
|
*/
|
|
295
296
|
// ─── Configuration ───────────────────────────────────────────────────────────
|
|
296
|
-
const DEFAULT_API_URL = "https://
|
|
297
|
-
const DEFAULT_VAULT_URL = "https://
|
|
297
|
+
const DEFAULT_API_URL = "https://ozurapay-pay-api-v2-staging-c8abbdfhfbd3c5em.eastus-01.azurewebsites.net";
|
|
298
|
+
const DEFAULT_VAULT_URL = "https://pci-vault-staging-drc0duhcakf4g4fr.eastus-01.azurewebsites.net";
|
|
298
299
|
const DEFAULT_TIMEOUT = 30000;
|
|
299
300
|
// ─── Error ───────────────────────────────────────────────────────────────────
|
|
300
301
|
class OzuraError extends Error {
|
|
@@ -334,7 +335,7 @@ function timeoutSignal(ms) {
|
|
|
334
335
|
// ─── Main class ──────────────────────────────────────────────────────────────
|
|
335
336
|
class Ozura {
|
|
336
337
|
constructor(config) {
|
|
337
|
-
var _a, _b;
|
|
338
|
+
var _a, _b, _c;
|
|
338
339
|
if (!config.vaultKey)
|
|
339
340
|
throw new OzuraError('vaultKey is required', 0);
|
|
340
341
|
this.merchantId = config.merchantId;
|
|
@@ -344,6 +345,30 @@ class Ozura {
|
|
|
344
345
|
this.vaultUrl = config.vaultUrl || DEFAULT_VAULT_URL;
|
|
345
346
|
this.timeoutMs = (_a = config.timeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT;
|
|
346
347
|
this.retries = (_b = config.retries) !== null && _b !== void 0 ? _b : DEFAULT_RETRIES;
|
|
348
|
+
this.debug = (_c = config.debug) !== null && _c !== void 0 ? _c : false;
|
|
349
|
+
}
|
|
350
|
+
/** Emits a `[Ozura]`-prefixed entry to `console.log`. No-op when `debug` is not set. */
|
|
351
|
+
log(message, data) {
|
|
352
|
+
if (!this.debug)
|
|
353
|
+
return;
|
|
354
|
+
if (data !== undefined) {
|
|
355
|
+
console.log(`[Ozura] ${message}`, data);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
console.log(`[Ozura] ${message}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Returns a copy of `body` with known-sensitive fields replaced by `'[REDACTED]'`.
|
|
363
|
+
* Used to make debug logs safe to emit without leaking vault tokens or session keys.
|
|
364
|
+
*/
|
|
365
|
+
static redactBody(body) {
|
|
366
|
+
const SENSITIVE = new Set(['ozuraVaultToken', 'ozuraCvcSession', 'wax_key', 'waxKey']);
|
|
367
|
+
const result = {};
|
|
368
|
+
for (const [k, v] of Object.entries(body)) {
|
|
369
|
+
result[k] = SENSITIVE.has(k) ? '[REDACTED]' : v;
|
|
370
|
+
}
|
|
371
|
+
return result;
|
|
347
372
|
}
|
|
348
373
|
/**
|
|
349
374
|
* Charge a tokenized card. Maps the friendly `CardSaleInput` shape to the
|
|
@@ -352,11 +377,12 @@ class Ozura {
|
|
|
352
377
|
* Rate limit: 100 requests/minute per merchant.
|
|
353
378
|
*/
|
|
354
379
|
async cardSale(input) {
|
|
355
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
380
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
356
381
|
if (!this.merchantId)
|
|
357
382
|
throw new OzuraError('merchantId is required for cardSale(). Add it to the Ozura constructor config.', 0);
|
|
358
383
|
if (!this.apiKey)
|
|
359
384
|
throw new OzuraError('apiKey is required for cardSale(). Add it to the Ozura constructor config.', 0);
|
|
385
|
+
this.log('cardSale', { merchantId: this.merchantId, amount: input.amount, currency: input.currency || 'USD' });
|
|
360
386
|
const billing = input.billing;
|
|
361
387
|
// Build body with required fields, then conditionally add optional billing
|
|
362
388
|
// fields only when non-empty. The Pay API enforces minLength:1 on these
|
|
@@ -366,30 +392,33 @@ class Ozura {
|
|
|
366
392
|
merchantId: this.merchantId,
|
|
367
393
|
amount: input.amount,
|
|
368
394
|
currency: input.currency || 'USD',
|
|
369
|
-
|
|
395
|
+
// Pay API requires these two fields. Default to standard web-checkout values.
|
|
396
|
+
transactionInitiationType: (_a = input.transactionInitiationType) !== null && _a !== void 0 ? _a : 'cit',
|
|
397
|
+
transactionChannel: (_b = input.transactionChannel) !== null && _b !== void 0 ? _b : 'ecommerce',
|
|
398
|
+
ozuraVaultToken: input.token,
|
|
370
399
|
ozuraCvcSession: input.cvcSession,
|
|
371
400
|
billingFirstName: billing.firstName,
|
|
372
401
|
billingLastName: billing.lastName,
|
|
373
402
|
clientIpAddress: input.clientIpAddress,
|
|
374
|
-
salesTaxExempt: (
|
|
375
|
-
surchargePercent: (
|
|
376
|
-
tipAmount: (
|
|
403
|
+
salesTaxExempt: (_c = input.salesTaxExempt) !== null && _c !== void 0 ? _c : false,
|
|
404
|
+
surchargePercent: (_d = input.surchargePercent) !== null && _d !== void 0 ? _d : '0.00',
|
|
405
|
+
tipAmount: (_e = input.tipAmount) !== null && _e !== void 0 ? _e : '0.00',
|
|
377
406
|
};
|
|
378
407
|
if (billing.email)
|
|
379
408
|
body.billingEmail = billing.email;
|
|
380
409
|
if (billing.phone)
|
|
381
410
|
body.billingPhone = billing.phone;
|
|
382
|
-
if ((
|
|
411
|
+
if ((_f = billing.address) === null || _f === void 0 ? void 0 : _f.line1)
|
|
383
412
|
body.billingAddress1 = billing.address.line1;
|
|
384
|
-
if ((
|
|
413
|
+
if ((_g = billing.address) === null || _g === void 0 ? void 0 : _g.line2)
|
|
385
414
|
body.billingAddress2 = billing.address.line2;
|
|
386
|
-
if ((
|
|
415
|
+
if ((_h = billing.address) === null || _h === void 0 ? void 0 : _h.city)
|
|
387
416
|
body.billingCity = billing.address.city;
|
|
388
|
-
if ((
|
|
417
|
+
if ((_j = billing.address) === null || _j === void 0 ? void 0 : _j.state)
|
|
389
418
|
body.billingState = billing.address.state;
|
|
390
|
-
if ((
|
|
419
|
+
if ((_k = billing.address) === null || _k === void 0 ? void 0 : _k.zip)
|
|
391
420
|
body.billingZipcode = billing.address.zip;
|
|
392
|
-
body.billingCountry = ((
|
|
421
|
+
body.billingCountry = ((_l = billing.address) === null || _l === void 0 ? void 0 : _l.country) || 'US';
|
|
393
422
|
if (input.processor) {
|
|
394
423
|
body.processor = input.processor;
|
|
395
424
|
}
|
|
@@ -457,7 +486,8 @@ class Ozura {
|
|
|
457
486
|
* app.post('/api/oz-session', createSessionMiddleware(ozura));
|
|
458
487
|
*/
|
|
459
488
|
async createSession(options) {
|
|
460
|
-
var _a, _b, _c;
|
|
489
|
+
var _a, _b, _c, _d;
|
|
490
|
+
this.log('createSession', { sessionId: options === null || options === void 0 ? void 0 : options.sessionId, sessionLimit: (_a = options === null || options === void 0 ? void 0 : options.sessionLimit) !== null && _a !== void 0 ? _a : 3 });
|
|
461
491
|
const body = {};
|
|
462
492
|
if (options === null || options === void 0 ? void 0 : options.sessionId) {
|
|
463
493
|
body.checkout_session_id = options.sessionId;
|
|
@@ -467,7 +497,7 @@ class Ozura {
|
|
|
467
497
|
// sessionLimit: number → use that value.
|
|
468
498
|
if ((options === null || options === void 0 ? void 0 : options.sessionLimit) === null) ;
|
|
469
499
|
else {
|
|
470
|
-
body.max_tokenize_calls = (
|
|
500
|
+
body.max_tokenize_calls = (_b = options === null || options === void 0 ? void 0 : options.sessionLimit) !== null && _b !== void 0 ? _b : 3;
|
|
471
501
|
}
|
|
472
502
|
// maxProxyCalls: null → omit (unlimited). undefined → omit (vault default).
|
|
473
503
|
if ((options === null || options === void 0 ? void 0 : options.maxProxyCalls) != null) {
|
|
@@ -483,6 +513,7 @@ class Ozura {
|
|
|
483
513
|
body.metadata = options.metadata;
|
|
484
514
|
if ((options === null || options === void 0 ? void 0 : options.ttlSeconds) != null)
|
|
485
515
|
body.ttl_seconds = options.ttlSeconds;
|
|
516
|
+
const sessionStartMs = Date.now();
|
|
486
517
|
const res = await this.fetchWithRetry(`${this.vaultUrl}/internal/wax-session`, {
|
|
487
518
|
method: 'POST',
|
|
488
519
|
headers: {
|
|
@@ -492,15 +523,16 @@ class Ozura {
|
|
|
492
523
|
body: JSON.stringify(body),
|
|
493
524
|
});
|
|
494
525
|
const json = await res.json().catch(() => ({}));
|
|
495
|
-
|
|
526
|
+
this.log(`createSession → ${res.status} (${Date.now() - sessionStartMs}ms)`);
|
|
527
|
+
if (res.ok) {
|
|
496
528
|
const data = json.data;
|
|
497
|
-
const sessionKey = String((
|
|
529
|
+
const sessionKey = String((_c = data === null || data === void 0 ? void 0 : data.wax_key) !== null && _c !== void 0 ? _c : '');
|
|
498
530
|
if (!sessionKey) {
|
|
499
531
|
throw new OzuraError('Vault session response missing data.wax_key', res.status, JSON.stringify(json));
|
|
500
532
|
}
|
|
501
533
|
return {
|
|
502
534
|
sessionKey,
|
|
503
|
-
expiresInSeconds: Number((
|
|
535
|
+
expiresInSeconds: Number((_d = data === null || data === void 0 ? void 0 : data.expires_in_seconds) !== null && _d !== void 0 ? _d : 1800),
|
|
504
536
|
};
|
|
505
537
|
}
|
|
506
538
|
const errorCode = json.error_code || '';
|
|
@@ -622,11 +654,20 @@ class Ozura {
|
|
|
622
654
|
if (includeVaultKey) {
|
|
623
655
|
headers['vault-api-key'] = this.vaultKey;
|
|
624
656
|
}
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
657
|
+
const postStartMs = Date.now();
|
|
658
|
+
let res;
|
|
659
|
+
try {
|
|
660
|
+
res = await this.fetchWithRetry(`${this.apiUrl}${path}`, {
|
|
661
|
+
method: 'POST',
|
|
662
|
+
headers,
|
|
663
|
+
body: JSON.stringify(body),
|
|
664
|
+
}, maxRetries);
|
|
665
|
+
}
|
|
666
|
+
catch (err) {
|
|
667
|
+
this.log(`POST ${path} ERROR (${Date.now() - postStartMs}ms)`, { error: err instanceof Error ? err.message : String(err) });
|
|
668
|
+
throw err;
|
|
669
|
+
}
|
|
670
|
+
this.log(`POST ${path} → ${res.status} (${Date.now() - postStartMs}ms)`, Ozura.redactBody(body));
|
|
630
671
|
return this.handleResponse(res);
|
|
631
672
|
}
|
|
632
673
|
async getRaw(path) {
|