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 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
@@ -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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-paysky-lightbox",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "React hook and component for PaySky OMNI Gateway Lightbox",
5
5
  "license": "MIT",
6
6
  "type": "module",