react-paysky-lightbox 0.1.2 → 0.1.3
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 +45 -0
- package/dist/index.cjs +40 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,11 +68,56 @@ function CheckoutButton({ session }) {
|
|
|
68
68
|
}
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
## Tokenization (saved cards)
|
|
72
|
+
|
|
73
|
+
This package only **passes through** PaySky tokenization fields. Your backend must:
|
|
74
|
+
|
|
75
|
+
1. Store `TokenCustomerId` for the customer after a successful payment.
|
|
76
|
+
2. Before opening lightbox for a returning customer, call PaySky **CreatePaymentSession** and get `TokenCustomerSession`.
|
|
77
|
+
3. Pass both into `additionalCustomerData` (WooCommerce uses `paymentMethodFromLightBox: 2`).
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import {
|
|
81
|
+
PaySkyCheckout,
|
|
82
|
+
extractTokenCustomerId,
|
|
83
|
+
} from 'react-paysky-lightbox';
|
|
84
|
+
|
|
85
|
+
<PaySkyCheckout
|
|
86
|
+
environment="sandbox"
|
|
87
|
+
paymentMethodFromLightBox={2}
|
|
88
|
+
session={sessionFromBackend}
|
|
89
|
+
additionalCustomerData={{
|
|
90
|
+
TokenCustomerId: savedTokenCustomerId,
|
|
91
|
+
TokenCustomerSession: sessionTokenFromCreatePaymentSession,
|
|
92
|
+
CustomerEmail: 'user@example.com',
|
|
93
|
+
CustomerMobile: '01xxxxxxxxx',
|
|
94
|
+
MerchantCustomerId: '42',
|
|
95
|
+
ShowCustomerEmail: true,
|
|
96
|
+
ShowCustomerMobile: true,
|
|
97
|
+
ShowMerchantCustomerId: true,
|
|
98
|
+
ShowMessageCustomer: false,
|
|
99
|
+
ShowCurrencyTwo: false,
|
|
100
|
+
ShowAdditionalFees: false,
|
|
101
|
+
ConsumerEnterAmount: false,
|
|
102
|
+
}}
|
|
103
|
+
onSuccess={(data) => {
|
|
104
|
+
const tokenId = extractTokenCustomerId(data);
|
|
105
|
+
// POST tokenId + data to your backend to persist
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
CreatePaymentSession API (typical):
|
|
111
|
+
|
|
112
|
+
- Test: `https://red3.paysky.io:3001/Cube/PayLink.svc/api/CreatePaymentSessionService`
|
|
113
|
+
- Live: `https://cube.paysky.io/Cube/PayLink.svc/api/CreatePaymentSessionService`
|
|
114
|
+
|
|
71
115
|
## Backend responsibilities
|
|
72
116
|
|
|
73
117
|
1. Generate `SecureHash` with your merchant secret (never ship the secret to the browser).
|
|
74
118
|
2. Return session fields: `mid`, `tid`, `amountTrxn`, optional `merchantReference`, `trxDateTime`, `secureHash`.
|
|
75
119
|
3. On success (browser `notifyUrl` and/or PaySky server notification), **re-verify** response `SecureHash` before marking the order paid.
|
|
120
|
+
4. For tokenization: run CreatePaymentSession server-side; persist `TokenCustomerId` from the complete callback (`extractTokenCustomerId`).
|
|
76
121
|
|
|
77
122
|
Amount is in the **smallest currency unit** (1 EGP = `100`).
|
|
78
123
|
|
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,7 @@ __export(index_exports, {
|
|
|
23
23
|
PaySkyCheckout: () => PaySkyCheckout,
|
|
24
24
|
SCRIPT_URLS: () => SCRIPT_URLS,
|
|
25
25
|
closeLightbox: () => closeLightbox,
|
|
26
|
+
extractTokenCustomerId: () => extractTokenCustomerId,
|
|
26
27
|
openLightbox: () => openLightbox,
|
|
27
28
|
resolveScriptUrl: () => resolveScriptUrl,
|
|
28
29
|
usePaySky: () => usePaySky
|
|
@@ -108,6 +109,8 @@ async function openLightbox(options) {
|
|
|
108
109
|
session,
|
|
109
110
|
environment = "sandbox",
|
|
110
111
|
scriptUrl,
|
|
112
|
+
additionalCustomerData,
|
|
113
|
+
paymentMethodFromLightBox,
|
|
111
114
|
notifyUrl,
|
|
112
115
|
notifyHeaders,
|
|
113
116
|
onSuccess,
|
|
@@ -138,6 +141,8 @@ async function openLightbox(options) {
|
|
|
138
141
|
...session.merchantReference != null ? { MerchantReference: session.merchantReference } : {},
|
|
139
142
|
...session.trxDateTime != null ? { TrxDateTime: session.trxDateTime } : {},
|
|
140
143
|
...session.secureHash != null ? { SecureHash: session.secureHash } : {},
|
|
144
|
+
...paymentMethodFromLightBox != null ? { paymentMethodFromLightBox } : {},
|
|
145
|
+
...additionalCustomerData != null ? { AdditionalCustomerData: additionalCustomerData } : {},
|
|
141
146
|
completeCallback: (data) => {
|
|
142
147
|
openInProgress = false;
|
|
143
148
|
onSuccess?.(data);
|
|
@@ -160,6 +165,40 @@ function closeLightbox() {
|
|
|
160
165
|
window.Lightbox?.Checkout?.closeLightbox();
|
|
161
166
|
}
|
|
162
167
|
|
|
168
|
+
// src/tokenization.ts
|
|
169
|
+
function extractTokenCustomerId(payload) {
|
|
170
|
+
const keys = [
|
|
171
|
+
"TokenCustomerId",
|
|
172
|
+
"tokenCustomerId",
|
|
173
|
+
"CustomerId",
|
|
174
|
+
"customerId",
|
|
175
|
+
"customer_id"
|
|
176
|
+
];
|
|
177
|
+
const visit = (value) => {
|
|
178
|
+
if (value == null) return null;
|
|
179
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
180
|
+
const trimmed = String(value).trim();
|
|
181
|
+
return trimmed ? trimmed : null;
|
|
182
|
+
}
|
|
183
|
+
if (typeof value !== "object") return null;
|
|
184
|
+
const record = value;
|
|
185
|
+
for (const key of keys) {
|
|
186
|
+
if (key in record) {
|
|
187
|
+
const found = visit(record[key]);
|
|
188
|
+
if (found) return found;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
for (const nested of Object.values(record)) {
|
|
192
|
+
if (nested && typeof nested === "object") {
|
|
193
|
+
const found = visit(nested);
|
|
194
|
+
if (found) return found;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
};
|
|
199
|
+
return visit(payload);
|
|
200
|
+
}
|
|
201
|
+
|
|
163
202
|
// src/usePaySky.ts
|
|
164
203
|
var import_react = require("react");
|
|
165
204
|
function usePaySky() {
|
|
@@ -237,6 +276,7 @@ function PaySkyCheckout({
|
|
|
237
276
|
PaySkyCheckout,
|
|
238
277
|
SCRIPT_URLS,
|
|
239
278
|
closeLightbox,
|
|
279
|
+
extractTokenCustomerId,
|
|
240
280
|
openLightbox,
|
|
241
281
|
resolveScriptUrl,
|
|
242
282
|
usePaySky
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/loadScript.ts","../src/notify.ts","../src/checkout.ts","../src/usePaySky.ts","../src/PaySkyCheckout.tsx"],"sourcesContent":["export { SCRIPT_URLS, resolveScriptUrl } from './constants';\r\nexport { openLightbox, closeLightbox } from './checkout';\r\nexport { usePaySky } from './usePaySky';\r\nexport type { UsePaySkyResult } from './usePaySky';\r\nexport { PaySkyCheckout } from './PaySkyCheckout';\r\nexport type {\r\n PaySkyEnvironment,\r\n PaySkySession,\r\n PaySkyCompleteData,\r\n PaySkyErrorData,\r\n PaySkyNotifyEvent,\r\n PaySkyNotifyPayload,\r\n PaySkyOpenOptions,\r\n PaySkyCheckoutProps,\r\n} from './types';\r\n","import type { PaySkyEnvironment } from './types';\r\n\r\nexport const SCRIPT_URLS: Record<PaySkyEnvironment, string> = {\r\n sandbox: 'https://red3.paysky.io:3011/LB/js/LightBox.js',\r\n production: 'https://cube.paysky.io:6006/js/LightBox.js',\r\n};\r\n\r\nexport function resolveScriptUrl(\r\n environment: PaySkyEnvironment = 'sandbox',\r\n scriptUrl?: string,\r\n): string {\r\n return scriptUrl ?? SCRIPT_URLS[environment];\r\n}\r\n","const loaders = new Map<string, Promise<void>>();\r\n\r\nexport function loadPaySkyScript(url: string): Promise<void> {\r\n const existing = loaders.get(url);\r\n if (existing) {\r\n return existing;\r\n }\r\n\r\n const promise = new Promise<void>((resolve, reject) => {\r\n const script = document.createElement('script');\r\n script.src = url;\r\n script.async = true;\r\n\r\n script.addEventListener('load', () => resolve(), { once: true });\r\n script.addEventListener(\r\n 'error',\r\n () => {\r\n loaders.delete(url);\r\n reject(new Error(`Failed to load PaySky Lightbox script: ${url}`));\r\n },\r\n { once: true },\r\n );\r\n\r\n document.head.appendChild(script);\r\n });\r\n\r\n loaders.set(url, promise);\r\n return promise;\r\n}\r\n\r\n/** Test helper — clears cached loaders between tests. */\r\nexport function __resetPaySkyScriptLoaders(): void {\r\n loaders.clear();\r\n}\r\n","import type { PaySkyNotifyPayload } from './types';\r\n\r\nexport async function postNotify(\r\n notifyUrl: string,\r\n payload: PaySkyNotifyPayload,\r\n headers?: Record<string, string>,\r\n): Promise<void> {\r\n const response = await fetch(notifyUrl, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...headers,\r\n },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(\r\n `PaySky notify failed: ${response.status} ${response.statusText}`,\r\n );\r\n }\r\n}\r\n","import { resolveScriptUrl } from './constants';\r\nimport { loadPaySkyScript } from './loadScript';\r\nimport { postNotify } from './notify';\r\nimport type {\r\n PaySkyCompleteData,\r\n PaySkyErrorData,\r\n PaySkyOpenOptions,\r\n PaySkySession,\r\n} from './types';\r\n\r\nlet openInProgress = false;\r\n\r\nfunction assertSession(session: PaySkySession): void {\r\n if (!session.mid?.trim()) {\r\n throw new Error('PaySky session.mid is required');\r\n }\r\n if (!session.tid?.trim()) {\r\n throw new Error('PaySky session.tid is required');\r\n }\r\n if (\r\n typeof session.amountTrxn !== 'number' ||\r\n !Number.isFinite(session.amountTrxn) ||\r\n session.amountTrxn <= 0\r\n ) {\r\n throw new Error('PaySky session.amountTrxn must be a positive number');\r\n }\r\n}\r\n\r\nfunction getCheckout() {\r\n const checkout = window.Lightbox?.Checkout;\r\n if (!checkout) {\r\n throw new Error(\r\n 'PaySky Lightbox is not available on window.Lightbox after script load',\r\n );\r\n }\r\n return checkout;\r\n}\r\n\r\nexport async function openLightbox(options: PaySkyOpenOptions): Promise<void> {\r\n const {\r\n session,\r\n environment = 'sandbox',\r\n scriptUrl,\r\n notifyUrl,\r\n notifyHeaders,\r\n onSuccess,\r\n onError,\r\n onCancel,\r\n onNotifyError,\r\n } = options;\r\n\r\n assertSession(session);\r\n\r\n if (openInProgress) {\r\n throw new Error('PaySky Lightbox is already open');\r\n }\r\n\r\n const url = resolveScriptUrl(environment, scriptUrl);\r\n await loadPaySkyScript(url);\r\n\r\n const checkout = getCheckout();\r\n\r\n openInProgress = true;\r\n\r\n const safeNotify = (\r\n event: 'complete' | 'error',\r\n data: PaySkyCompleteData | PaySkyErrorData,\r\n ) => {\r\n if (!notifyUrl) return;\r\n void postNotify(notifyUrl, { event, data }, notifyHeaders).catch(\r\n (error) => {\r\n onNotifyError?.(error);\r\n },\r\n );\r\n };\r\n\r\n checkout.configure = {\r\n MID: session.mid,\r\n TID: session.tid,\r\n AmountTrxn: session.amountTrxn,\r\n ...(session.merchantReference != null\r\n ? { MerchantReference: session.merchantReference }\r\n : {}),\r\n ...(session.trxDateTime != null ? { TrxDateTime: session.trxDateTime } : {}),\r\n ...(session.secureHash != null ? { SecureHash: session.secureHash } : {}),\r\n completeCallback: (data: PaySkyCompleteData) => {\r\n openInProgress = false;\r\n onSuccess?.(data);\r\n safeNotify('complete', data);\r\n },\r\n errorCallback: (error: PaySkyErrorData) => {\r\n openInProgress = false;\r\n onError?.(error);\r\n safeNotify('error', error);\r\n },\r\n cancelCallback: () => {\r\n openInProgress = false;\r\n onCancel?.();\r\n },\r\n };\r\n\r\n checkout.showLightbox();\r\n}\r\n\r\nexport function closeLightbox(): void {\r\n openInProgress = false;\r\n window.Lightbox?.Checkout?.closeLightbox();\r\n}\r\n\r\n/** Test helper */\r\nexport function __resetCheckoutOpenState(): void {\r\n openInProgress = false;\r\n}\r\n","import { useCallback, useState } from 'react';\r\nimport { closeLightbox, openLightbox } from './checkout';\r\nimport type { PaySkyOpenOptions } from './types';\r\n\r\nexport interface UsePaySkyResult {\r\n openPayment: (options: PaySkyOpenOptions) => Promise<void>;\r\n closePayment: () => void;\r\n isOpen: boolean;\r\n isLoading: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport function usePaySky(): UsePaySkyResult {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const closePayment = useCallback(() => {\r\n closeLightbox();\r\n setIsOpen(false);\r\n }, []);\r\n\r\n const openPayment = useCallback(async (options: PaySkyOpenOptions) => {\r\n setIsLoading(true);\r\n setError(null);\r\n\r\n try {\r\n await openLightbox({\r\n ...options,\r\n onSuccess: (data) => {\r\n setIsOpen(false);\r\n options.onSuccess?.(data);\r\n },\r\n onError: (err) => {\r\n setIsOpen(false);\r\n options.onError?.(err);\r\n },\r\n onCancel: () => {\r\n setIsOpen(false);\r\n options.onCancel?.();\r\n },\r\n });\r\n setIsOpen(true);\r\n } catch (err) {\r\n const next =\r\n err instanceof Error ? err : new Error('Failed to open PaySky Lightbox');\r\n setError(next);\r\n setIsOpen(false);\r\n throw next;\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }, []);\r\n\r\n return {\r\n openPayment,\r\n closePayment,\r\n isOpen,\r\n isLoading,\r\n error,\r\n };\r\n}\r\n","import type { PaySkyCheckoutProps } from './types';\r\nimport { usePaySky } from './usePaySky';\r\n\r\nexport function PaySkyCheckout({\r\n children = 'Pay with PaySky',\r\n className,\r\n disabled,\r\n ...options\r\n}: PaySkyCheckoutProps) {\r\n const { openPayment, isLoading, error } = usePaySky();\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n className={className}\r\n disabled={disabled || isLoading}\r\n aria-busy={isLoading || undefined}\r\n data-error={error ? error.message : undefined}\r\n onClick={() => {\r\n void openPayment(options);\r\n }}\r\n >\r\n {children}\r\n </button>\r\n );\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/loadScript.ts","../src/notify.ts","../src/checkout.ts","../src/tokenization.ts","../src/usePaySky.ts","../src/PaySkyCheckout.tsx"],"sourcesContent":["export { SCRIPT_URLS, resolveScriptUrl } from './constants';\r\nexport { openLightbox, closeLightbox } from './checkout';\r\nexport { extractTokenCustomerId } from './tokenization';\r\nexport { usePaySky } from './usePaySky';\r\nexport type { UsePaySkyResult } from './usePaySky';\r\nexport { PaySkyCheckout } from './PaySkyCheckout';\r\nexport type {\r\n PaySkyEnvironment,\r\n PaySkySession,\r\n PaySkyCompleteData,\r\n PaySkyErrorData,\r\n PaySkyAdditionalCustomerData,\r\n PaySkyNotifyEvent,\r\n PaySkyNotifyPayload,\r\n PaySkyOpenOptions,\r\n PaySkyCheckoutProps,\r\n} from './types';\r\n","import type { PaySkyEnvironment } from './types';\r\n\r\nexport const SCRIPT_URLS: Record<PaySkyEnvironment, string> = {\r\n sandbox: 'https://red3.paysky.io:3011/LB/js/LightBox.js',\r\n production: 'https://cube.paysky.io:6006/js/LightBox.js',\r\n};\r\n\r\nexport function resolveScriptUrl(\r\n environment: PaySkyEnvironment = 'sandbox',\r\n scriptUrl?: string,\r\n): string {\r\n return scriptUrl ?? SCRIPT_URLS[environment];\r\n}\r\n","const loaders = new Map<string, Promise<void>>();\r\n\r\nexport function loadPaySkyScript(url: string): Promise<void> {\r\n const existing = loaders.get(url);\r\n if (existing) {\r\n return existing;\r\n }\r\n\r\n const promise = new Promise<void>((resolve, reject) => {\r\n const script = document.createElement('script');\r\n script.src = url;\r\n script.async = true;\r\n\r\n script.addEventListener('load', () => resolve(), { once: true });\r\n script.addEventListener(\r\n 'error',\r\n () => {\r\n loaders.delete(url);\r\n reject(new Error(`Failed to load PaySky Lightbox script: ${url}`));\r\n },\r\n { once: true },\r\n );\r\n\r\n document.head.appendChild(script);\r\n });\r\n\r\n loaders.set(url, promise);\r\n return promise;\r\n}\r\n\r\n/** Test helper — clears cached loaders between tests. */\r\nexport function __resetPaySkyScriptLoaders(): void {\r\n loaders.clear();\r\n}\r\n","import type { PaySkyNotifyPayload } from './types';\r\n\r\nexport async function postNotify(\r\n notifyUrl: string,\r\n payload: PaySkyNotifyPayload,\r\n headers?: Record<string, string>,\r\n): Promise<void> {\r\n const response = await fetch(notifyUrl, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...headers,\r\n },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(\r\n `PaySky notify failed: ${response.status} ${response.statusText}`,\r\n );\r\n }\r\n}\r\n","import { resolveScriptUrl } from './constants';\r\nimport { loadPaySkyScript } from './loadScript';\r\nimport { postNotify } from './notify';\r\nimport type {\r\n PaySkyCompleteData,\r\n PaySkyErrorData,\r\n PaySkyOpenOptions,\r\n PaySkySession,\r\n} from './types';\r\n\r\nlet openInProgress = false;\r\n\r\nfunction assertSession(session: PaySkySession): void {\r\n if (!session.mid?.trim()) {\r\n throw new Error('PaySky session.mid is required');\r\n }\r\n if (!session.tid?.trim()) {\r\n throw new Error('PaySky session.tid is required');\r\n }\r\n if (\r\n typeof session.amountTrxn !== 'number' ||\r\n !Number.isFinite(session.amountTrxn) ||\r\n session.amountTrxn <= 0\r\n ) {\r\n throw new Error('PaySky session.amountTrxn must be a positive number');\r\n }\r\n}\r\n\r\nfunction getCheckout() {\r\n const checkout = window.Lightbox?.Checkout;\r\n if (!checkout) {\r\n throw new Error(\r\n 'PaySky Lightbox is not available on window.Lightbox after script load',\r\n );\r\n }\r\n return checkout;\r\n}\r\n\r\nexport async function openLightbox(options: PaySkyOpenOptions): Promise<void> {\r\n const {\r\n session,\r\n environment = 'sandbox',\r\n scriptUrl,\r\n additionalCustomerData,\r\n paymentMethodFromLightBox,\r\n notifyUrl,\r\n notifyHeaders,\r\n onSuccess,\r\n onError,\r\n onCancel,\r\n onNotifyError,\r\n } = options;\r\n\r\n assertSession(session);\r\n\r\n if (openInProgress) {\r\n throw new Error('PaySky Lightbox is already open');\r\n }\r\n\r\n const url = resolveScriptUrl(environment, scriptUrl);\r\n await loadPaySkyScript(url);\r\n\r\n const checkout = getCheckout();\r\n\r\n openInProgress = true;\r\n\r\n const safeNotify = (\r\n event: 'complete' | 'error',\r\n data: PaySkyCompleteData | PaySkyErrorData,\r\n ) => {\r\n if (!notifyUrl) return;\r\n void postNotify(notifyUrl, { event, data }, notifyHeaders).catch(\r\n (error) => {\r\n onNotifyError?.(error);\r\n },\r\n );\r\n };\r\n\r\n checkout.configure = {\r\n MID: session.mid,\r\n TID: session.tid,\r\n AmountTrxn: session.amountTrxn,\r\n ...(session.merchantReference != null\r\n ? { MerchantReference: session.merchantReference }\r\n : {}),\r\n ...(session.trxDateTime != null ? { TrxDateTime: session.trxDateTime } : {}),\r\n ...(session.secureHash != null ? { SecureHash: session.secureHash } : {}),\r\n ...(paymentMethodFromLightBox != null\r\n ? { paymentMethodFromLightBox }\r\n : {}),\r\n ...(additionalCustomerData != null\r\n ? { AdditionalCustomerData: additionalCustomerData }\r\n : {}),\r\n completeCallback: (data: PaySkyCompleteData) => {\r\n openInProgress = false;\r\n onSuccess?.(data);\r\n safeNotify('complete', data);\r\n },\r\n errorCallback: (error: PaySkyErrorData) => {\r\n openInProgress = false;\r\n onError?.(error);\r\n safeNotify('error', error);\r\n },\r\n cancelCallback: () => {\r\n openInProgress = false;\r\n onCancel?.();\r\n },\r\n };\r\n\r\n checkout.showLightbox();\r\n}\r\n\r\nexport function closeLightbox(): void {\r\n openInProgress = false;\r\n window.Lightbox?.Checkout?.closeLightbox();\r\n}\r\n\r\n/** Test helper */\r\nexport function __resetCheckoutOpenState(): void {\r\n openInProgress = false;\r\n}\r\n","/**\r\n * Extract TokenCustomerId from a PaySky complete-callback payload (any nesting).\r\n * Merchant backend should persist this for later CreatePaymentSession calls.\r\n */\r\nexport function extractTokenCustomerId(payload: unknown): string | null {\r\n const keys = [\r\n 'TokenCustomerId',\r\n 'tokenCustomerId',\r\n 'CustomerId',\r\n 'customerId',\r\n 'customer_id',\r\n ];\r\n\r\n const visit = (value: unknown): string | null => {\r\n if (value == null) return null;\r\n if (typeof value === 'string' || typeof value === 'number') {\r\n const trimmed = String(value).trim();\r\n return trimmed ? trimmed : null;\r\n }\r\n if (typeof value !== 'object') return null;\r\n\r\n const record = value as Record<string, unknown>;\r\n for (const key of keys) {\r\n if (key in record) {\r\n const found = visit(record[key]);\r\n if (found) return found;\r\n }\r\n }\r\n for (const nested of Object.values(record)) {\r\n if (nested && typeof nested === 'object') {\r\n const found = visit(nested);\r\n if (found) return found;\r\n }\r\n }\r\n return null;\r\n };\r\n\r\n return visit(payload);\r\n}\r\n","import { useCallback, useState } from 'react';\r\nimport { closeLightbox, openLightbox } from './checkout';\r\nimport type { PaySkyOpenOptions } from './types';\r\n\r\nexport interface UsePaySkyResult {\r\n openPayment: (options: PaySkyOpenOptions) => Promise<void>;\r\n closePayment: () => void;\r\n isOpen: boolean;\r\n isLoading: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport function usePaySky(): UsePaySkyResult {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const closePayment = useCallback(() => {\r\n closeLightbox();\r\n setIsOpen(false);\r\n }, []);\r\n\r\n const openPayment = useCallback(async (options: PaySkyOpenOptions) => {\r\n setIsLoading(true);\r\n setError(null);\r\n\r\n try {\r\n await openLightbox({\r\n ...options,\r\n onSuccess: (data) => {\r\n setIsOpen(false);\r\n options.onSuccess?.(data);\r\n },\r\n onError: (err) => {\r\n setIsOpen(false);\r\n options.onError?.(err);\r\n },\r\n onCancel: () => {\r\n setIsOpen(false);\r\n options.onCancel?.();\r\n },\r\n });\r\n setIsOpen(true);\r\n } catch (err) {\r\n const next =\r\n err instanceof Error ? err : new Error('Failed to open PaySky Lightbox');\r\n setError(next);\r\n setIsOpen(false);\r\n throw next;\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }, []);\r\n\r\n return {\r\n openPayment,\r\n closePayment,\r\n isOpen,\r\n isLoading,\r\n error,\r\n };\r\n}\r\n","import type { PaySkyCheckoutProps } from './types';\r\nimport { usePaySky } from './usePaySky';\r\n\r\nexport function PaySkyCheckout({\r\n children = 'Pay with PaySky',\r\n className,\r\n disabled,\r\n ...options\r\n}: PaySkyCheckoutProps) {\r\n const { openPayment, isLoading, error } = usePaySky();\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n className={className}\r\n disabled={disabled || isLoading}\r\n aria-busy={isLoading || undefined}\r\n data-error={error ? error.message : undefined}\r\n onClick={() => {\r\n void openPayment(options);\r\n }}\r\n >\r\n {children}\r\n </button>\r\n );\r\n}\r\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -33,6 +33,28 @@ interface PaySkyErrorData {
|
|
|
33
33
|
Amount?: string;
|
|
34
34
|
SecureHash: string;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* PaySky Lightbox `AdditionalCustomerData` (tokenization / saved card).
|
|
38
|
+
* Merchant backend must call CreatePaymentSession and supply TokenCustomerSession.
|
|
39
|
+
*/
|
|
40
|
+
interface PaySkyAdditionalCustomerData {
|
|
41
|
+
TokenCustomerId?: string;
|
|
42
|
+
TokenCustomerSession?: string;
|
|
43
|
+
CustomerEmail?: string;
|
|
44
|
+
CustomerMobile?: string;
|
|
45
|
+
MessageCustomer?: string;
|
|
46
|
+
MerchantCustomerId?: string;
|
|
47
|
+
CurrencyTwo?: string;
|
|
48
|
+
CurrencyTwoValue?: string | number;
|
|
49
|
+
AdditionalFees?: number;
|
|
50
|
+
ShowCustomerEmail?: boolean;
|
|
51
|
+
ShowCustomerMobile?: boolean;
|
|
52
|
+
ShowMessageCustomer?: boolean;
|
|
53
|
+
ShowMerchantCustomerId?: boolean;
|
|
54
|
+
ShowCurrencyTwo?: boolean;
|
|
55
|
+
ShowAdditionalFees?: boolean;
|
|
56
|
+
ConsumerEnterAmount?: boolean;
|
|
57
|
+
}
|
|
36
58
|
type PaySkyNotifyEvent = 'complete' | 'error';
|
|
37
59
|
interface PaySkyNotifyPayload {
|
|
38
60
|
event: PaySkyNotifyEvent;
|
|
@@ -43,6 +65,15 @@ interface PaySkyOpenOptions {
|
|
|
43
65
|
environment?: PaySkyEnvironment;
|
|
44
66
|
/** Overrides the default sandbox/production script URL. */
|
|
45
67
|
scriptUrl?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Tokenization / customer fields passed to Lightbox as AdditionalCustomerData.
|
|
70
|
+
* Include TokenCustomerId + TokenCustomerSession for saved-card checkout.
|
|
71
|
+
*/
|
|
72
|
+
additionalCustomerData?: PaySkyAdditionalCustomerData;
|
|
73
|
+
/**
|
|
74
|
+
* PaySky lightbox paymentMethodFromLightBox (WooCommerce tokenization uses `2`).
|
|
75
|
+
*/
|
|
76
|
+
paymentMethodFromLightBox?: number;
|
|
46
77
|
/** Optional merchant backend URL to POST callback payloads. */
|
|
47
78
|
notifyUrl?: string;
|
|
48
79
|
notifyHeaders?: Record<string, string>;
|
|
@@ -77,6 +108,12 @@ declare function resolveScriptUrl(environment?: PaySkyEnvironment, scriptUrl?: s
|
|
|
77
108
|
declare function openLightbox(options: PaySkyOpenOptions): Promise<void>;
|
|
78
109
|
declare function closeLightbox(): void;
|
|
79
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Extract TokenCustomerId from a PaySky complete-callback payload (any nesting).
|
|
113
|
+
* Merchant backend should persist this for later CreatePaymentSession calls.
|
|
114
|
+
*/
|
|
115
|
+
declare function extractTokenCustomerId(payload: unknown): string | null;
|
|
116
|
+
|
|
80
117
|
interface UsePaySkyResult {
|
|
81
118
|
openPayment: (options: PaySkyOpenOptions) => Promise<void>;
|
|
82
119
|
closePayment: () => void;
|
|
@@ -88,4 +125,4 @@ declare function usePaySky(): UsePaySkyResult;
|
|
|
88
125
|
|
|
89
126
|
declare function PaySkyCheckout({ children, className, disabled, ...options }: PaySkyCheckoutProps): react.JSX.Element;
|
|
90
127
|
|
|
91
|
-
export { PaySkyCheckout, type PaySkyCheckoutProps, type PaySkyCompleteData, type PaySkyEnvironment, type PaySkyErrorData, type PaySkyNotifyEvent, type PaySkyNotifyPayload, type PaySkyOpenOptions, type PaySkySession, SCRIPT_URLS, type UsePaySkyResult, closeLightbox, openLightbox, resolveScriptUrl, usePaySky };
|
|
128
|
+
export { type PaySkyAdditionalCustomerData, PaySkyCheckout, type PaySkyCheckoutProps, type PaySkyCompleteData, type PaySkyEnvironment, type PaySkyErrorData, type PaySkyNotifyEvent, type PaySkyNotifyPayload, type PaySkyOpenOptions, type PaySkySession, SCRIPT_URLS, type UsePaySkyResult, closeLightbox, extractTokenCustomerId, openLightbox, resolveScriptUrl, usePaySky };
|
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,28 @@ interface PaySkyErrorData {
|
|
|
33
33
|
Amount?: string;
|
|
34
34
|
SecureHash: string;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* PaySky Lightbox `AdditionalCustomerData` (tokenization / saved card).
|
|
38
|
+
* Merchant backend must call CreatePaymentSession and supply TokenCustomerSession.
|
|
39
|
+
*/
|
|
40
|
+
interface PaySkyAdditionalCustomerData {
|
|
41
|
+
TokenCustomerId?: string;
|
|
42
|
+
TokenCustomerSession?: string;
|
|
43
|
+
CustomerEmail?: string;
|
|
44
|
+
CustomerMobile?: string;
|
|
45
|
+
MessageCustomer?: string;
|
|
46
|
+
MerchantCustomerId?: string;
|
|
47
|
+
CurrencyTwo?: string;
|
|
48
|
+
CurrencyTwoValue?: string | number;
|
|
49
|
+
AdditionalFees?: number;
|
|
50
|
+
ShowCustomerEmail?: boolean;
|
|
51
|
+
ShowCustomerMobile?: boolean;
|
|
52
|
+
ShowMessageCustomer?: boolean;
|
|
53
|
+
ShowMerchantCustomerId?: boolean;
|
|
54
|
+
ShowCurrencyTwo?: boolean;
|
|
55
|
+
ShowAdditionalFees?: boolean;
|
|
56
|
+
ConsumerEnterAmount?: boolean;
|
|
57
|
+
}
|
|
36
58
|
type PaySkyNotifyEvent = 'complete' | 'error';
|
|
37
59
|
interface PaySkyNotifyPayload {
|
|
38
60
|
event: PaySkyNotifyEvent;
|
|
@@ -43,6 +65,15 @@ interface PaySkyOpenOptions {
|
|
|
43
65
|
environment?: PaySkyEnvironment;
|
|
44
66
|
/** Overrides the default sandbox/production script URL. */
|
|
45
67
|
scriptUrl?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Tokenization / customer fields passed to Lightbox as AdditionalCustomerData.
|
|
70
|
+
* Include TokenCustomerId + TokenCustomerSession for saved-card checkout.
|
|
71
|
+
*/
|
|
72
|
+
additionalCustomerData?: PaySkyAdditionalCustomerData;
|
|
73
|
+
/**
|
|
74
|
+
* PaySky lightbox paymentMethodFromLightBox (WooCommerce tokenization uses `2`).
|
|
75
|
+
*/
|
|
76
|
+
paymentMethodFromLightBox?: number;
|
|
46
77
|
/** Optional merchant backend URL to POST callback payloads. */
|
|
47
78
|
notifyUrl?: string;
|
|
48
79
|
notifyHeaders?: Record<string, string>;
|
|
@@ -77,6 +108,12 @@ declare function resolveScriptUrl(environment?: PaySkyEnvironment, scriptUrl?: s
|
|
|
77
108
|
declare function openLightbox(options: PaySkyOpenOptions): Promise<void>;
|
|
78
109
|
declare function closeLightbox(): void;
|
|
79
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Extract TokenCustomerId from a PaySky complete-callback payload (any nesting).
|
|
113
|
+
* Merchant backend should persist this for later CreatePaymentSession calls.
|
|
114
|
+
*/
|
|
115
|
+
declare function extractTokenCustomerId(payload: unknown): string | null;
|
|
116
|
+
|
|
80
117
|
interface UsePaySkyResult {
|
|
81
118
|
openPayment: (options: PaySkyOpenOptions) => Promise<void>;
|
|
82
119
|
closePayment: () => void;
|
|
@@ -88,4 +125,4 @@ declare function usePaySky(): UsePaySkyResult;
|
|
|
88
125
|
|
|
89
126
|
declare function PaySkyCheckout({ children, className, disabled, ...options }: PaySkyCheckoutProps): react.JSX.Element;
|
|
90
127
|
|
|
91
|
-
export { PaySkyCheckout, type PaySkyCheckoutProps, type PaySkyCompleteData, type PaySkyEnvironment, type PaySkyErrorData, type PaySkyNotifyEvent, type PaySkyNotifyPayload, type PaySkyOpenOptions, type PaySkySession, SCRIPT_URLS, type UsePaySkyResult, closeLightbox, openLightbox, resolveScriptUrl, usePaySky };
|
|
128
|
+
export { type PaySkyAdditionalCustomerData, PaySkyCheckout, type PaySkyCheckoutProps, type PaySkyCompleteData, type PaySkyEnvironment, type PaySkyErrorData, type PaySkyNotifyEvent, type PaySkyNotifyPayload, type PaySkyOpenOptions, type PaySkySession, SCRIPT_URLS, type UsePaySkyResult, closeLightbox, extractTokenCustomerId, openLightbox, resolveScriptUrl, usePaySky };
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,8 @@ async function openLightbox(options) {
|
|
|
77
77
|
session,
|
|
78
78
|
environment = "sandbox",
|
|
79
79
|
scriptUrl,
|
|
80
|
+
additionalCustomerData,
|
|
81
|
+
paymentMethodFromLightBox,
|
|
80
82
|
notifyUrl,
|
|
81
83
|
notifyHeaders,
|
|
82
84
|
onSuccess,
|
|
@@ -107,6 +109,8 @@ async function openLightbox(options) {
|
|
|
107
109
|
...session.merchantReference != null ? { MerchantReference: session.merchantReference } : {},
|
|
108
110
|
...session.trxDateTime != null ? { TrxDateTime: session.trxDateTime } : {},
|
|
109
111
|
...session.secureHash != null ? { SecureHash: session.secureHash } : {},
|
|
112
|
+
...paymentMethodFromLightBox != null ? { paymentMethodFromLightBox } : {},
|
|
113
|
+
...additionalCustomerData != null ? { AdditionalCustomerData: additionalCustomerData } : {},
|
|
110
114
|
completeCallback: (data) => {
|
|
111
115
|
openInProgress = false;
|
|
112
116
|
onSuccess?.(data);
|
|
@@ -129,6 +133,40 @@ function closeLightbox() {
|
|
|
129
133
|
window.Lightbox?.Checkout?.closeLightbox();
|
|
130
134
|
}
|
|
131
135
|
|
|
136
|
+
// src/tokenization.ts
|
|
137
|
+
function extractTokenCustomerId(payload) {
|
|
138
|
+
const keys = [
|
|
139
|
+
"TokenCustomerId",
|
|
140
|
+
"tokenCustomerId",
|
|
141
|
+
"CustomerId",
|
|
142
|
+
"customerId",
|
|
143
|
+
"customer_id"
|
|
144
|
+
];
|
|
145
|
+
const visit = (value) => {
|
|
146
|
+
if (value == null) return null;
|
|
147
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
148
|
+
const trimmed = String(value).trim();
|
|
149
|
+
return trimmed ? trimmed : null;
|
|
150
|
+
}
|
|
151
|
+
if (typeof value !== "object") return null;
|
|
152
|
+
const record = value;
|
|
153
|
+
for (const key of keys) {
|
|
154
|
+
if (key in record) {
|
|
155
|
+
const found = visit(record[key]);
|
|
156
|
+
if (found) return found;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (const nested of Object.values(record)) {
|
|
160
|
+
if (nested && typeof nested === "object") {
|
|
161
|
+
const found = visit(nested);
|
|
162
|
+
if (found) return found;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
};
|
|
167
|
+
return visit(payload);
|
|
168
|
+
}
|
|
169
|
+
|
|
132
170
|
// src/usePaySky.ts
|
|
133
171
|
import { useCallback, useState } from "react";
|
|
134
172
|
function usePaySky() {
|
|
@@ -205,6 +243,7 @@ export {
|
|
|
205
243
|
PaySkyCheckout,
|
|
206
244
|
SCRIPT_URLS,
|
|
207
245
|
closeLightbox,
|
|
246
|
+
extractTokenCustomerId,
|
|
208
247
|
openLightbox,
|
|
209
248
|
resolveScriptUrl,
|
|
210
249
|
usePaySky
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/loadScript.ts","../src/notify.ts","../src/checkout.ts","../src/usePaySky.ts","../src/PaySkyCheckout.tsx"],"sourcesContent":["import type { PaySkyEnvironment } from './types';\r\n\r\nexport const SCRIPT_URLS: Record<PaySkyEnvironment, string> = {\r\n sandbox: 'https://red3.paysky.io:3011/LB/js/LightBox.js',\r\n production: 'https://cube.paysky.io:6006/js/LightBox.js',\r\n};\r\n\r\nexport function resolveScriptUrl(\r\n environment: PaySkyEnvironment = 'sandbox',\r\n scriptUrl?: string,\r\n): string {\r\n return scriptUrl ?? SCRIPT_URLS[environment];\r\n}\r\n","const loaders = new Map<string, Promise<void>>();\r\n\r\nexport function loadPaySkyScript(url: string): Promise<void> {\r\n const existing = loaders.get(url);\r\n if (existing) {\r\n return existing;\r\n }\r\n\r\n const promise = new Promise<void>((resolve, reject) => {\r\n const script = document.createElement('script');\r\n script.src = url;\r\n script.async = true;\r\n\r\n script.addEventListener('load', () => resolve(), { once: true });\r\n script.addEventListener(\r\n 'error',\r\n () => {\r\n loaders.delete(url);\r\n reject(new Error(`Failed to load PaySky Lightbox script: ${url}`));\r\n },\r\n { once: true },\r\n );\r\n\r\n document.head.appendChild(script);\r\n });\r\n\r\n loaders.set(url, promise);\r\n return promise;\r\n}\r\n\r\n/** Test helper — clears cached loaders between tests. */\r\nexport function __resetPaySkyScriptLoaders(): void {\r\n loaders.clear();\r\n}\r\n","import type { PaySkyNotifyPayload } from './types';\r\n\r\nexport async function postNotify(\r\n notifyUrl: string,\r\n payload: PaySkyNotifyPayload,\r\n headers?: Record<string, string>,\r\n): Promise<void> {\r\n const response = await fetch(notifyUrl, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...headers,\r\n },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(\r\n `PaySky notify failed: ${response.status} ${response.statusText}`,\r\n );\r\n }\r\n}\r\n","import { resolveScriptUrl } from './constants';\r\nimport { loadPaySkyScript } from './loadScript';\r\nimport { postNotify } from './notify';\r\nimport type {\r\n PaySkyCompleteData,\r\n PaySkyErrorData,\r\n PaySkyOpenOptions,\r\n PaySkySession,\r\n} from './types';\r\n\r\nlet openInProgress = false;\r\n\r\nfunction assertSession(session: PaySkySession): void {\r\n if (!session.mid?.trim()) {\r\n throw new Error('PaySky session.mid is required');\r\n }\r\n if (!session.tid?.trim()) {\r\n throw new Error('PaySky session.tid is required');\r\n }\r\n if (\r\n typeof session.amountTrxn !== 'number' ||\r\n !Number.isFinite(session.amountTrxn) ||\r\n session.amountTrxn <= 0\r\n ) {\r\n throw new Error('PaySky session.amountTrxn must be a positive number');\r\n }\r\n}\r\n\r\nfunction getCheckout() {\r\n const checkout = window.Lightbox?.Checkout;\r\n if (!checkout) {\r\n throw new Error(\r\n 'PaySky Lightbox is not available on window.Lightbox after script load',\r\n );\r\n }\r\n return checkout;\r\n}\r\n\r\nexport async function openLightbox(options: PaySkyOpenOptions): Promise<void> {\r\n const {\r\n session,\r\n environment = 'sandbox',\r\n scriptUrl,\r\n notifyUrl,\r\n notifyHeaders,\r\n onSuccess,\r\n onError,\r\n onCancel,\r\n onNotifyError,\r\n } = options;\r\n\r\n assertSession(session);\r\n\r\n if (openInProgress) {\r\n throw new Error('PaySky Lightbox is already open');\r\n }\r\n\r\n const url = resolveScriptUrl(environment, scriptUrl);\r\n await loadPaySkyScript(url);\r\n\r\n const checkout = getCheckout();\r\n\r\n openInProgress = true;\r\n\r\n const safeNotify = (\r\n event: 'complete' | 'error',\r\n data: PaySkyCompleteData | PaySkyErrorData,\r\n ) => {\r\n if (!notifyUrl) return;\r\n void postNotify(notifyUrl, { event, data }, notifyHeaders).catch(\r\n (error) => {\r\n onNotifyError?.(error);\r\n },\r\n );\r\n };\r\n\r\n checkout.configure = {\r\n MID: session.mid,\r\n TID: session.tid,\r\n AmountTrxn: session.amountTrxn,\r\n ...(session.merchantReference != null\r\n ? { MerchantReference: session.merchantReference }\r\n : {}),\r\n ...(session.trxDateTime != null ? { TrxDateTime: session.trxDateTime } : {}),\r\n ...(session.secureHash != null ? { SecureHash: session.secureHash } : {}),\r\n completeCallback: (data: PaySkyCompleteData) => {\r\n openInProgress = false;\r\n onSuccess?.(data);\r\n safeNotify('complete', data);\r\n },\r\n errorCallback: (error: PaySkyErrorData) => {\r\n openInProgress = false;\r\n onError?.(error);\r\n safeNotify('error', error);\r\n },\r\n cancelCallback: () => {\r\n openInProgress = false;\r\n onCancel?.();\r\n },\r\n };\r\n\r\n checkout.showLightbox();\r\n}\r\n\r\nexport function closeLightbox(): void {\r\n openInProgress = false;\r\n window.Lightbox?.Checkout?.closeLightbox();\r\n}\r\n\r\n/** Test helper */\r\nexport function __resetCheckoutOpenState(): void {\r\n openInProgress = false;\r\n}\r\n","import { useCallback, useState } from 'react';\r\nimport { closeLightbox, openLightbox } from './checkout';\r\nimport type { PaySkyOpenOptions } from './types';\r\n\r\nexport interface UsePaySkyResult {\r\n openPayment: (options: PaySkyOpenOptions) => Promise<void>;\r\n closePayment: () => void;\r\n isOpen: boolean;\r\n isLoading: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport function usePaySky(): UsePaySkyResult {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const closePayment = useCallback(() => {\r\n closeLightbox();\r\n setIsOpen(false);\r\n }, []);\r\n\r\n const openPayment = useCallback(async (options: PaySkyOpenOptions) => {\r\n setIsLoading(true);\r\n setError(null);\r\n\r\n try {\r\n await openLightbox({\r\n ...options,\r\n onSuccess: (data) => {\r\n setIsOpen(false);\r\n options.onSuccess?.(data);\r\n },\r\n onError: (err) => {\r\n setIsOpen(false);\r\n options.onError?.(err);\r\n },\r\n onCancel: () => {\r\n setIsOpen(false);\r\n options.onCancel?.();\r\n },\r\n });\r\n setIsOpen(true);\r\n } catch (err) {\r\n const next =\r\n err instanceof Error ? err : new Error('Failed to open PaySky Lightbox');\r\n setError(next);\r\n setIsOpen(false);\r\n throw next;\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }, []);\r\n\r\n return {\r\n openPayment,\r\n closePayment,\r\n isOpen,\r\n isLoading,\r\n error,\r\n };\r\n}\r\n","import type { PaySkyCheckoutProps } from './types';\r\nimport { usePaySky } from './usePaySky';\r\n\r\nexport function PaySkyCheckout({\r\n children = 'Pay with PaySky',\r\n className,\r\n disabled,\r\n ...options\r\n}: PaySkyCheckoutProps) {\r\n const { openPayment, isLoading, error } = usePaySky();\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n className={className}\r\n disabled={disabled || isLoading}\r\n aria-busy={isLoading || undefined}\r\n data-error={error ? error.message : undefined}\r\n onClick={() => {\r\n void openPayment(options);\r\n }}\r\n >\r\n {children}\r\n </button>\r\n );\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/loadScript.ts","../src/notify.ts","../src/checkout.ts","../src/tokenization.ts","../src/usePaySky.ts","../src/PaySkyCheckout.tsx"],"sourcesContent":["import type { PaySkyEnvironment } from './types';\r\n\r\nexport const SCRIPT_URLS: Record<PaySkyEnvironment, string> = {\r\n sandbox: 'https://red3.paysky.io:3011/LB/js/LightBox.js',\r\n production: 'https://cube.paysky.io:6006/js/LightBox.js',\r\n};\r\n\r\nexport function resolveScriptUrl(\r\n environment: PaySkyEnvironment = 'sandbox',\r\n scriptUrl?: string,\r\n): string {\r\n return scriptUrl ?? SCRIPT_URLS[environment];\r\n}\r\n","const loaders = new Map<string, Promise<void>>();\r\n\r\nexport function loadPaySkyScript(url: string): Promise<void> {\r\n const existing = loaders.get(url);\r\n if (existing) {\r\n return existing;\r\n }\r\n\r\n const promise = new Promise<void>((resolve, reject) => {\r\n const script = document.createElement('script');\r\n script.src = url;\r\n script.async = true;\r\n\r\n script.addEventListener('load', () => resolve(), { once: true });\r\n script.addEventListener(\r\n 'error',\r\n () => {\r\n loaders.delete(url);\r\n reject(new Error(`Failed to load PaySky Lightbox script: ${url}`));\r\n },\r\n { once: true },\r\n );\r\n\r\n document.head.appendChild(script);\r\n });\r\n\r\n loaders.set(url, promise);\r\n return promise;\r\n}\r\n\r\n/** Test helper — clears cached loaders between tests. */\r\nexport function __resetPaySkyScriptLoaders(): void {\r\n loaders.clear();\r\n}\r\n","import type { PaySkyNotifyPayload } from './types';\r\n\r\nexport async function postNotify(\r\n notifyUrl: string,\r\n payload: PaySkyNotifyPayload,\r\n headers?: Record<string, string>,\r\n): Promise<void> {\r\n const response = await fetch(notifyUrl, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...headers,\r\n },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(\r\n `PaySky notify failed: ${response.status} ${response.statusText}`,\r\n );\r\n }\r\n}\r\n","import { resolveScriptUrl } from './constants';\r\nimport { loadPaySkyScript } from './loadScript';\r\nimport { postNotify } from './notify';\r\nimport type {\r\n PaySkyCompleteData,\r\n PaySkyErrorData,\r\n PaySkyOpenOptions,\r\n PaySkySession,\r\n} from './types';\r\n\r\nlet openInProgress = false;\r\n\r\nfunction assertSession(session: PaySkySession): void {\r\n if (!session.mid?.trim()) {\r\n throw new Error('PaySky session.mid is required');\r\n }\r\n if (!session.tid?.trim()) {\r\n throw new Error('PaySky session.tid is required');\r\n }\r\n if (\r\n typeof session.amountTrxn !== 'number' ||\r\n !Number.isFinite(session.amountTrxn) ||\r\n session.amountTrxn <= 0\r\n ) {\r\n throw new Error('PaySky session.amountTrxn must be a positive number');\r\n }\r\n}\r\n\r\nfunction getCheckout() {\r\n const checkout = window.Lightbox?.Checkout;\r\n if (!checkout) {\r\n throw new Error(\r\n 'PaySky Lightbox is not available on window.Lightbox after script load',\r\n );\r\n }\r\n return checkout;\r\n}\r\n\r\nexport async function openLightbox(options: PaySkyOpenOptions): Promise<void> {\r\n const {\r\n session,\r\n environment = 'sandbox',\r\n scriptUrl,\r\n additionalCustomerData,\r\n paymentMethodFromLightBox,\r\n notifyUrl,\r\n notifyHeaders,\r\n onSuccess,\r\n onError,\r\n onCancel,\r\n onNotifyError,\r\n } = options;\r\n\r\n assertSession(session);\r\n\r\n if (openInProgress) {\r\n throw new Error('PaySky Lightbox is already open');\r\n }\r\n\r\n const url = resolveScriptUrl(environment, scriptUrl);\r\n await loadPaySkyScript(url);\r\n\r\n const checkout = getCheckout();\r\n\r\n openInProgress = true;\r\n\r\n const safeNotify = (\r\n event: 'complete' | 'error',\r\n data: PaySkyCompleteData | PaySkyErrorData,\r\n ) => {\r\n if (!notifyUrl) return;\r\n void postNotify(notifyUrl, { event, data }, notifyHeaders).catch(\r\n (error) => {\r\n onNotifyError?.(error);\r\n },\r\n );\r\n };\r\n\r\n checkout.configure = {\r\n MID: session.mid,\r\n TID: session.tid,\r\n AmountTrxn: session.amountTrxn,\r\n ...(session.merchantReference != null\r\n ? { MerchantReference: session.merchantReference }\r\n : {}),\r\n ...(session.trxDateTime != null ? { TrxDateTime: session.trxDateTime } : {}),\r\n ...(session.secureHash != null ? { SecureHash: session.secureHash } : {}),\r\n ...(paymentMethodFromLightBox != null\r\n ? { paymentMethodFromLightBox }\r\n : {}),\r\n ...(additionalCustomerData != null\r\n ? { AdditionalCustomerData: additionalCustomerData }\r\n : {}),\r\n completeCallback: (data: PaySkyCompleteData) => {\r\n openInProgress = false;\r\n onSuccess?.(data);\r\n safeNotify('complete', data);\r\n },\r\n errorCallback: (error: PaySkyErrorData) => {\r\n openInProgress = false;\r\n onError?.(error);\r\n safeNotify('error', error);\r\n },\r\n cancelCallback: () => {\r\n openInProgress = false;\r\n onCancel?.();\r\n },\r\n };\r\n\r\n checkout.showLightbox();\r\n}\r\n\r\nexport function closeLightbox(): void {\r\n openInProgress = false;\r\n window.Lightbox?.Checkout?.closeLightbox();\r\n}\r\n\r\n/** Test helper */\r\nexport function __resetCheckoutOpenState(): void {\r\n openInProgress = false;\r\n}\r\n","/**\r\n * Extract TokenCustomerId from a PaySky complete-callback payload (any nesting).\r\n * Merchant backend should persist this for later CreatePaymentSession calls.\r\n */\r\nexport function extractTokenCustomerId(payload: unknown): string | null {\r\n const keys = [\r\n 'TokenCustomerId',\r\n 'tokenCustomerId',\r\n 'CustomerId',\r\n 'customerId',\r\n 'customer_id',\r\n ];\r\n\r\n const visit = (value: unknown): string | null => {\r\n if (value == null) return null;\r\n if (typeof value === 'string' || typeof value === 'number') {\r\n const trimmed = String(value).trim();\r\n return trimmed ? trimmed : null;\r\n }\r\n if (typeof value !== 'object') return null;\r\n\r\n const record = value as Record<string, unknown>;\r\n for (const key of keys) {\r\n if (key in record) {\r\n const found = visit(record[key]);\r\n if (found) return found;\r\n }\r\n }\r\n for (const nested of Object.values(record)) {\r\n if (nested && typeof nested === 'object') {\r\n const found = visit(nested);\r\n if (found) return found;\r\n }\r\n }\r\n return null;\r\n };\r\n\r\n return visit(payload);\r\n}\r\n","import { useCallback, useState } from 'react';\r\nimport { closeLightbox, openLightbox } from './checkout';\r\nimport type { PaySkyOpenOptions } from './types';\r\n\r\nexport interface UsePaySkyResult {\r\n openPayment: (options: PaySkyOpenOptions) => Promise<void>;\r\n closePayment: () => void;\r\n isOpen: boolean;\r\n isLoading: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport function usePaySky(): UsePaySkyResult {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [error, setError] = useState<Error | null>(null);\r\n\r\n const closePayment = useCallback(() => {\r\n closeLightbox();\r\n setIsOpen(false);\r\n }, []);\r\n\r\n const openPayment = useCallback(async (options: PaySkyOpenOptions) => {\r\n setIsLoading(true);\r\n setError(null);\r\n\r\n try {\r\n await openLightbox({\r\n ...options,\r\n onSuccess: (data) => {\r\n setIsOpen(false);\r\n options.onSuccess?.(data);\r\n },\r\n onError: (err) => {\r\n setIsOpen(false);\r\n options.onError?.(err);\r\n },\r\n onCancel: () => {\r\n setIsOpen(false);\r\n options.onCancel?.();\r\n },\r\n });\r\n setIsOpen(true);\r\n } catch (err) {\r\n const next =\r\n err instanceof Error ? err : new Error('Failed to open PaySky Lightbox');\r\n setError(next);\r\n setIsOpen(false);\r\n throw next;\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }, []);\r\n\r\n return {\r\n openPayment,\r\n closePayment,\r\n isOpen,\r\n isLoading,\r\n error,\r\n };\r\n}\r\n","import type { PaySkyCheckoutProps } from './types';\r\nimport { usePaySky } from './usePaySky';\r\n\r\nexport function PaySkyCheckout({\r\n children = 'Pay with PaySky',\r\n className,\r\n disabled,\r\n ...options\r\n}: PaySkyCheckoutProps) {\r\n const { openPayment, isLoading, error } = usePaySky();\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n className={className}\r\n disabled={disabled || isLoading}\r\n aria-busy={isLoading || undefined}\r\n data-error={error ? error.message : undefined}\r\n onClick={() => {\r\n void openPayment(options);\r\n }}\r\n >\r\n {children}\r\n </button>\r\n );\r\n}\r\n"],"mappings":"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","names":[]}
|