payluk-escrow-inline-checkout 0.2.10 → 0.3.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 +146 -38
- package/dist/{chunk-TL6HBLNO.js → chunk-E6SFCKQP.js} +16 -4
- package/dist/chunk-E6SFCKQP.js.map +1 -0
- package/dist/index.cjs +14 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +1 -1
- package/dist/react/index.cjs +14 -2
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +4 -1
- package/dist/react/index.d.ts +4 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-TL6HBLNO.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ A lightweight client SDK that initializes your checkout configuration, creates a
|
|
|
6
6
|
- Promise-based API.
|
|
7
7
|
- First-class TypeScript types.
|
|
8
8
|
- Optional React hook with loading/error state.
|
|
9
|
+
- Multi-escrow support: pay for multiple escrows in a single checkout session.
|
|
9
10
|
|
|
10
11
|
## Installation
|
|
11
12
|
|
|
@@ -178,6 +179,106 @@ export function CheckoutButton() {
|
|
|
178
179
|
- Only use publishable keys in the browser. Keep any secret keys on your server.
|
|
179
180
|
- Validate inputs on your backend and return the required session payload.
|
|
180
181
|
|
|
182
|
+
## Multi-Escrow Payments
|
|
183
|
+
|
|
184
|
+
You can now pay for multiple escrows in a single checkout session by passing an array of payment tokens. This is useful when a customer wants to pay for multiple items or services at once.
|
|
185
|
+
|
|
186
|
+
### Requirements
|
|
187
|
+
|
|
188
|
+
- All escrows must belong to the same merchant
|
|
189
|
+
- All escrows must be in `PENDING` status
|
|
190
|
+
- If using merchant escrows, a `customerId` is required
|
|
191
|
+
- The total amount from all escrows will be charged in a single transaction
|
|
192
|
+
|
|
193
|
+
### Vanilla JS/TS Example
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { initEscrowCheckout, pay } from 'payluk-escrow-inline-checkout';
|
|
197
|
+
|
|
198
|
+
// Initialize once
|
|
199
|
+
initEscrowCheckout({
|
|
200
|
+
publicKey: '<YOUR_PUBLISHABLE_KEY>'
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Pay for multiple escrows
|
|
204
|
+
async function onPayMultipleClick() {
|
|
205
|
+
try {
|
|
206
|
+
await pay({
|
|
207
|
+
// Pass an array of payment tokens
|
|
208
|
+
paymentToken: ['TOKEN_1', 'TOKEN_2', 'TOKEN_3'],
|
|
209
|
+
reference: '<REFERENCE_ID>',
|
|
210
|
+
redirectUrl: 'https://your-app.example.com/checkout/complete',
|
|
211
|
+
logoUrl: 'https://mediacloud.me/media/W8HU9TK245QF528ZULCFSJXX2SBBLT.jpg',
|
|
212
|
+
brand: 'YourBrand',
|
|
213
|
+
customerId: 'customer_123', // Required for merchant escrows
|
|
214
|
+
callback: (result) => {
|
|
215
|
+
// result.paymentId will contain comma-separated IDs: "id1,id2,id3"
|
|
216
|
+
// result.reference will contain unique refs: "REF_1,REF_2,REF_3"
|
|
217
|
+
console.log('Multi-escrow checkout result:', result);
|
|
218
|
+
},
|
|
219
|
+
onClose: () => {
|
|
220
|
+
console.log('Widget closed');
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
} catch (err) {
|
|
224
|
+
console.error('Payment failed:', err);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### React Example
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import React from 'react';
|
|
233
|
+
import { useEscrowCheckout } from 'payluk-escrow-inline-checkout/react';
|
|
234
|
+
|
|
235
|
+
export function MultiEscrowCheckoutButton() {
|
|
236
|
+
const { pay } = useEscrowCheckout();
|
|
237
|
+
|
|
238
|
+
const handleClick = async () => {
|
|
239
|
+
try {
|
|
240
|
+
await pay({
|
|
241
|
+
// Array of payment tokens for multi-escrow checkout
|
|
242
|
+
paymentToken: ['TOKEN_1', 'TOKEN_2', 'TOKEN_3'],
|
|
243
|
+
reference: '<REFERENCE_ID>',
|
|
244
|
+
redirectUrl: 'https://your-app.example.com/checkout/complete',
|
|
245
|
+
customerId: 'customer_123',
|
|
246
|
+
callback: (result) => {
|
|
247
|
+
console.log('Payment successful:', result);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
} catch (err) {
|
|
251
|
+
console.error('Payment failed:', err);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<button onClick={handleClick}>
|
|
257
|
+
Pay for Multiple Items
|
|
258
|
+
</button>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### How It Works
|
|
264
|
+
|
|
265
|
+
1. **Individual Processing**: Each escrow is processed individually with its own payment intent
|
|
266
|
+
2. **Aggregated Total**: The checkout widget displays the total amount from all escrows
|
|
267
|
+
3. **Unique References**: Multi-payments generate unique references for each escrow (e.g., `REF_1`, `REF_2`, `REF_3`)
|
|
268
|
+
4. **Single Transaction**: The customer completes one payment for all escrows combined
|
|
269
|
+
5. **Backward Compatible**: Single payment tokens (strings) still work exactly as before
|
|
270
|
+
|
|
271
|
+
### Response Format
|
|
272
|
+
|
|
273
|
+
When using multi-escrow payments, the callback receives:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
{
|
|
277
|
+
paymentId: "token1,token2,token3", // Comma-separated escrow IDs
|
|
278
|
+
reference: "REF_1,REF_2,REF_3" // Comma-separated unique references
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
181
282
|
## API
|
|
182
283
|
|
|
183
284
|
### `initEscrowCheckout(config)`
|
|
@@ -207,20 +308,27 @@ initEscrowCheckout({
|
|
|
207
308
|
Creates a checkout session via your backend and opens the widget.
|
|
208
309
|
|
|
209
310
|
**Required:**
|
|
210
|
-
- `paymentToken`: `string`
|
|
311
|
+
- `paymentToken`: `string | string[]` — single payment token or array of tokens for multi-escrow checkout
|
|
211
312
|
- `reference`: `string`
|
|
212
313
|
- `redirectUrl`: `string`
|
|
213
314
|
|
|
214
315
|
**Optional:**
|
|
215
316
|
- `logoUrl?`: `string`
|
|
216
|
-
- `customerId?`: `string` —
|
|
317
|
+
- `customerId?`: `string` — required for merchant escrows, optional for business escrows
|
|
217
318
|
- `brand?`: `string`
|
|
218
319
|
- `callback?`: `(result: unknown) => void`
|
|
219
320
|
- `onClose?`: `() => void`
|
|
321
|
+
- `extra?`: `Record<string, unknown>` — additional widget configuration
|
|
220
322
|
|
|
221
323
|
**Returns:**
|
|
222
324
|
- `Promise<void>` that resolves when the widget is opened (and rejects on errors).
|
|
223
325
|
|
|
326
|
+
**Multi-Escrow Notes:**
|
|
327
|
+
- When using an array of payment tokens, all escrows must belong to the same merchant
|
|
328
|
+
- The checkout widget will display the aggregated total amount
|
|
329
|
+
- Each escrow is processed individually with its own payment intent
|
|
330
|
+
- Empty arrays or arrays containing empty strings will be rejected
|
|
331
|
+
|
|
224
332
|
### `useEscrowCheckout(): { ready, loading, error, pay }`
|
|
225
333
|
|
|
226
334
|
React hook that exposes:
|
|
@@ -241,42 +349,42 @@ import { useEscrowCheckout } from 'payluk-escrow-inline-checkout/react';
|
|
|
241
349
|
- **Initialize on the client:** If using frameworks like Next.js, call `initEscrowCheckout(...)` in a client component or in an effect.
|
|
242
350
|
- **Preloading:** The hook marks `ready` after the first successful `pay`. If you need earlier preloading, you can trigger a preparatory flow (depending on your setup).
|
|
243
351
|
|
|
244
|
-
## Error Handling
|
|
245
|
-
|
|
246
|
-
Common issues:
|
|
247
|
-
- **Not initialized:** Ensure `initEscrowCheckout({ publicKey })` is called before `pay(...)`.
|
|
248
|
-
- **Browser-only:** Do not call `pay(...)` on the server.
|
|
249
|
-
- **Network/API errors:** If the session endpoint fails, `pay(...)` will reject with an `EscrowCheckoutError` that includes a `code`, optional HTTP `status`, and `details`.
|
|
250
|
-
|
|
251
|
-
`EscrowCheckoutError` codes:
|
|
252
|
-
- `NOT_INITIALIZED`
|
|
253
|
-
- `BROWSER_ONLY`
|
|
254
|
-
- `INVALID_INPUT`
|
|
255
|
-
- `WIDGET_LOAD`
|
|
256
|
-
- `NETWORK`
|
|
257
|
-
- `SESSION_CREATE`
|
|
258
|
-
- `SESSION_RESPONSE`
|
|
259
|
-
|
|
260
|
-
You can import the error class if you want stricter checks in TypeScript:
|
|
261
|
-
|
|
262
|
-
```ts
|
|
263
|
-
import { EscrowCheckoutError } from 'payluk-escrow-inline-checkout';
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
**Example:**
|
|
267
|
-
|
|
268
|
-
```ts
|
|
269
|
-
try {
|
|
270
|
-
await pay({ /* ... */ });
|
|
271
|
-
} catch (err) {
|
|
272
|
-
if (err instanceof EscrowCheckoutError) {
|
|
273
|
-
console.error(err.code, err.status, err.message);
|
|
274
|
-
alert(err.message);
|
|
275
|
-
} else {
|
|
276
|
-
alert('Checkout failed');
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
```
|
|
352
|
+
## Error Handling
|
|
353
|
+
|
|
354
|
+
Common issues:
|
|
355
|
+
- **Not initialized:** Ensure `initEscrowCheckout({ publicKey })` is called before `pay(...)`.
|
|
356
|
+
- **Browser-only:** Do not call `pay(...)` on the server.
|
|
357
|
+
- **Network/API errors:** If the session endpoint fails, `pay(...)` will reject with an `EscrowCheckoutError` that includes a `code`, optional HTTP `status`, and `details`.
|
|
358
|
+
|
|
359
|
+
`EscrowCheckoutError` codes:
|
|
360
|
+
- `NOT_INITIALIZED`
|
|
361
|
+
- `BROWSER_ONLY`
|
|
362
|
+
- `INVALID_INPUT`
|
|
363
|
+
- `WIDGET_LOAD`
|
|
364
|
+
- `NETWORK`
|
|
365
|
+
- `SESSION_CREATE`
|
|
366
|
+
- `SESSION_RESPONSE`
|
|
367
|
+
|
|
368
|
+
You can import the error class if you want stricter checks in TypeScript:
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
import { EscrowCheckoutError } from 'payluk-escrow-inline-checkout';
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Example:**
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
try {
|
|
378
|
+
await pay({ /* ... */ });
|
|
379
|
+
} catch (err) {
|
|
380
|
+
if (err instanceof EscrowCheckoutError) {
|
|
381
|
+
console.error(err.code, err.status, err.message);
|
|
382
|
+
alert(err.message);
|
|
383
|
+
} else {
|
|
384
|
+
alert('Checkout failed');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
280
388
|
|
|
281
389
|
## Security
|
|
282
390
|
|
|
@@ -38,6 +38,15 @@ function normalizeError(error) {
|
|
|
38
38
|
function isNonEmptyString(value) {
|
|
39
39
|
return typeof value === "string" && value.trim().length > 0;
|
|
40
40
|
}
|
|
41
|
+
function isValidPaymentToken(value) {
|
|
42
|
+
if (isNonEmptyString(value)) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
return value.length > 0 && value.every((token) => isNonEmptyString(token));
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
41
50
|
function isSessionResponse(value) {
|
|
42
51
|
if (!value || typeof value !== "object") return false;
|
|
43
52
|
return "session" in value;
|
|
@@ -144,8 +153,11 @@ async function pay(input) {
|
|
|
144
153
|
throw new EscrowCheckoutError("pay(...) can only run in the browser.", "BROWSER_ONLY");
|
|
145
154
|
}
|
|
146
155
|
assertConfigured(CONFIG);
|
|
147
|
-
if (!input || !
|
|
148
|
-
throw new EscrowCheckoutError(
|
|
156
|
+
if (!input || !isValidPaymentToken(input.paymentToken)) {
|
|
157
|
+
throw new EscrowCheckoutError(
|
|
158
|
+
'pay(...) requires "paymentToken" (must be a non-empty string or array of non-empty strings).',
|
|
159
|
+
"INVALID_INPUT"
|
|
160
|
+
);
|
|
149
161
|
}
|
|
150
162
|
if (!isNonEmptyString(input.reference)) {
|
|
151
163
|
throw new EscrowCheckoutError('pay(...) requires "reference".', "INVALID_INPUT");
|
|
@@ -167,5 +179,5 @@ async function pay(input) {
|
|
|
167
179
|
}
|
|
168
180
|
|
|
169
181
|
export { EscrowCheckoutError, initEscrowCheckout, pay };
|
|
170
|
-
//# sourceMappingURL=chunk-
|
|
171
|
-
//# sourceMappingURL=chunk-
|
|
182
|
+
//# sourceMappingURL=chunk-E6SFCKQP.js.map
|
|
183
|
+
//# sourceMappingURL=chunk-E6SFCKQP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"chunk-E6SFCKQP.js","sourcesContent":["export type EscrowWidget = (options: Record<string, unknown>) => void;\n\nexport type EscrowCheckoutErrorCode =\n | 'NOT_INITIALIZED'\n | 'BROWSER_ONLY'\n | 'INVALID_INPUT'\n | 'WIDGET_LOAD'\n | 'NETWORK'\n | 'SESSION_CREATE'\n | 'SESSION_RESPONSE';\n\nexport class EscrowCheckoutError extends Error {\n code: EscrowCheckoutErrorCode;\n status?: number;\n details?: unknown;\n\n constructor(message: string, code: EscrowCheckoutErrorCode, options?: { status?: number; details?: unknown }) {\n super(message);\n this.name = 'EscrowCheckoutError';\n this.code = code;\n this.status = options?.status;\n this.details = options?.details;\n }\n}\n\nexport interface InitConfig {\n publicKey: string; // publishable key only\n /**\n * Optional overrides for experts. End users don't need to set these.\n */\n scriptUrlOverride?: string;\n globalName?: string; // default 'EscrowCheckout'\n crossOrigin?: '' | 'anonymous' | 'use-credentials';\n}\n\nexport interface PayInput {\n /**\n * Single escrow payment token or array of payment tokens for multi-escrow checkout.\n * When providing multiple tokens, they must all belong to the same merchant.\n */\n paymentToken: string | string[];\n reference: string;\n redirectUrl: string;\n logoUrl?: string;\n brand?: string;\n customerId?: string;\n /**\n * Extra fields supported by the widget.\n */\n extra?: Record<string, unknown>;\n callback?: (result: any) => void;\n onClose?: () => void;\n}\n\nexport interface SessionResponse {\n session: unknown;\n}\n\nconst DEFAULT_SCRIPT_URL = 'https://checkout.payluk.ng/escrow-checkout.min.js';\nconst DEFAULT_GLOBAL_NAME = 'EscrowCheckout';\n\ntype ResolvedConfig = Required<Pick<InitConfig, 'publicKey'>> & {\n scriptUrl: string;\n globalName: string;\n crossOrigin: '' | 'anonymous' | 'use-credentials';\n};\n\nlet CONFIG: ResolvedConfig | null = null;\n\n/**\n * Initialize the library once (e.g., in app bootstrap).\n * Hides script URL and session creation from consumers.\n */\nexport function initEscrowCheckout(config: InitConfig): void {\n if (!isNonEmptyString(config?.publicKey)) {\n throw new EscrowCheckoutError('initEscrowCheckout(...) requires \"publicKey\".', 'INVALID_INPUT');\n }\n CONFIG = {\n publicKey: config.publicKey.trim(),\n scriptUrl: config.scriptUrlOverride || DEFAULT_SCRIPT_URL,\n globalName: config.globalName || DEFAULT_GLOBAL_NAME,\n crossOrigin: config.crossOrigin ?? 'anonymous'\n };\n}\n\n/**\n * Narrow a provided config to ResolvedConfig or throw if not initialized.\n */\nfunction assertConfigured(config: ResolvedConfig | null): asserts config is ResolvedConfig {\n if (!config) {\n throw new EscrowCheckoutError(\n 'Escrow checkout not initialized. Call initEscrowCheckout({ publicKey }) first.',\n 'NOT_INITIALIZED'\n );\n }\n}\n\nfunction normalizeError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === 'string') return new Error(error);\n return new Error('Unknown error');\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isValidPaymentToken(value: unknown): value is string | string[] {\n // Check if it's a valid string\n if (isNonEmptyString(value)) {\n return true;\n }\n\n // Check if it's a valid array of strings\n if (Array.isArray(value)) {\n return value.length > 0 && value.every((token) => isNonEmptyString(token));\n }\n\n return false;\n}\n\nfunction isSessionResponse(value: unknown): value is SessionResponse {\n if (!value || typeof value !== 'object') return false;\n return 'session' in value;\n}\n\nasync function parseErrorBody(resp: Response): Promise<{ message?: string; details?: unknown }> {\n const text = await resp.text();\n if (!text) return {};\n\n try {\n const json = JSON.parse(text) as { message?: unknown };\n const message = typeof json?.message === 'string' ? json.message : undefined;\n return { message, details: json };\n } catch {\n return { message: text, details: text };\n }\n}\n\nasync function parseJsonResponse(resp: Response): Promise<unknown> {\n try {\n return await resp.json();\n } catch {\n throw new EscrowCheckoutError('Invalid JSON response from session endpoint.', 'SESSION_RESPONSE', {\n status: resp.status\n });\n }\n}\n\n/**\n * Internal: load the widget script once and return the global function.\n */\nasync function loadWidget(): Promise<EscrowWidget> {\n assertConfigured(CONFIG);\n const { scriptUrl, globalName, crossOrigin } = CONFIG;\n\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('EscrowCheckout can only be loaded in the browser.', 'BROWSER_ONLY');\n }\n\n const win = window as any;\n win.__escrowCheckoutLoader ??= {};\n\n if (win.__escrowCheckoutLoader[scriptUrl]) {\n return win.__escrowCheckoutLoader[scriptUrl];\n }\n\n if (typeof win[globalName] === 'function') {\n const fn = win[globalName] as EscrowWidget;\n win.__escrowCheckoutLoader[scriptUrl] = Promise.resolve(fn);\n return fn;\n }\n\n win.__escrowCheckoutLoader[scriptUrl] = new Promise<EscrowWidget>((resolve, reject) => {\n const script = document.createElement('script');\n script.src = scriptUrl;\n script.async = true;\n if (crossOrigin) script.crossOrigin = crossOrigin;\n\n script.onload = () => {\n const fn = win[globalName];\n if (typeof fn === 'function') resolve(fn as EscrowWidget);\n else {\n reject(\n new EscrowCheckoutError(\n `Escrow checkout script loaded, but window.${globalName} is not a function.`,\n 'WIDGET_LOAD'\n )\n );\n }\n };\n\n script.onerror = () =>\n reject(new EscrowCheckoutError(`Failed to load escrow checkout script: ${scriptUrl}`, 'WIDGET_LOAD'));\n\n document.head.appendChild(script);\n });\n\n return win.__escrowCheckoutLoader[scriptUrl];\n}\n\n/**\n * Internal: create a checkout session by calling your API.\n * This is intentionally inside the lib (user doesn't implement it).\n */\nasync function createSession(input: PayInput): Promise<SessionResponse> {\n assertConfigured(CONFIG);\n const { publicKey } = CONFIG;\n const apiBaseUrl = publicKey.startsWith('pk_live_') ? 'https://live.payluk.ng' : 'https://staging.live.payluk.ng';\n let resp: Response;\n\n try {\n resp = await fetch(`${apiBaseUrl}/v1/checkout/session`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentToken: input.paymentToken,\n redirectUrl: input.redirectUrl,\n reference: input.reference,\n customerId: input.customerId,\n publicKey\n })\n });\n } catch (error) {\n const normalized = normalizeError(error);\n throw new EscrowCheckoutError('Network error while creating checkout session.', 'NETWORK', {\n details: normalized.message\n });\n }\n\n if (!resp.ok) {\n const { message, details } = await parseErrorBody(resp);\n throw new EscrowCheckoutError(message || `Failed to create session (HTTP ${resp.status}).`, 'SESSION_CREATE', {\n status: resp.status,\n details\n });\n }\n\n const data = await parseJsonResponse(resp);\n if (!isSessionResponse(data)) {\n throw new EscrowCheckoutError('Session response missing \"session\".', 'SESSION_RESPONSE', {\n status: resp.status,\n details: data\n });\n }\n\n return data as SessionResponse;\n}\n\n/**\n * Public API: create the session and open the widget.\n * Consumers only supply business inputs (no script URL, no API calls).\n */\nexport async function pay(input: PayInput): Promise<void> {\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('pay(...) can only run in the browser.', 'BROWSER_ONLY');\n }\n\n assertConfigured(CONFIG);\n if (!input || !isValidPaymentToken(input.paymentToken)) {\n throw new EscrowCheckoutError(\n 'pay(...) requires \"paymentToken\" (must be a non-empty string or array of non-empty strings).',\n 'INVALID_INPUT'\n );\n }\n if (!isNonEmptyString(input.reference)) {\n throw new EscrowCheckoutError('pay(...) requires \"reference\".', 'INVALID_INPUT');\n }\n if (!isNonEmptyString(input.redirectUrl)) {\n throw new EscrowCheckoutError('pay(...) requires \"redirectUrl\".', 'INVALID_INPUT');\n }\n\n const [{ session }, widget] = await Promise.all([createSession(input), loadWidget()]);\n\n widget({\n session,\n logoUrl: input.logoUrl,\n brand: input.brand,\n callback: input.callback,\n onClose: input.onClose,\n customerId: input.customerId,\n publicKey: CONFIG.publicKey,\n ...(input.extra ?? {})\n });\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -40,6 +40,15 @@ function normalizeError(error) {
|
|
|
40
40
|
function isNonEmptyString(value) {
|
|
41
41
|
return typeof value === "string" && value.trim().length > 0;
|
|
42
42
|
}
|
|
43
|
+
function isValidPaymentToken(value) {
|
|
44
|
+
if (isNonEmptyString(value)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
return value.length > 0 && value.every((token) => isNonEmptyString(token));
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
43
52
|
function isSessionResponse(value) {
|
|
44
53
|
if (!value || typeof value !== "object") return false;
|
|
45
54
|
return "session" in value;
|
|
@@ -146,8 +155,11 @@ async function pay(input) {
|
|
|
146
155
|
throw new EscrowCheckoutError("pay(...) can only run in the browser.", "BROWSER_ONLY");
|
|
147
156
|
}
|
|
148
157
|
assertConfigured(CONFIG);
|
|
149
|
-
if (!input || !
|
|
150
|
-
throw new EscrowCheckoutError(
|
|
158
|
+
if (!input || !isValidPaymentToken(input.paymentToken)) {
|
|
159
|
+
throw new EscrowCheckoutError(
|
|
160
|
+
'pay(...) requires "paymentToken" (must be a non-empty string or array of non-empty strings).',
|
|
161
|
+
"INVALID_INPUT"
|
|
162
|
+
);
|
|
151
163
|
}
|
|
152
164
|
if (!isNonEmptyString(input.reference)) {
|
|
153
165
|
throw new EscrowCheckoutError('pay(...) requires "reference".', "INVALID_INPUT");
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAWO,IAAM,mBAAA,GAAN,cAAkC,KAAA,CAAM;AAAA,EAK3C,WAAA,CAAY,OAAA,EAAiB,IAAA,EAA+B,OAAA,EAAkD;AAC1G,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,SAAS,OAAA,EAAS,MAAA;AACvB,IAAA,IAAA,CAAK,UAAU,OAAA,EAAS,OAAA;AAAA,EAC5B;AACJ;AA+BA,IAAM,kBAAA,GAAqB,mDAAA;AAC3B,IAAM,mBAAA,GAAsB,gBAAA;AAQ5B,IAAI,MAAA,GAAgC,IAAA;AAM7B,SAAS,mBAAmB,MAAA,EAA0B;AACzD,EAAA,IAAI,CAAC,gBAAA,CAAiB,MAAA,EAAQ,SAAS,CAAA,EAAG;AACtC,IAAA,MAAM,IAAI,mBAAA,CAAoB,+CAAA,EAAiD,eAAe,CAAA;AAAA,EAClG;AACA,EAAA,MAAA,GAAS;AAAA,IACL,SAAA,EAAW,MAAA,CAAO,SAAA,CAAU,IAAA,EAAK;AAAA,IACjC,SAAA,EAAW,OAAO,iBAAA,IAAqB,kBAAA;AAAA,IACvC,UAAA,EAAY,OAAO,UAAA,IAAc,mBAAA;AAAA,IACjC,WAAA,EAAa,OAAO,WAAA,IAAe;AAAA,GACvC;AACJ;AAKA,SAAS,iBAAiB,MAAA,EAAiE;AACvF,EAAA,IAAI,CAAC,MAAA,EAAQ;AACT,IAAA,MAAM,IAAI,mBAAA;AAAA,MACN,gFAAA;AAAA,MACA;AAAA,KACJ;AAAA,EACJ;AACJ;AAEA,SAAS,eAAe,KAAA,EAAuB;AAC3C,EAAA,IAAI,KAAA,YAAiB,OAAO,OAAO,KAAA;AACnC,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,IAAI,MAAM,KAAK,CAAA;AACrD,EAAA,OAAO,IAAI,MAAM,eAAe,CAAA;AACpC;AAEA,SAAS,iBAAiB,KAAA,EAAiC;AACvD,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,IAAA,GAAO,MAAA,GAAS,CAAA;AAC9D;AAEA,SAAS,kBAAkB,KAAA,EAA0C;AACjE,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,UAAU,OAAO,KAAA;AAChD,EAAA,OAAO,SAAA,IAAa,KAAA;AACxB;AAEA,eAAe,eAAe,IAAA,EAAkE;AAC5F,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,EAAK;AAC7B,EAAA,IAAI,CAAC,IAAA,EAAM,OAAO,EAAC;AAEnB,EAAA,IAAI;AACA,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC5B,IAAA,MAAM,UAAU,OAAO,IAAA,EAAM,OAAA,KAAY,QAAA,GAAW,KAAK,OAAA,GAAU,KAAA,CAAA;AACnE,IAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,IAAA,EAAK;AAAA,EACpC,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,OAAA,EAAS,IAAA,EAAK;AAAA,EAC1C;AACJ;AAEA,eAAe,kBAAkB,IAAA,EAAkC;AAC/D,EAAA,IAAI;AACA,IAAA,OAAO,MAAM,KAAK,IAAA,EAAK;AAAA,EAC3B,CAAA,CAAA,MAAQ;AACJ,IAAA,MAAM,IAAI,mBAAA,CAAoB,8CAAA,EAAgD,kBAAA,EAAoB;AAAA,MAC9F,QAAQ,IAAA,CAAK;AAAA,KAChB,CAAA;AAAA,EACL;AACJ;AAKA,eAAe,UAAA,GAAoC;AAC/C,EAAA,gBAAA,CAAiB,MAAM,CAAA;AACvB,EAAA,MAAM,EAAE,SAAA,EAAW,UAAA,EAAY,WAAA,EAAY,GAAI,MAAA;AAE/C,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,MAAM,IAAI,mBAAA,CAAoB,mDAAA,EAAqD,cAAc,CAAA;AAAA,EACrG;AAEA,EAAA,MAAM,GAAA,GAAM,MAAA;AACZ,EAAA,GAAA,CAAI,sBAAA,KAAJ,GAAA,CAAI,sBAAA,GAA2B,EAAC,CAAA;AAEhC,EAAA,IAAI,GAAA,CAAI,sBAAA,CAAuB,SAAS,CAAA,EAAG;AACvC,IAAA,OAAO,GAAA,CAAI,uBAAuB,SAAS,CAAA;AAAA,EAC/C;AAEA,EAAA,IAAI,OAAO,GAAA,CAAI,UAAU,CAAA,KAAM,UAAA,EAAY;AACvC,IAAA,MAAM,EAAA,GAAK,IAAI,UAAU,CAAA;AACzB,IAAA,GAAA,CAAI,sBAAA,CAAuB,SAAS,CAAA,GAAI,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAC1D,IAAA,OAAO,EAAA;AAAA,EACX;AAEA,EAAA,GAAA,CAAI,uBAAuB,SAAS,CAAA,GAAI,IAAI,OAAA,CAAsB,CAAC,SAAS,MAAA,KAAW;AACnF,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAA,CAAO,GAAA,GAAM,SAAA;AACb,IAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AACf,IAAA,IAAI,WAAA,SAAoB,WAAA,GAAc,WAAA;AAEtC,IAAA,MAAA,CAAO,SAAS,MAAM;AAClB,MAAA,MAAM,EAAA,GAAK,IAAI,UAAU,CAAA;AACzB,MAAA,IAAI,OAAO,EAAA,KAAO,UAAA,EAAY,OAAA,CAAQ,EAAkB,CAAA;AAAA,WACnD;AACD,QAAA,MAAA;AAAA,UACI,IAAI,mBAAA;AAAA,YACA,6CAA6C,UAAU,CAAA,mBAAA,CAAA;AAAA,YACvD;AAAA;AACJ,SACJ;AAAA,MACJ;AAAA,IACJ,CAAA;AAEA,IAAA,MAAA,CAAO,OAAA,GAAU,MACb,MAAA,CAAO,IAAI,oBAAoB,CAAA,uCAAA,EAA0C,SAAS,CAAA,CAAA,EAAI,aAAa,CAAC,CAAA;AAExG,IAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAAA,EACpC,CAAC,CAAA;AAED,EAAA,OAAO,GAAA,CAAI,uBAAuB,SAAS,CAAA;AAC/C;AAMA,eAAe,cAAc,KAAA,EAA2C;AACpE,EAAA,gBAAA,CAAiB,MAAM,CAAA;AACvB,EAAA,MAAM,EAAE,WAAU,GAAI,MAAA;AACtB,EAAA,MAAM,UAAA,GAAa,SAAA,CAAU,UAAA,CAAW,UAAU,IAAI,wBAAA,GAA2B,gCAAA;AACjF,EAAA,IAAI,IAAA;AAEJ,EAAA,IAAI;AACA,IAAA,IAAA,GAAO,MAAM,KAAA,CAAM,CAAA,EAAG,UAAU,CAAA,oBAAA,CAAA,EAAwB;AAAA,MACpD,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,QACjB,cAAc,KAAA,CAAM,YAAA;AAAA,QACpB,aAAa,KAAA,CAAM,WAAA;AAAA,QACnB,WAAW,KAAA,CAAM,SAAA;AAAA,QACjB,YAAY,KAAA,CAAM,UAAA;AAAA,QAClB;AAAA,OACH;AAAA,KACJ,CAAA;AAAA,EACL,SAAS,KAAA,EAAO;AACZ,IAAA,MAAM,UAAA,GAAa,eAAe,KAAK,CAAA;AACvC,IAAA,MAAM,IAAI,mBAAA,CAAoB,gDAAA,EAAkD,SAAA,EAAW;AAAA,MACvF,SAAS,UAAA,CAAW;AAAA,KACvB,CAAA;AAAA,EACL;AAEA,EAAA,IAAI,CAAC,KAAK,EAAA,EAAI;AACV,IAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAQ,GAAI,MAAM,eAAe,IAAI,CAAA;AACtD,IAAA,MAAM,IAAI,mBAAA,CAAoB,OAAA,IAAW,kCAAkC,IAAA,CAAK,MAAM,MAAM,gBAAA,EAAkB;AAAA,MAC1G,QAAQ,IAAA,CAAK,MAAA;AAAA,MACb;AAAA,KACH,CAAA;AAAA,EACL;AAEA,EAAA,MAAM,IAAA,GAAO,MAAM,iBAAA,CAAkB,IAAI,CAAA;AACzC,EAAA,IAAI,CAAC,iBAAA,CAAkB,IAAI,CAAA,EAAG;AAC1B,IAAA,MAAM,IAAI,mBAAA,CAAoB,qCAAA,EAAuC,kBAAA,EAAoB;AAAA,MACrF,QAAQ,IAAA,CAAK,MAAA;AAAA,MACb,OAAA,EAAS;AAAA,KACZ,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,IAAA;AACX;AAMA,eAAsB,IAAI,KAAA,EAAgC;AACtD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,MAAM,IAAI,mBAAA,CAAoB,uCAAA,EAAyC,cAAc,CAAA;AAAA,EACzF;AAEA,EAAA,gBAAA,CAAiB,MAAM,CAAA;AACvB,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,gBAAA,CAAiB,KAAA,CAAM,YAAY,CAAA,EAAG;AACjD,IAAA,MAAM,IAAI,mBAAA,CAAoB,mCAAA,EAAqC,eAAe,CAAA;AAAA,EACtF;AACA,EAAA,IAAI,CAAC,gBAAA,CAAiB,KAAA,CAAM,SAAS,CAAA,EAAG;AACpC,IAAA,MAAM,IAAI,mBAAA,CAAoB,gCAAA,EAAkC,eAAe,CAAA;AAAA,EACnF;AACA,EAAA,IAAI,CAAC,gBAAA,CAAiB,KAAA,CAAM,WAAW,CAAA,EAAG;AACtC,IAAA,MAAM,IAAI,mBAAA,CAAoB,kCAAA,EAAoC,eAAe,CAAA;AAAA,EACrF;AAEA,EAAA,MAAM,CAAC,EAAE,OAAA,EAAQ,EAAG,MAAM,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,aAAA,CAAc,KAAK,CAAA,EAAG,UAAA,EAAY,CAAC,CAAA;AAEpF,EAAA,MAAA,CAAO;AAAA,IACH,OAAA;AAAA,IACA,SAAS,KAAA,CAAM,OAAA;AAAA,IACf,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,UAAU,KAAA,CAAM,QAAA;AAAA,IAChB,SAAS,KAAA,CAAM,OAAA;AAAA,IACf,YAAY,KAAA,CAAM,UAAA;AAAA,IAClB,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,GAAI,KAAA,CAAM,KAAA,IAAS;AAAC,GACvB,CAAA;AACL","file":"index.cjs","sourcesContent":["export type EscrowWidget = (options: Record<string, unknown>) => void;\n\nexport type EscrowCheckoutErrorCode =\n | 'NOT_INITIALIZED'\n | 'BROWSER_ONLY'\n | 'INVALID_INPUT'\n | 'WIDGET_LOAD'\n | 'NETWORK'\n | 'SESSION_CREATE'\n | 'SESSION_RESPONSE';\n\nexport class EscrowCheckoutError extends Error {\n code: EscrowCheckoutErrorCode;\n status?: number;\n details?: unknown;\n\n constructor(message: string, code: EscrowCheckoutErrorCode, options?: { status?: number; details?: unknown }) {\n super(message);\n this.name = 'EscrowCheckoutError';\n this.code = code;\n this.status = options?.status;\n this.details = options?.details;\n }\n}\n\nexport interface InitConfig {\n publicKey: string; // publishable key only\n /**\n * Optional overrides for experts. End users don't need to set these.\n */\n scriptUrlOverride?: string;\r\n globalName?: string; // default 'EscrowCheckout'\r\n crossOrigin?: '' | 'anonymous' | 'use-credentials';\r\n}\r\n\r\nexport interface PayInput {\r\n paymentToken: string;\r\n reference: string;\r\n redirectUrl: string;\r\n logoUrl?: string;\r\n brand?: string;\r\n customerId?: string;\r\n /**\r\n * Extra fields supported by the widget.\r\n */\r\n extra?: Record<string, unknown>;\r\n callback?: (result: any) => void;\r\n onClose?: () => void;\r\n}\r\n\r\nexport interface SessionResponse {\r\n session: unknown;\r\n}\r\n\r\nconst DEFAULT_SCRIPT_URL = 'https://checkout.payluk.ng/escrow-checkout.min.js';\r\nconst DEFAULT_GLOBAL_NAME = 'EscrowCheckout';\r\n\r\ntype ResolvedConfig = Required<Pick<InitConfig, 'publicKey'>> & {\r\n scriptUrl: string;\r\n globalName: string;\r\n crossOrigin: '' | 'anonymous' | 'use-credentials';\r\n};\r\n\r\nlet CONFIG: ResolvedConfig | null = null;\r\n\r\n/**\r\n * Initialize the library once (e.g., in app bootstrap).\r\n * Hides script URL and session creation from consumers.\r\n */\r\nexport function initEscrowCheckout(config: InitConfig): void {\n if (!isNonEmptyString(config?.publicKey)) {\n throw new EscrowCheckoutError('initEscrowCheckout(...) requires \"publicKey\".', 'INVALID_INPUT');\n }\n CONFIG = {\n publicKey: config.publicKey.trim(),\n scriptUrl: config.scriptUrlOverride || DEFAULT_SCRIPT_URL,\n globalName: config.globalName || DEFAULT_GLOBAL_NAME,\n crossOrigin: config.crossOrigin ?? 'anonymous'\n };\n}\n\r\n/**\r\n * Narrow a provided config to ResolvedConfig or throw if not initialized.\r\n */\r\nfunction assertConfigured(config: ResolvedConfig | null): asserts config is ResolvedConfig {\n if (!config) {\n throw new EscrowCheckoutError(\n 'Escrow checkout not initialized. Call initEscrowCheckout({ publicKey }) first.',\n 'NOT_INITIALIZED'\n );\n }\n}\n\nfunction normalizeError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === 'string') return new Error(error);\n return new Error('Unknown error');\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isSessionResponse(value: unknown): value is SessionResponse {\n if (!value || typeof value !== 'object') return false;\n return 'session' in value;\n}\n\nasync function parseErrorBody(resp: Response): Promise<{ message?: string; details?: unknown }> {\n const text = await resp.text();\n if (!text) return {};\n\n try {\n const json = JSON.parse(text) as { message?: unknown };\n const message = typeof json?.message === 'string' ? json.message : undefined;\n return { message, details: json };\n } catch {\n return { message: text, details: text };\n }\n}\n\nasync function parseJsonResponse(resp: Response): Promise<unknown> {\n try {\n return await resp.json();\n } catch {\n throw new EscrowCheckoutError('Invalid JSON response from session endpoint.', 'SESSION_RESPONSE', {\n status: resp.status\n });\n }\n}\n\n/**\n * Internal: load the widget script once and return the global function.\n */\nasync function loadWidget(): Promise<EscrowWidget> {\n assertConfigured(CONFIG);\n const { scriptUrl, globalName, crossOrigin } = CONFIG;\r\n\r\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('EscrowCheckout can only be loaded in the browser.', 'BROWSER_ONLY');\n }\n\r\n const win = window as any;\r\n win.__escrowCheckoutLoader ??= {};\r\n\r\n if (win.__escrowCheckoutLoader[scriptUrl]) {\r\n return win.__escrowCheckoutLoader[scriptUrl];\r\n }\r\n\r\n if (typeof win[globalName] === 'function') {\r\n const fn = win[globalName] as EscrowWidget;\r\n win.__escrowCheckoutLoader[scriptUrl] = Promise.resolve(fn);\r\n return fn;\r\n }\r\n\r\n win.__escrowCheckoutLoader[scriptUrl] = new Promise<EscrowWidget>((resolve, reject) => {\r\n const script = document.createElement('script');\r\n script.src = scriptUrl;\r\n script.async = true;\r\n if (crossOrigin) script.crossOrigin = crossOrigin;\r\n\r\n script.onload = () => {\r\n const fn = win[globalName];\r\n if (typeof fn === 'function') resolve(fn as EscrowWidget);\n else {\n reject(\n new EscrowCheckoutError(\n `Escrow checkout script loaded, but window.${globalName} is not a function.`,\n 'WIDGET_LOAD'\n )\n );\n }\n };\n\n script.onerror = () =>\n reject(new EscrowCheckoutError(`Failed to load escrow checkout script: ${scriptUrl}`, 'WIDGET_LOAD'));\n\n document.head.appendChild(script);\n });\n\r\n return win.__escrowCheckoutLoader[scriptUrl];\r\n}\r\n\r\n/**\r\n * Internal: create a checkout session by calling your API.\r\n * This is intentionally inside the lib (user doesn't implement it).\r\n */\r\nasync function createSession(input: PayInput): Promise<SessionResponse> {\n assertConfigured(CONFIG);\n const { publicKey } = CONFIG;\n const apiBaseUrl = publicKey.startsWith('pk_live_') ? 'https://live.payluk.ng' : 'https://staging.live.payluk.ng';\n let resp: Response;\n\n try {\n resp = await fetch(`${apiBaseUrl}/v1/checkout/session`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentToken: input.paymentToken,\n redirectUrl: input.redirectUrl,\n reference: input.reference,\n customerId: input.customerId,\n publicKey\n })\n });\n } catch (error) {\n const normalized = normalizeError(error);\n throw new EscrowCheckoutError('Network error while creating checkout session.', 'NETWORK', {\n details: normalized.message\n });\n }\n\n if (!resp.ok) {\n const { message, details } = await parseErrorBody(resp);\n throw new EscrowCheckoutError(message || `Failed to create session (HTTP ${resp.status}).`, 'SESSION_CREATE', {\n status: resp.status,\n details\n });\n }\n\n const data = await parseJsonResponse(resp);\n if (!isSessionResponse(data)) {\n throw new EscrowCheckoutError('Session response missing \"session\".', 'SESSION_RESPONSE', {\n status: resp.status,\n details: data\n });\n }\n\n return data as SessionResponse;\n}\n\r\n/**\r\n * Public API: create the session and open the widget.\r\n * Consumers only supply business inputs (no script URL, no API calls).\r\n */\r\nexport async function pay(input: PayInput): Promise<void> {\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('pay(...) can only run in the browser.', 'BROWSER_ONLY');\n }\n\n assertConfigured(CONFIG);\n if (!input || !isNonEmptyString(input.paymentToken)) {\n throw new EscrowCheckoutError('pay(...) requires \"paymentToken\".', 'INVALID_INPUT');\n }\n if (!isNonEmptyString(input.reference)) {\n throw new EscrowCheckoutError('pay(...) requires \"reference\".', 'INVALID_INPUT');\n }\n if (!isNonEmptyString(input.redirectUrl)) {\n throw new EscrowCheckoutError('pay(...) requires \"redirectUrl\".', 'INVALID_INPUT');\n }\n\n const [{ session }, widget] = await Promise.all([createSession(input), loadWidget()]);\n\n widget({\n session,\n logoUrl: input.logoUrl,\n brand: input.brand,\n callback: input.callback,\n onClose: input.onClose,\n customerId: input.customerId,\n publicKey: CONFIG.publicKey,\n ...(input.extra ?? {})\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["export type EscrowWidget = (options: Record<string, unknown>) => void;\n\nexport type EscrowCheckoutErrorCode =\n | 'NOT_INITIALIZED'\n | 'BROWSER_ONLY'\n | 'INVALID_INPUT'\n | 'WIDGET_LOAD'\n | 'NETWORK'\n | 'SESSION_CREATE'\n | 'SESSION_RESPONSE';\n\nexport class EscrowCheckoutError extends Error {\n code: EscrowCheckoutErrorCode;\n status?: number;\n details?: unknown;\n\n constructor(message: string, code: EscrowCheckoutErrorCode, options?: { status?: number; details?: unknown }) {\n super(message);\n this.name = 'EscrowCheckoutError';\n this.code = code;\n this.status = options?.status;\n this.details = options?.details;\n }\n}\n\nexport interface InitConfig {\n publicKey: string; // publishable key only\n /**\n * Optional overrides for experts. End users don't need to set these.\n */\n scriptUrlOverride?: string;\n globalName?: string; // default 'EscrowCheckout'\n crossOrigin?: '' | 'anonymous' | 'use-credentials';\n}\n\nexport interface PayInput {\n /**\n * Single escrow payment token or array of payment tokens for multi-escrow checkout.\n * When providing multiple tokens, they must all belong to the same merchant.\n */\n paymentToken: string | string[];\n reference: string;\n redirectUrl: string;\n logoUrl?: string;\n brand?: string;\n customerId?: string;\n /**\n * Extra fields supported by the widget.\n */\n extra?: Record<string, unknown>;\n callback?: (result: any) => void;\n onClose?: () => void;\n}\n\nexport interface SessionResponse {\n session: unknown;\n}\n\nconst DEFAULT_SCRIPT_URL = 'https://checkout.payluk.ng/escrow-checkout.min.js';\nconst DEFAULT_GLOBAL_NAME = 'EscrowCheckout';\n\ntype ResolvedConfig = Required<Pick<InitConfig, 'publicKey'>> & {\n scriptUrl: string;\n globalName: string;\n crossOrigin: '' | 'anonymous' | 'use-credentials';\n};\n\nlet CONFIG: ResolvedConfig | null = null;\n\n/**\n * Initialize the library once (e.g., in app bootstrap).\n * Hides script URL and session creation from consumers.\n */\nexport function initEscrowCheckout(config: InitConfig): void {\n if (!isNonEmptyString(config?.publicKey)) {\n throw new EscrowCheckoutError('initEscrowCheckout(...) requires \"publicKey\".', 'INVALID_INPUT');\n }\n CONFIG = {\n publicKey: config.publicKey.trim(),\n scriptUrl: config.scriptUrlOverride || DEFAULT_SCRIPT_URL,\n globalName: config.globalName || DEFAULT_GLOBAL_NAME,\n crossOrigin: config.crossOrigin ?? 'anonymous'\n };\n}\n\n/**\n * Narrow a provided config to ResolvedConfig or throw if not initialized.\n */\nfunction assertConfigured(config: ResolvedConfig | null): asserts config is ResolvedConfig {\n if (!config) {\n throw new EscrowCheckoutError(\n 'Escrow checkout not initialized. Call initEscrowCheckout({ publicKey }) first.',\n 'NOT_INITIALIZED'\n );\n }\n}\n\nfunction normalizeError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === 'string') return new Error(error);\n return new Error('Unknown error');\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isValidPaymentToken(value: unknown): value is string | string[] {\n // Check if it's a valid string\n if (isNonEmptyString(value)) {\n return true;\n }\n\n // Check if it's a valid array of strings\n if (Array.isArray(value)) {\n return value.length > 0 && value.every((token) => isNonEmptyString(token));\n }\n\n return false;\n}\n\nfunction isSessionResponse(value: unknown): value is SessionResponse {\n if (!value || typeof value !== 'object') return false;\n return 'session' in value;\n}\n\nasync function parseErrorBody(resp: Response): Promise<{ message?: string; details?: unknown }> {\n const text = await resp.text();\n if (!text) return {};\n\n try {\n const json = JSON.parse(text) as { message?: unknown };\n const message = typeof json?.message === 'string' ? json.message : undefined;\n return { message, details: json };\n } catch {\n return { message: text, details: text };\n }\n}\n\nasync function parseJsonResponse(resp: Response): Promise<unknown> {\n try {\n return await resp.json();\n } catch {\n throw new EscrowCheckoutError('Invalid JSON response from session endpoint.', 'SESSION_RESPONSE', {\n status: resp.status\n });\n }\n}\n\n/**\n * Internal: load the widget script once and return the global function.\n */\nasync function loadWidget(): Promise<EscrowWidget> {\n assertConfigured(CONFIG);\n const { scriptUrl, globalName, crossOrigin } = CONFIG;\n\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('EscrowCheckout can only be loaded in the browser.', 'BROWSER_ONLY');\n }\n\n const win = window as any;\n win.__escrowCheckoutLoader ??= {};\n\n if (win.__escrowCheckoutLoader[scriptUrl]) {\n return win.__escrowCheckoutLoader[scriptUrl];\n }\n\n if (typeof win[globalName] === 'function') {\n const fn = win[globalName] as EscrowWidget;\n win.__escrowCheckoutLoader[scriptUrl] = Promise.resolve(fn);\n return fn;\n }\n\n win.__escrowCheckoutLoader[scriptUrl] = new Promise<EscrowWidget>((resolve, reject) => {\n const script = document.createElement('script');\n script.src = scriptUrl;\n script.async = true;\n if (crossOrigin) script.crossOrigin = crossOrigin;\n\n script.onload = () => {\n const fn = win[globalName];\n if (typeof fn === 'function') resolve(fn as EscrowWidget);\n else {\n reject(\n new EscrowCheckoutError(\n `Escrow checkout script loaded, but window.${globalName} is not a function.`,\n 'WIDGET_LOAD'\n )\n );\n }\n };\n\n script.onerror = () =>\n reject(new EscrowCheckoutError(`Failed to load escrow checkout script: ${scriptUrl}`, 'WIDGET_LOAD'));\n\n document.head.appendChild(script);\n });\n\n return win.__escrowCheckoutLoader[scriptUrl];\n}\n\n/**\n * Internal: create a checkout session by calling your API.\n * This is intentionally inside the lib (user doesn't implement it).\n */\nasync function createSession(input: PayInput): Promise<SessionResponse> {\n assertConfigured(CONFIG);\n const { publicKey } = CONFIG;\n const apiBaseUrl = publicKey.startsWith('pk_live_') ? 'https://live.payluk.ng' : 'https://staging.live.payluk.ng';\n let resp: Response;\n\n try {\n resp = await fetch(`${apiBaseUrl}/v1/checkout/session`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentToken: input.paymentToken,\n redirectUrl: input.redirectUrl,\n reference: input.reference,\n customerId: input.customerId,\n publicKey\n })\n });\n } catch (error) {\n const normalized = normalizeError(error);\n throw new EscrowCheckoutError('Network error while creating checkout session.', 'NETWORK', {\n details: normalized.message\n });\n }\n\n if (!resp.ok) {\n const { message, details } = await parseErrorBody(resp);\n throw new EscrowCheckoutError(message || `Failed to create session (HTTP ${resp.status}).`, 'SESSION_CREATE', {\n status: resp.status,\n details\n });\n }\n\n const data = await parseJsonResponse(resp);\n if (!isSessionResponse(data)) {\n throw new EscrowCheckoutError('Session response missing \"session\".', 'SESSION_RESPONSE', {\n status: resp.status,\n details: data\n });\n }\n\n return data as SessionResponse;\n}\n\n/**\n * Public API: create the session and open the widget.\n * Consumers only supply business inputs (no script URL, no API calls).\n */\nexport async function pay(input: PayInput): Promise<void> {\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('pay(...) can only run in the browser.', 'BROWSER_ONLY');\n }\n\n assertConfigured(CONFIG);\n if (!input || !isValidPaymentToken(input.paymentToken)) {\n throw new EscrowCheckoutError(\n 'pay(...) requires \"paymentToken\" (must be a non-empty string or array of non-empty strings).',\n 'INVALID_INPUT'\n );\n }\n if (!isNonEmptyString(input.reference)) {\n throw new EscrowCheckoutError('pay(...) requires \"reference\".', 'INVALID_INPUT');\n }\n if (!isNonEmptyString(input.redirectUrl)) {\n throw new EscrowCheckoutError('pay(...) requires \"redirectUrl\".', 'INVALID_INPUT');\n }\n\n const [{ session }, widget] = await Promise.all([createSession(input), loadWidget()]);\n\n widget({\n session,\n logoUrl: input.logoUrl,\n brand: input.brand,\n callback: input.callback,\n onClose: input.onClose,\n customerId: input.customerId,\n publicKey: CONFIG.publicKey,\n ...(input.extra ?? {})\n });\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -19,7 +19,11 @@ interface InitConfig {
|
|
|
19
19
|
crossOrigin?: '' | 'anonymous' | 'use-credentials';
|
|
20
20
|
}
|
|
21
21
|
interface PayInput {
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Single escrow payment token or array of payment tokens for multi-escrow checkout.
|
|
24
|
+
* When providing multiple tokens, they must all belong to the same merchant.
|
|
25
|
+
*/
|
|
26
|
+
paymentToken: string | string[];
|
|
23
27
|
reference: string;
|
|
24
28
|
redirectUrl: string;
|
|
25
29
|
logoUrl?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,11 @@ interface InitConfig {
|
|
|
19
19
|
crossOrigin?: '' | 'anonymous' | 'use-credentials';
|
|
20
20
|
}
|
|
21
21
|
interface PayInput {
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Single escrow payment token or array of payment tokens for multi-escrow checkout.
|
|
24
|
+
* When providing multiple tokens, they must all belong to the same merchant.
|
|
25
|
+
*/
|
|
26
|
+
paymentToken: string | string[];
|
|
23
27
|
reference: string;
|
|
24
28
|
redirectUrl: string;
|
|
25
29
|
logoUrl?: string;
|
package/dist/index.js
CHANGED
package/dist/react/index.cjs
CHANGED
|
@@ -32,6 +32,15 @@ function normalizeError(error) {
|
|
|
32
32
|
function isNonEmptyString(value) {
|
|
33
33
|
return typeof value === "string" && value.trim().length > 0;
|
|
34
34
|
}
|
|
35
|
+
function isValidPaymentToken(value) {
|
|
36
|
+
if (isNonEmptyString(value)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
return value.length > 0 && value.every((token) => isNonEmptyString(token));
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
35
44
|
function isSessionResponse(value) {
|
|
36
45
|
if (!value || typeof value !== "object") return false;
|
|
37
46
|
return "session" in value;
|
|
@@ -138,8 +147,11 @@ async function pay(input) {
|
|
|
138
147
|
throw new EscrowCheckoutError("pay(...) can only run in the browser.", "BROWSER_ONLY");
|
|
139
148
|
}
|
|
140
149
|
assertConfigured();
|
|
141
|
-
if (!input || !
|
|
142
|
-
throw new EscrowCheckoutError(
|
|
150
|
+
if (!input || !isValidPaymentToken(input.paymentToken)) {
|
|
151
|
+
throw new EscrowCheckoutError(
|
|
152
|
+
'pay(...) requires "paymentToken" (must be a non-empty string or array of non-empty strings).',
|
|
153
|
+
"INVALID_INPUT"
|
|
154
|
+
);
|
|
143
155
|
}
|
|
144
156
|
if (!isNonEmptyString(input.reference)) {
|
|
145
157
|
throw new EscrowCheckoutError('pay(...) requires "reference".', "INVALID_INPUT");
|
package/dist/react/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/react/useEscrowCheckout.ts","../../src/react/EscrowCheckoutButton.tsx"],"names":["useState","normalizeError","useCallback","pay","useEffect","useMemo","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["export type EscrowWidget = (options: Record<string, unknown>) => void;\n\nexport type EscrowCheckoutErrorCode =\n | 'NOT_INITIALIZED'\n | 'BROWSER_ONLY'\n | 'INVALID_INPUT'\n | 'WIDGET_LOAD'\n | 'NETWORK'\n | 'SESSION_CREATE'\n | 'SESSION_RESPONSE';\n\nexport class EscrowCheckoutError extends Error {\n code: EscrowCheckoutErrorCode;\n status?: number;\n details?: unknown;\n\n constructor(message: string, code: EscrowCheckoutErrorCode, options?: { status?: number; details?: unknown }) {\n super(message);\n this.name = 'EscrowCheckoutError';\n this.code = code;\n this.status = options?.status;\n this.details = options?.details;\n }\n}\n\nexport interface InitConfig {\n publicKey: string; // publishable key only\n /**\n * Optional overrides for experts. End users don't need to set these.\n */\n scriptUrlOverride?: string;\r\n globalName?: string; // default 'EscrowCheckout'\r\n crossOrigin?: '' | 'anonymous' | 'use-credentials';\r\n}\r\n\r\nexport interface PayInput {\r\n paymentToken: string;\r\n reference: string;\r\n redirectUrl: string;\r\n logoUrl?: string;\r\n brand?: string;\r\n customerId?: string;\r\n /**\r\n * Extra fields supported by the widget.\r\n */\r\n extra?: Record<string, unknown>;\r\n callback?: (result: any) => void;\r\n onClose?: () => void;\r\n}\r\n\r\nexport interface SessionResponse {\r\n session: unknown;\r\n}\r\n\r\nconst DEFAULT_SCRIPT_URL = 'https://checkout.payluk.ng/escrow-checkout.min.js';\r\nconst DEFAULT_GLOBAL_NAME = 'EscrowCheckout';\r\n\r\ntype ResolvedConfig = Required<Pick<InitConfig, 'publicKey'>> & {\r\n scriptUrl: string;\r\n globalName: string;\r\n crossOrigin: '' | 'anonymous' | 'use-credentials';\r\n};\r\n\r\nlet CONFIG: ResolvedConfig | null = null;\r\n\r\n/**\r\n * Initialize the library once (e.g., in app bootstrap).\r\n * Hides script URL and session creation from consumers.\r\n */\r\nexport function initEscrowCheckout(config: InitConfig): void {\n if (!isNonEmptyString(config?.publicKey)) {\n throw new EscrowCheckoutError('initEscrowCheckout(...) requires \"publicKey\".', 'INVALID_INPUT');\n }\n CONFIG = {\n publicKey: config.publicKey.trim(),\n scriptUrl: config.scriptUrlOverride || DEFAULT_SCRIPT_URL,\n globalName: config.globalName || DEFAULT_GLOBAL_NAME,\n crossOrigin: config.crossOrigin ?? 'anonymous'\n };\n}\n\r\n/**\r\n * Narrow a provided config to ResolvedConfig or throw if not initialized.\r\n */\r\nfunction assertConfigured(config: ResolvedConfig | null): asserts config is ResolvedConfig {\n if (!config) {\n throw new EscrowCheckoutError(\n 'Escrow checkout not initialized. Call initEscrowCheckout({ publicKey }) first.',\n 'NOT_INITIALIZED'\n );\n }\n}\n\nfunction normalizeError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === 'string') return new Error(error);\n return new Error('Unknown error');\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isSessionResponse(value: unknown): value is SessionResponse {\n if (!value || typeof value !== 'object') return false;\n return 'session' in value;\n}\n\nasync function parseErrorBody(resp: Response): Promise<{ message?: string; details?: unknown }> {\n const text = await resp.text();\n if (!text) return {};\n\n try {\n const json = JSON.parse(text) as { message?: unknown };\n const message = typeof json?.message === 'string' ? json.message : undefined;\n return { message, details: json };\n } catch {\n return { message: text, details: text };\n }\n}\n\nasync function parseJsonResponse(resp: Response): Promise<unknown> {\n try {\n return await resp.json();\n } catch {\n throw new EscrowCheckoutError('Invalid JSON response from session endpoint.', 'SESSION_RESPONSE', {\n status: resp.status\n });\n }\n}\n\n/**\n * Internal: load the widget script once and return the global function.\n */\nasync function loadWidget(): Promise<EscrowWidget> {\n assertConfigured(CONFIG);\n const { scriptUrl, globalName, crossOrigin } = CONFIG;\r\n\r\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('EscrowCheckout can only be loaded in the browser.', 'BROWSER_ONLY');\n }\n\r\n const win = window as any;\r\n win.__escrowCheckoutLoader ??= {};\r\n\r\n if (win.__escrowCheckoutLoader[scriptUrl]) {\r\n return win.__escrowCheckoutLoader[scriptUrl];\r\n }\r\n\r\n if (typeof win[globalName] === 'function') {\r\n const fn = win[globalName] as EscrowWidget;\r\n win.__escrowCheckoutLoader[scriptUrl] = Promise.resolve(fn);\r\n return fn;\r\n }\r\n\r\n win.__escrowCheckoutLoader[scriptUrl] = new Promise<EscrowWidget>((resolve, reject) => {\r\n const script = document.createElement('script');\r\n script.src = scriptUrl;\r\n script.async = true;\r\n if (crossOrigin) script.crossOrigin = crossOrigin;\r\n\r\n script.onload = () => {\r\n const fn = win[globalName];\r\n if (typeof fn === 'function') resolve(fn as EscrowWidget);\n else {\n reject(\n new EscrowCheckoutError(\n `Escrow checkout script loaded, but window.${globalName} is not a function.`,\n 'WIDGET_LOAD'\n )\n );\n }\n };\n\n script.onerror = () =>\n reject(new EscrowCheckoutError(`Failed to load escrow checkout script: ${scriptUrl}`, 'WIDGET_LOAD'));\n\n document.head.appendChild(script);\n });\n\r\n return win.__escrowCheckoutLoader[scriptUrl];\r\n}\r\n\r\n/**\r\n * Internal: create a checkout session by calling your API.\r\n * This is intentionally inside the lib (user doesn't implement it).\r\n */\r\nasync function createSession(input: PayInput): Promise<SessionResponse> {\n assertConfigured(CONFIG);\n const { publicKey } = CONFIG;\n const apiBaseUrl = publicKey.startsWith('pk_live_') ? 'https://live.payluk.ng' : 'https://staging.live.payluk.ng';\n let resp: Response;\n\n try {\n resp = await fetch(`${apiBaseUrl}/v1/checkout/session`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentToken: input.paymentToken,\n redirectUrl: input.redirectUrl,\n reference: input.reference,\n customerId: input.customerId,\n publicKey\n })\n });\n } catch (error) {\n const normalized = normalizeError(error);\n throw new EscrowCheckoutError('Network error while creating checkout session.', 'NETWORK', {\n details: normalized.message\n });\n }\n\n if (!resp.ok) {\n const { message, details } = await parseErrorBody(resp);\n throw new EscrowCheckoutError(message || `Failed to create session (HTTP ${resp.status}).`, 'SESSION_CREATE', {\n status: resp.status,\n details\n });\n }\n\n const data = await parseJsonResponse(resp);\n if (!isSessionResponse(data)) {\n throw new EscrowCheckoutError('Session response missing \"session\".', 'SESSION_RESPONSE', {\n status: resp.status,\n details: data\n });\n }\n\n return data as SessionResponse;\n}\n\r\n/**\r\n * Public API: create the session and open the widget.\r\n * Consumers only supply business inputs (no script URL, no API calls).\r\n */\r\nexport async function pay(input: PayInput): Promise<void> {\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('pay(...) can only run in the browser.', 'BROWSER_ONLY');\n }\n\n assertConfigured(CONFIG);\n if (!input || !isNonEmptyString(input.paymentToken)) {\n throw new EscrowCheckoutError('pay(...) requires \"paymentToken\".', 'INVALID_INPUT');\n }\n if (!isNonEmptyString(input.reference)) {\n throw new EscrowCheckoutError('pay(...) requires \"reference\".', 'INVALID_INPUT');\n }\n if (!isNonEmptyString(input.redirectUrl)) {\n throw new EscrowCheckoutError('pay(...) requires \"redirectUrl\".', 'INVALID_INPUT');\n }\n\n const [{ session }, widget] = await Promise.all([createSession(input), loadWidget()]);\n\n widget({\n session,\n logoUrl: input.logoUrl,\n brand: input.brand,\n callback: input.callback,\n onClose: input.onClose,\n customerId: input.customerId,\n publicKey: CONFIG.publicKey,\n ...(input.extra ?? {})\n });\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\r\nimport { pay as corePay } from '../index';\r\n\r\nexport interface UseEscrowCheckoutResult {\r\n ready: boolean;\r\n loading: boolean;\r\n error: Error | null;\r\n pay: typeof corePay;\r\n}\r\n\r\n/**\r\n * React hook that preloads the widget script (implicitly via pay) and exposes pay(...)\r\n * Since the library abstracts script loading and session creation, the hook is thin.\r\n */\r\nexport function useEscrowCheckout(): UseEscrowCheckoutResult {\n const [ready, setReady] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const normalizeError = useCallback((err: unknown): Error => {\n if (err instanceof Error) return err;\n if (typeof err === 'string') return new Error(err);\n return new Error('Unknown error');\n }, []);\n\n // Lazy mark \"ready\" after first successful pay or script load attempt.\n // If you want to preload the script earlier, you may trigger a no-op pay with a dry-run endpoint on your side.\n\n // Provide a stable pay function that reports loading state.\n const pay = useCallback<typeof corePay>(async (input) => {\n setLoading(true);\r\n try {\r\n await corePay(input);\n setReady(true);\n setError(null);\n } catch (e) {\n const normalized = normalizeError(e);\n setError(normalized);\n throw normalized;\n } finally {\n setLoading(false);\n }\n }, [normalizeError]);\n\r\n // In SSR, mark not loading\n useEffect(() => {\n if (typeof window === 'undefined') {\n setLoading(false);\n }\n }, []);\n\r\n return useMemo(() => ({ ready, loading, error, pay }), [ready, loading, error, pay]);\r\n}\n","import React, { useCallback } from 'react';\r\nimport { useEscrowCheckout } from './useEscrowCheckout';\r\n\r\nexport interface EscrowCheckoutButtonProps {\r\n paymentToken: string;\r\n reference: string;\r\n redirectUrl: string;\r\n children?: React.ReactNode;\r\n disabled?: boolean;\r\n className?: string;\r\n title?: string;\r\n\r\n brand?: string;\r\n customerId?: string;\r\n logoUrl?: string;\r\n callback?: (result: any) => void;\r\n onClose?: () => void;\r\n extra?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Button that triggers pay(...) with your provided inputs.\r\n * The library handles both session creation and widget loading.\r\n */\r\nexport function EscrowCheckoutButton({\r\n paymentToken,\r\n reference,\r\n redirectUrl,\r\n brand,\r\n logoUrl,\r\n callback,\r\n onClose,\r\n extra,\r\n children = 'Pay',\r\n disabled,\r\n className,\r\n title,\r\n customerId\r\n }: EscrowCheckoutButtonProps) {\r\n const { loading, error, pay } = useEscrowCheckout();\n\n const handleClick = useCallback(async () => {\n try {\n await pay({\n paymentToken,\n reference,\n redirectUrl,\n brand,\n logoUrl,\n callback,\n onClose,\n extra,\n customerId\n });\n } catch {\n // Error state is already exposed via the hook.\n }\n }, [pay, paymentToken, reference, redirectUrl, brand, logoUrl, callback, onClose, extra, customerId]);\n\r\n return (\r\n <button\r\n type=\"button\"\n onClick={handleClick}\n disabled={disabled || loading}\n className={className}\n aria-disabled={disabled || loading}\n title={title ?? (error ? error.message : undefined)}\n >\n {children}\n </button>\n );\r\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/react/useEscrowCheckout.ts","../../src/react/EscrowCheckoutButton.tsx"],"names":["useState","normalizeError","useCallback","pay","useEffect","useMemo","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["export type EscrowWidget = (options: Record<string, unknown>) => void;\n\nexport type EscrowCheckoutErrorCode =\n | 'NOT_INITIALIZED'\n | 'BROWSER_ONLY'\n | 'INVALID_INPUT'\n | 'WIDGET_LOAD'\n | 'NETWORK'\n | 'SESSION_CREATE'\n | 'SESSION_RESPONSE';\n\nexport class EscrowCheckoutError extends Error {\n code: EscrowCheckoutErrorCode;\n status?: number;\n details?: unknown;\n\n constructor(message: string, code: EscrowCheckoutErrorCode, options?: { status?: number; details?: unknown }) {\n super(message);\n this.name = 'EscrowCheckoutError';\n this.code = code;\n this.status = options?.status;\n this.details = options?.details;\n }\n}\n\nexport interface InitConfig {\n publicKey: string; // publishable key only\n /**\n * Optional overrides for experts. End users don't need to set these.\n */\n scriptUrlOverride?: string;\n globalName?: string; // default 'EscrowCheckout'\n crossOrigin?: '' | 'anonymous' | 'use-credentials';\n}\n\nexport interface PayInput {\n /**\n * Single escrow payment token or array of payment tokens for multi-escrow checkout.\n * When providing multiple tokens, they must all belong to the same merchant.\n */\n paymentToken: string | string[];\n reference: string;\n redirectUrl: string;\n logoUrl?: string;\n brand?: string;\n customerId?: string;\n /**\n * Extra fields supported by the widget.\n */\n extra?: Record<string, unknown>;\n callback?: (result: any) => void;\n onClose?: () => void;\n}\n\nexport interface SessionResponse {\n session: unknown;\n}\n\nconst DEFAULT_SCRIPT_URL = 'https://checkout.payluk.ng/escrow-checkout.min.js';\nconst DEFAULT_GLOBAL_NAME = 'EscrowCheckout';\n\ntype ResolvedConfig = Required<Pick<InitConfig, 'publicKey'>> & {\n scriptUrl: string;\n globalName: string;\n crossOrigin: '' | 'anonymous' | 'use-credentials';\n};\n\nlet CONFIG: ResolvedConfig | null = null;\n\n/**\n * Initialize the library once (e.g., in app bootstrap).\n * Hides script URL and session creation from consumers.\n */\nexport function initEscrowCheckout(config: InitConfig): void {\n if (!isNonEmptyString(config?.publicKey)) {\n throw new EscrowCheckoutError('initEscrowCheckout(...) requires \"publicKey\".', 'INVALID_INPUT');\n }\n CONFIG = {\n publicKey: config.publicKey.trim(),\n scriptUrl: config.scriptUrlOverride || DEFAULT_SCRIPT_URL,\n globalName: config.globalName || DEFAULT_GLOBAL_NAME,\n crossOrigin: config.crossOrigin ?? 'anonymous'\n };\n}\n\n/**\n * Narrow a provided config to ResolvedConfig or throw if not initialized.\n */\nfunction assertConfigured(config: ResolvedConfig | null): asserts config is ResolvedConfig {\n if (!config) {\n throw new EscrowCheckoutError(\n 'Escrow checkout not initialized. Call initEscrowCheckout({ publicKey }) first.',\n 'NOT_INITIALIZED'\n );\n }\n}\n\nfunction normalizeError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === 'string') return new Error(error);\n return new Error('Unknown error');\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isValidPaymentToken(value: unknown): value is string | string[] {\n // Check if it's a valid string\n if (isNonEmptyString(value)) {\n return true;\n }\n\n // Check if it's a valid array of strings\n if (Array.isArray(value)) {\n return value.length > 0 && value.every((token) => isNonEmptyString(token));\n }\n\n return false;\n}\n\nfunction isSessionResponse(value: unknown): value is SessionResponse {\n if (!value || typeof value !== 'object') return false;\n return 'session' in value;\n}\n\nasync function parseErrorBody(resp: Response): Promise<{ message?: string; details?: unknown }> {\n const text = await resp.text();\n if (!text) return {};\n\n try {\n const json = JSON.parse(text) as { message?: unknown };\n const message = typeof json?.message === 'string' ? json.message : undefined;\n return { message, details: json };\n } catch {\n return { message: text, details: text };\n }\n}\n\nasync function parseJsonResponse(resp: Response): Promise<unknown> {\n try {\n return await resp.json();\n } catch {\n throw new EscrowCheckoutError('Invalid JSON response from session endpoint.', 'SESSION_RESPONSE', {\n status: resp.status\n });\n }\n}\n\n/**\n * Internal: load the widget script once and return the global function.\n */\nasync function loadWidget(): Promise<EscrowWidget> {\n assertConfigured(CONFIG);\n const { scriptUrl, globalName, crossOrigin } = CONFIG;\n\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('EscrowCheckout can only be loaded in the browser.', 'BROWSER_ONLY');\n }\n\n const win = window as any;\n win.__escrowCheckoutLoader ??= {};\n\n if (win.__escrowCheckoutLoader[scriptUrl]) {\n return win.__escrowCheckoutLoader[scriptUrl];\n }\n\n if (typeof win[globalName] === 'function') {\n const fn = win[globalName] as EscrowWidget;\n win.__escrowCheckoutLoader[scriptUrl] = Promise.resolve(fn);\n return fn;\n }\n\n win.__escrowCheckoutLoader[scriptUrl] = new Promise<EscrowWidget>((resolve, reject) => {\n const script = document.createElement('script');\n script.src = scriptUrl;\n script.async = true;\n if (crossOrigin) script.crossOrigin = crossOrigin;\n\n script.onload = () => {\n const fn = win[globalName];\n if (typeof fn === 'function') resolve(fn as EscrowWidget);\n else {\n reject(\n new EscrowCheckoutError(\n `Escrow checkout script loaded, but window.${globalName} is not a function.`,\n 'WIDGET_LOAD'\n )\n );\n }\n };\n\n script.onerror = () =>\n reject(new EscrowCheckoutError(`Failed to load escrow checkout script: ${scriptUrl}`, 'WIDGET_LOAD'));\n\n document.head.appendChild(script);\n });\n\n return win.__escrowCheckoutLoader[scriptUrl];\n}\n\n/**\n * Internal: create a checkout session by calling your API.\n * This is intentionally inside the lib (user doesn't implement it).\n */\nasync function createSession(input: PayInput): Promise<SessionResponse> {\n assertConfigured(CONFIG);\n const { publicKey } = CONFIG;\n const apiBaseUrl = publicKey.startsWith('pk_live_') ? 'https://live.payluk.ng' : 'https://staging.live.payluk.ng';\n let resp: Response;\n\n try {\n resp = await fetch(`${apiBaseUrl}/v1/checkout/session`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentToken: input.paymentToken,\n redirectUrl: input.redirectUrl,\n reference: input.reference,\n customerId: input.customerId,\n publicKey\n })\n });\n } catch (error) {\n const normalized = normalizeError(error);\n throw new EscrowCheckoutError('Network error while creating checkout session.', 'NETWORK', {\n details: normalized.message\n });\n }\n\n if (!resp.ok) {\n const { message, details } = await parseErrorBody(resp);\n throw new EscrowCheckoutError(message || `Failed to create session (HTTP ${resp.status}).`, 'SESSION_CREATE', {\n status: resp.status,\n details\n });\n }\n\n const data = await parseJsonResponse(resp);\n if (!isSessionResponse(data)) {\n throw new EscrowCheckoutError('Session response missing \"session\".', 'SESSION_RESPONSE', {\n status: resp.status,\n details: data\n });\n }\n\n return data as SessionResponse;\n}\n\n/**\n * Public API: create the session and open the widget.\n * Consumers only supply business inputs (no script URL, no API calls).\n */\nexport async function pay(input: PayInput): Promise<void> {\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('pay(...) can only run in the browser.', 'BROWSER_ONLY');\n }\n\n assertConfigured(CONFIG);\n if (!input || !isValidPaymentToken(input.paymentToken)) {\n throw new EscrowCheckoutError(\n 'pay(...) requires \"paymentToken\" (must be a non-empty string or array of non-empty strings).',\n 'INVALID_INPUT'\n );\n }\n if (!isNonEmptyString(input.reference)) {\n throw new EscrowCheckoutError('pay(...) requires \"reference\".', 'INVALID_INPUT');\n }\n if (!isNonEmptyString(input.redirectUrl)) {\n throw new EscrowCheckoutError('pay(...) requires \"redirectUrl\".', 'INVALID_INPUT');\n }\n\n const [{ session }, widget] = await Promise.all([createSession(input), loadWidget()]);\n\n widget({\n session,\n logoUrl: input.logoUrl,\n brand: input.brand,\n callback: input.callback,\n onClose: input.onClose,\n customerId: input.customerId,\n publicKey: CONFIG.publicKey,\n ...(input.extra ?? {})\n });\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\r\nimport { pay as corePay } from '../index';\r\n\r\nexport interface UseEscrowCheckoutResult {\r\n ready: boolean;\r\n loading: boolean;\r\n error: Error | null;\r\n pay: typeof corePay;\r\n}\r\n\r\n/**\r\n * React hook that preloads the widget script (implicitly via pay) and exposes pay(...)\r\n * Since the library abstracts script loading and session creation, the hook is thin.\r\n */\r\nexport function useEscrowCheckout(): UseEscrowCheckoutResult {\n const [ready, setReady] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const normalizeError = useCallback((err: unknown): Error => {\n if (err instanceof Error) return err;\n if (typeof err === 'string') return new Error(err);\n return new Error('Unknown error');\n }, []);\n\n // Lazy mark \"ready\" after first successful pay or script load attempt.\n // If you want to preload the script earlier, you may trigger a no-op pay with a dry-run endpoint on your side.\n\n // Provide a stable pay function that reports loading state.\n const pay = useCallback<typeof corePay>(async (input) => {\n setLoading(true);\r\n try {\r\n await corePay(input);\n setReady(true);\n setError(null);\n } catch (e) {\n const normalized = normalizeError(e);\n setError(normalized);\n throw normalized;\n } finally {\n setLoading(false);\n }\n }, [normalizeError]);\n\r\n // In SSR, mark not loading\n useEffect(() => {\n if (typeof window === 'undefined') {\n setLoading(false);\n }\n }, []);\n\r\n return useMemo(() => ({ ready, loading, error, pay }), [ready, loading, error, pay]);\r\n}\n","import React, { useCallback } from 'react';\r\nimport { useEscrowCheckout } from './useEscrowCheckout';\r\n\r\nexport interface EscrowCheckoutButtonProps {\r\n /**\r\n * Single escrow payment token or array of payment tokens for multi-escrow checkout.\r\n */\r\n paymentToken: string | string[];\r\n reference: string;\r\n redirectUrl: string;\r\n children?: React.ReactNode;\r\n disabled?: boolean;\r\n className?: string;\r\n title?: string;\r\n\r\n brand?: string;\r\n customerId?: string;\r\n logoUrl?: string;\r\n callback?: (result: any) => void;\r\n onClose?: () => void;\r\n extra?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Button that triggers pay(...) with your provided inputs.\r\n * The library handles both session creation and widget loading.\r\n */\r\nexport function EscrowCheckoutButton({\r\n paymentToken,\r\n reference,\r\n redirectUrl,\r\n brand,\r\n logoUrl,\r\n callback,\r\n onClose,\r\n extra,\r\n children = 'Pay',\r\n disabled,\r\n className,\r\n title,\r\n customerId\r\n }: EscrowCheckoutButtonProps) {\r\n const { loading, error, pay } = useEscrowCheckout();\r\n\r\n const handleClick = useCallback(async () => {\r\n try {\r\n await pay({\r\n paymentToken,\r\n reference,\r\n redirectUrl,\r\n brand,\r\n logoUrl,\r\n callback,\r\n onClose,\r\n extra,\r\n customerId\r\n });\r\n } catch {\r\n // Error state is already exposed via the hook.\r\n }\r\n }, [pay, paymentToken, reference, redirectUrl, brand, logoUrl, callback, onClose, extra, customerId]);\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n onClick={handleClick}\r\n disabled={disabled || loading}\r\n className={className}\r\n aria-disabled={disabled || loading}\r\n title={title ?? (error ? error.message : undefined)}\r\n >\r\n {children}\r\n </button>\r\n );\r\n}\r\n"]}
|
package/dist/react/index.d.cts
CHANGED
|
@@ -15,7 +15,10 @@ interface UseEscrowCheckoutResult {
|
|
|
15
15
|
declare function useEscrowCheckout(): UseEscrowCheckoutResult;
|
|
16
16
|
|
|
17
17
|
interface EscrowCheckoutButtonProps {
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Single escrow payment token or array of payment tokens for multi-escrow checkout.
|
|
20
|
+
*/
|
|
21
|
+
paymentToken: string | string[];
|
|
19
22
|
reference: string;
|
|
20
23
|
redirectUrl: string;
|
|
21
24
|
children?: React.ReactNode;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -15,7 +15,10 @@ interface UseEscrowCheckoutResult {
|
|
|
15
15
|
declare function useEscrowCheckout(): UseEscrowCheckoutResult;
|
|
16
16
|
|
|
17
17
|
interface EscrowCheckoutButtonProps {
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Single escrow payment token or array of payment tokens for multi-escrow checkout.
|
|
20
|
+
*/
|
|
21
|
+
paymentToken: string | string[];
|
|
19
22
|
reference: string;
|
|
20
23
|
redirectUrl: string;
|
|
21
24
|
children?: React.ReactNode;
|
package/dist/react/index.js
CHANGED
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/useEscrowCheckout.ts","../../src/react/EscrowCheckoutButton.tsx"],"names":["pay","useCallback"],"mappings":";;;;AAcO,SAAS,iBAAA,GAA6C;AACzD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,KAAK,CAAA;AACxC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,cAAA,GAAiB,WAAA,CAAY,CAAC,GAAA,KAAwB;AACxD,IAAA,IAAI,GAAA,YAAe,OAAO,OAAO,GAAA;AACjC,IAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,EAAU,OAAO,IAAI,MAAM,GAAG,CAAA;AACjD,IAAA,OAAO,IAAI,MAAM,eAAe,CAAA;AAAA,EACpC,CAAA,EAAG,EAAE,CAAA;AAML,EAAA,MAAMA,IAAAA,GAAM,WAAA,CAA4B,OAAO,KAAA,KAAU;AACrD,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,IAAI;AACA,MAAA,MAAM,IAAQ,KAAK,CAAA;AACnB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,QAAA,CAAS,IAAI,CAAA;AAAA,IACjB,SAAS,CAAA,EAAG;AACR,MAAA,MAAM,UAAA,GAAa,eAAe,CAAC,CAAA;AACnC,MAAA,QAAA,CAAS,UAAU,CAAA;AACnB,MAAA,MAAM,UAAA;AAAA,IACV,CAAA,SAAE;AACE,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IACpB;AAAA,EACJ,CAAA,EAAG,CAAC,cAAc,CAAC,CAAA;AAGnB,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IACpB;AAAA,EACJ,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,OAAA,CAAQ,OAAO,EAAE,KAAA,EAAO,SAAS,KAAA,EAAO,GAAA,EAAAA,IAAAA,EAAI,CAAA,EAAI,CAAC,KAAA,EAAO,OAAA,EAAS,KAAA,EAAOA,IAAG,CAAC,CAAA;AACvF;
|
|
1
|
+
{"version":3,"sources":["../../src/react/useEscrowCheckout.ts","../../src/react/EscrowCheckoutButton.tsx"],"names":["pay","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react';\r\nimport { pay as corePay } from '../index';\r\n\r\nexport interface UseEscrowCheckoutResult {\r\n ready: boolean;\r\n loading: boolean;\r\n error: Error | null;\r\n pay: typeof corePay;\r\n}\r\n\r\n/**\r\n * React hook that preloads the widget script (implicitly via pay) and exposes pay(...)\r\n * Since the library abstracts script loading and session creation, the hook is thin.\r\n */\r\nexport function useEscrowCheckout(): UseEscrowCheckoutResult {\n const [ready, setReady] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const normalizeError = useCallback((err: unknown): Error => {\n if (err instanceof Error) return err;\n if (typeof err === 'string') return new Error(err);\n return new Error('Unknown error');\n }, []);\n\n // Lazy mark \"ready\" after first successful pay or script load attempt.\n // If you want to preload the script earlier, you may trigger a no-op pay with a dry-run endpoint on your side.\n\n // Provide a stable pay function that reports loading state.\n const pay = useCallback<typeof corePay>(async (input) => {\n setLoading(true);\r\n try {\r\n await corePay(input);\n setReady(true);\n setError(null);\n } catch (e) {\n const normalized = normalizeError(e);\n setError(normalized);\n throw normalized;\n } finally {\n setLoading(false);\n }\n }, [normalizeError]);\n\r\n // In SSR, mark not loading\n useEffect(() => {\n if (typeof window === 'undefined') {\n setLoading(false);\n }\n }, []);\n\r\n return useMemo(() => ({ ready, loading, error, pay }), [ready, loading, error, pay]);\r\n}\n","import React, { useCallback } from 'react';\r\nimport { useEscrowCheckout } from './useEscrowCheckout';\r\n\r\nexport interface EscrowCheckoutButtonProps {\r\n /**\r\n * Single escrow payment token or array of payment tokens for multi-escrow checkout.\r\n */\r\n paymentToken: string | string[];\r\n reference: string;\r\n redirectUrl: string;\r\n children?: React.ReactNode;\r\n disabled?: boolean;\r\n className?: string;\r\n title?: string;\r\n\r\n brand?: string;\r\n customerId?: string;\r\n logoUrl?: string;\r\n callback?: (result: any) => void;\r\n onClose?: () => void;\r\n extra?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Button that triggers pay(...) with your provided inputs.\r\n * The library handles both session creation and widget loading.\r\n */\r\nexport function EscrowCheckoutButton({\r\n paymentToken,\r\n reference,\r\n redirectUrl,\r\n brand,\r\n logoUrl,\r\n callback,\r\n onClose,\r\n extra,\r\n children = 'Pay',\r\n disabled,\r\n className,\r\n title,\r\n customerId\r\n }: EscrowCheckoutButtonProps) {\r\n const { loading, error, pay } = useEscrowCheckout();\r\n\r\n const handleClick = useCallback(async () => {\r\n try {\r\n await pay({\r\n paymentToken,\r\n reference,\r\n redirectUrl,\r\n brand,\r\n logoUrl,\r\n callback,\r\n onClose,\r\n extra,\r\n customerId\r\n });\r\n } catch {\r\n // Error state is already exposed via the hook.\r\n }\r\n }, [pay, paymentToken, reference, redirectUrl, brand, logoUrl, callback, onClose, extra, customerId]);\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n onClick={handleClick}\r\n disabled={disabled || loading}\r\n className={className}\r\n aria-disabled={disabled || loading}\r\n title={title ?? (error ? error.message : undefined)}\r\n >\r\n {children}\r\n </button>\r\n );\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"chunk-TL6HBLNO.js","sourcesContent":["export type EscrowWidget = (options: Record<string, unknown>) => void;\n\nexport type EscrowCheckoutErrorCode =\n | 'NOT_INITIALIZED'\n | 'BROWSER_ONLY'\n | 'INVALID_INPUT'\n | 'WIDGET_LOAD'\n | 'NETWORK'\n | 'SESSION_CREATE'\n | 'SESSION_RESPONSE';\n\nexport class EscrowCheckoutError extends Error {\n code: EscrowCheckoutErrorCode;\n status?: number;\n details?: unknown;\n\n constructor(message: string, code: EscrowCheckoutErrorCode, options?: { status?: number; details?: unknown }) {\n super(message);\n this.name = 'EscrowCheckoutError';\n this.code = code;\n this.status = options?.status;\n this.details = options?.details;\n }\n}\n\nexport interface InitConfig {\n publicKey: string; // publishable key only\n /**\n * Optional overrides for experts. End users don't need to set these.\n */\n scriptUrlOverride?: string;\r\n globalName?: string; // default 'EscrowCheckout'\r\n crossOrigin?: '' | 'anonymous' | 'use-credentials';\r\n}\r\n\r\nexport interface PayInput {\r\n paymentToken: string;\r\n reference: string;\r\n redirectUrl: string;\r\n logoUrl?: string;\r\n brand?: string;\r\n customerId?: string;\r\n /**\r\n * Extra fields supported by the widget.\r\n */\r\n extra?: Record<string, unknown>;\r\n callback?: (result: any) => void;\r\n onClose?: () => void;\r\n}\r\n\r\nexport interface SessionResponse {\r\n session: unknown;\r\n}\r\n\r\nconst DEFAULT_SCRIPT_URL = 'https://checkout.payluk.ng/escrow-checkout.min.js';\r\nconst DEFAULT_GLOBAL_NAME = 'EscrowCheckout';\r\n\r\ntype ResolvedConfig = Required<Pick<InitConfig, 'publicKey'>> & {\r\n scriptUrl: string;\r\n globalName: string;\r\n crossOrigin: '' | 'anonymous' | 'use-credentials';\r\n};\r\n\r\nlet CONFIG: ResolvedConfig | null = null;\r\n\r\n/**\r\n * Initialize the library once (e.g., in app bootstrap).\r\n * Hides script URL and session creation from consumers.\r\n */\r\nexport function initEscrowCheckout(config: InitConfig): void {\n if (!isNonEmptyString(config?.publicKey)) {\n throw new EscrowCheckoutError('initEscrowCheckout(...) requires \"publicKey\".', 'INVALID_INPUT');\n }\n CONFIG = {\n publicKey: config.publicKey.trim(),\n scriptUrl: config.scriptUrlOverride || DEFAULT_SCRIPT_URL,\n globalName: config.globalName || DEFAULT_GLOBAL_NAME,\n crossOrigin: config.crossOrigin ?? 'anonymous'\n };\n}\n\r\n/**\r\n * Narrow a provided config to ResolvedConfig or throw if not initialized.\r\n */\r\nfunction assertConfigured(config: ResolvedConfig | null): asserts config is ResolvedConfig {\n if (!config) {\n throw new EscrowCheckoutError(\n 'Escrow checkout not initialized. Call initEscrowCheckout({ publicKey }) first.',\n 'NOT_INITIALIZED'\n );\n }\n}\n\nfunction normalizeError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === 'string') return new Error(error);\n return new Error('Unknown error');\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isSessionResponse(value: unknown): value is SessionResponse {\n if (!value || typeof value !== 'object') return false;\n return 'session' in value;\n}\n\nasync function parseErrorBody(resp: Response): Promise<{ message?: string; details?: unknown }> {\n const text = await resp.text();\n if (!text) return {};\n\n try {\n const json = JSON.parse(text) as { message?: unknown };\n const message = typeof json?.message === 'string' ? json.message : undefined;\n return { message, details: json };\n } catch {\n return { message: text, details: text };\n }\n}\n\nasync function parseJsonResponse(resp: Response): Promise<unknown> {\n try {\n return await resp.json();\n } catch {\n throw new EscrowCheckoutError('Invalid JSON response from session endpoint.', 'SESSION_RESPONSE', {\n status: resp.status\n });\n }\n}\n\n/**\n * Internal: load the widget script once and return the global function.\n */\nasync function loadWidget(): Promise<EscrowWidget> {\n assertConfigured(CONFIG);\n const { scriptUrl, globalName, crossOrigin } = CONFIG;\r\n\r\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('EscrowCheckout can only be loaded in the browser.', 'BROWSER_ONLY');\n }\n\r\n const win = window as any;\r\n win.__escrowCheckoutLoader ??= {};\r\n\r\n if (win.__escrowCheckoutLoader[scriptUrl]) {\r\n return win.__escrowCheckoutLoader[scriptUrl];\r\n }\r\n\r\n if (typeof win[globalName] === 'function') {\r\n const fn = win[globalName] as EscrowWidget;\r\n win.__escrowCheckoutLoader[scriptUrl] = Promise.resolve(fn);\r\n return fn;\r\n }\r\n\r\n win.__escrowCheckoutLoader[scriptUrl] = new Promise<EscrowWidget>((resolve, reject) => {\r\n const script = document.createElement('script');\r\n script.src = scriptUrl;\r\n script.async = true;\r\n if (crossOrigin) script.crossOrigin = crossOrigin;\r\n\r\n script.onload = () => {\r\n const fn = win[globalName];\r\n if (typeof fn === 'function') resolve(fn as EscrowWidget);\n else {\n reject(\n new EscrowCheckoutError(\n `Escrow checkout script loaded, but window.${globalName} is not a function.`,\n 'WIDGET_LOAD'\n )\n );\n }\n };\n\n script.onerror = () =>\n reject(new EscrowCheckoutError(`Failed to load escrow checkout script: ${scriptUrl}`, 'WIDGET_LOAD'));\n\n document.head.appendChild(script);\n });\n\r\n return win.__escrowCheckoutLoader[scriptUrl];\r\n}\r\n\r\n/**\r\n * Internal: create a checkout session by calling your API.\r\n * This is intentionally inside the lib (user doesn't implement it).\r\n */\r\nasync function createSession(input: PayInput): Promise<SessionResponse> {\n assertConfigured(CONFIG);\n const { publicKey } = CONFIG;\n const apiBaseUrl = publicKey.startsWith('pk_live_') ? 'https://live.payluk.ng' : 'https://staging.live.payluk.ng';\n let resp: Response;\n\n try {\n resp = await fetch(`${apiBaseUrl}/v1/checkout/session`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentToken: input.paymentToken,\n redirectUrl: input.redirectUrl,\n reference: input.reference,\n customerId: input.customerId,\n publicKey\n })\n });\n } catch (error) {\n const normalized = normalizeError(error);\n throw new EscrowCheckoutError('Network error while creating checkout session.', 'NETWORK', {\n details: normalized.message\n });\n }\n\n if (!resp.ok) {\n const { message, details } = await parseErrorBody(resp);\n throw new EscrowCheckoutError(message || `Failed to create session (HTTP ${resp.status}).`, 'SESSION_CREATE', {\n status: resp.status,\n details\n });\n }\n\n const data = await parseJsonResponse(resp);\n if (!isSessionResponse(data)) {\n throw new EscrowCheckoutError('Session response missing \"session\".', 'SESSION_RESPONSE', {\n status: resp.status,\n details: data\n });\n }\n\n return data as SessionResponse;\n}\n\r\n/**\r\n * Public API: create the session and open the widget.\r\n * Consumers only supply business inputs (no script URL, no API calls).\r\n */\r\nexport async function pay(input: PayInput): Promise<void> {\n if (typeof window === 'undefined') {\n throw new EscrowCheckoutError('pay(...) can only run in the browser.', 'BROWSER_ONLY');\n }\n\n assertConfigured(CONFIG);\n if (!input || !isNonEmptyString(input.paymentToken)) {\n throw new EscrowCheckoutError('pay(...) requires \"paymentToken\".', 'INVALID_INPUT');\n }\n if (!isNonEmptyString(input.reference)) {\n throw new EscrowCheckoutError('pay(...) requires \"reference\".', 'INVALID_INPUT');\n }\n if (!isNonEmptyString(input.redirectUrl)) {\n throw new EscrowCheckoutError('pay(...) requires \"redirectUrl\".', 'INVALID_INPUT');\n }\n\n const [{ session }, widget] = await Promise.all([createSession(input), loadWidget()]);\n\n widget({\n session,\n logoUrl: input.logoUrl,\n brand: input.brand,\n callback: input.callback,\n onClose: input.onClose,\n customerId: input.customerId,\n publicKey: CONFIG.publicKey,\n ...(input.extra ?? {})\n });\n}\n"]}
|