react-paysky-lightbox 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # react-paysky-lightbox
2
+
3
+ React hook and button component for **PaySky OMNI Gateway Lightbox**.
4
+
5
+ The package only opens PaySky’s hosted checkout. Your **backend** must create the payment session (including `SecureHash`) and verify the result SecureHash before fulfilling orders.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install react-paysky-lightbox
11
+ ```
12
+
13
+ Peer dependencies: `react` and `react-dom` ≥ 18.
14
+
15
+ ## Quick start — component
16
+
17
+ ```tsx
18
+ import { PaySkyCheckout } from 'react-paysky-lightbox';
19
+
20
+ function Checkout() {
21
+ return (
22
+ <PaySkyCheckout
23
+ environment="sandbox"
24
+ session={{
25
+ mid: session.mid,
26
+ tid: session.tid,
27
+ amountTrxn: session.amountTrxn,
28
+ merchantReference: session.merchantReference,
29
+ trxDateTime: session.trxDateTime,
30
+ secureHash: session.secureHash,
31
+ }}
32
+ notifyUrl="https://api.yoursite.com/paysky/notify"
33
+ onSuccess={(data) => console.log('paid', data)}
34
+ onError={(err) => console.error(err)}
35
+ onCancel={() => console.log('cancelled')}
36
+ >
37
+ Pay now
38
+ </PaySkyCheckout>
39
+ );
40
+ }
41
+ ```
42
+
43
+ ## Quick start — hook
44
+
45
+ ```tsx
46
+ import { usePaySky } from 'react-paysky-lightbox';
47
+
48
+ function CheckoutButton({ session }) {
49
+ const { openPayment, isLoading, error } = usePaySky();
50
+
51
+ return (
52
+ <button
53
+ disabled={isLoading}
54
+ onClick={() =>
55
+ void openPayment({
56
+ environment: 'production',
57
+ session,
58
+ onSuccess: (data) => {
59
+ /* still verify SecureHash on your backend */
60
+ },
61
+ })
62
+ }
63
+ >
64
+ {isLoading ? 'Opening…' : 'Pay'}
65
+ {error ? ` (${error.message})` : null}
66
+ </button>
67
+ );
68
+ }
69
+ ```
70
+
71
+ ## Backend responsibilities
72
+
73
+ 1. Generate `SecureHash` with your merchant secret (never ship the secret to the browser).
74
+ 2. Return session fields: `mid`, `tid`, `amountTrxn`, optional `merchantReference`, `trxDateTime`, `secureHash`.
75
+ 3. On success (browser `notifyUrl` and/or PaySky server notification), **re-verify** response `SecureHash` before marking the order paid.
76
+
77
+ Amount is in the **smallest currency unit** (1 EGP = `100`).
78
+
79
+ ## Environments
80
+
81
+ | `environment` | Script |
82
+ | --- | --- |
83
+ | `sandbox` (default) | `https://grey.paysky.io:9006/invchost/JS/LightBox.js` |
84
+ | `production` | `https://cube.paysky.io:6006/js/LightBox.js` |
85
+
86
+ Override with `scriptUrl` if PaySky gives you a different host.
87
+
88
+ ## Optional notify
89
+
90
+ If you pass `notifyUrl`, the package `POST`s JSON after complete/error:
91
+
92
+ ```json
93
+ { "event": "complete", "data": { /* PaySky callback fields */ } }
94
+ ```
95
+
96
+ Cancel does not notify. Notify failures call `onNotifyError` and do not change payment success.
97
+
98
+ ## License
99
+
100
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ PaySkyCheckout: () => PaySkyCheckout,
24
+ SCRIPT_URLS: () => SCRIPT_URLS,
25
+ closeLightbox: () => closeLightbox,
26
+ openLightbox: () => openLightbox,
27
+ resolveScriptUrl: () => resolveScriptUrl,
28
+ usePaySky: () => usePaySky
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/constants.ts
33
+ var SCRIPT_URLS = {
34
+ sandbox: "https://grey.paysky.io:9006/invchost/JS/LightBox.js",
35
+ production: "https://cube.paysky.io:6006/js/LightBox.js"
36
+ };
37
+ function resolveScriptUrl(environment = "sandbox", scriptUrl) {
38
+ return scriptUrl ?? SCRIPT_URLS[environment];
39
+ }
40
+
41
+ // src/loadScript.ts
42
+ var loaders = /* @__PURE__ */ new Map();
43
+ function loadPaySkyScript(url) {
44
+ const existing = loaders.get(url);
45
+ if (existing) {
46
+ return existing;
47
+ }
48
+ const promise = new Promise((resolve, reject) => {
49
+ const script = document.createElement("script");
50
+ script.src = url;
51
+ script.async = true;
52
+ script.addEventListener("load", () => resolve(), { once: true });
53
+ script.addEventListener(
54
+ "error",
55
+ () => {
56
+ loaders.delete(url);
57
+ reject(new Error(`Failed to load PaySky Lightbox script: ${url}`));
58
+ },
59
+ { once: true }
60
+ );
61
+ document.head.appendChild(script);
62
+ });
63
+ loaders.set(url, promise);
64
+ return promise;
65
+ }
66
+
67
+ // src/notify.ts
68
+ async function postNotify(notifyUrl, payload, headers) {
69
+ const response = await fetch(notifyUrl, {
70
+ method: "POST",
71
+ headers: {
72
+ "Content-Type": "application/json",
73
+ ...headers
74
+ },
75
+ body: JSON.stringify(payload)
76
+ });
77
+ if (!response.ok) {
78
+ throw new Error(
79
+ `PaySky notify failed: ${response.status} ${response.statusText}`
80
+ );
81
+ }
82
+ }
83
+
84
+ // src/checkout.ts
85
+ var openInProgress = false;
86
+ function assertSession(session) {
87
+ if (!session.mid?.trim()) {
88
+ throw new Error("PaySky session.mid is required");
89
+ }
90
+ if (!session.tid?.trim()) {
91
+ throw new Error("PaySky session.tid is required");
92
+ }
93
+ if (typeof session.amountTrxn !== "number" || !Number.isFinite(session.amountTrxn) || session.amountTrxn <= 0) {
94
+ throw new Error("PaySky session.amountTrxn must be a positive number");
95
+ }
96
+ }
97
+ function getCheckout() {
98
+ const checkout = window.Lightbox?.Checkout;
99
+ if (!checkout) {
100
+ throw new Error(
101
+ "PaySky Lightbox is not available on window.Lightbox after script load"
102
+ );
103
+ }
104
+ return checkout;
105
+ }
106
+ async function openLightbox(options) {
107
+ const {
108
+ session,
109
+ environment = "sandbox",
110
+ scriptUrl,
111
+ notifyUrl,
112
+ notifyHeaders,
113
+ onSuccess,
114
+ onError,
115
+ onCancel,
116
+ onNotifyError
117
+ } = options;
118
+ assertSession(session);
119
+ if (openInProgress) {
120
+ throw new Error("PaySky Lightbox is already open");
121
+ }
122
+ const url = resolveScriptUrl(environment, scriptUrl);
123
+ await loadPaySkyScript(url);
124
+ const checkout = getCheckout();
125
+ openInProgress = true;
126
+ const safeNotify = (event, data) => {
127
+ if (!notifyUrl) return;
128
+ void postNotify(notifyUrl, { event, data }, notifyHeaders).catch(
129
+ (error) => {
130
+ onNotifyError?.(error);
131
+ }
132
+ );
133
+ };
134
+ checkout.configure = {
135
+ MID: session.mid,
136
+ TID: session.tid,
137
+ AmountTrxn: session.amountTrxn,
138
+ ...session.merchantReference != null ? { MerchantReference: session.merchantReference } : {},
139
+ ...session.trxDateTime != null ? { TrxDateTime: session.trxDateTime } : {},
140
+ ...session.secureHash != null ? { SecureHash: session.secureHash } : {},
141
+ completeCallback: (data) => {
142
+ openInProgress = false;
143
+ onSuccess?.(data);
144
+ safeNotify("complete", data);
145
+ },
146
+ errorCallback: (error) => {
147
+ openInProgress = false;
148
+ onError?.(error);
149
+ safeNotify("error", error);
150
+ },
151
+ cancelCallback: () => {
152
+ openInProgress = false;
153
+ onCancel?.();
154
+ }
155
+ };
156
+ checkout.showLightbox();
157
+ }
158
+ function closeLightbox() {
159
+ openInProgress = false;
160
+ window.Lightbox?.Checkout?.closeLightbox();
161
+ }
162
+
163
+ // src/usePaySky.ts
164
+ var import_react = require("react");
165
+ function usePaySky() {
166
+ const [isOpen, setIsOpen] = (0, import_react.useState)(false);
167
+ const [isLoading, setIsLoading] = (0, import_react.useState)(false);
168
+ const [error, setError] = (0, import_react.useState)(null);
169
+ const closePayment = (0, import_react.useCallback)(() => {
170
+ closeLightbox();
171
+ setIsOpen(false);
172
+ }, []);
173
+ const openPayment = (0, import_react.useCallback)(async (options) => {
174
+ setIsLoading(true);
175
+ setError(null);
176
+ try {
177
+ await openLightbox({
178
+ ...options,
179
+ onSuccess: (data) => {
180
+ setIsOpen(false);
181
+ options.onSuccess?.(data);
182
+ },
183
+ onError: (err) => {
184
+ setIsOpen(false);
185
+ options.onError?.(err);
186
+ },
187
+ onCancel: () => {
188
+ setIsOpen(false);
189
+ options.onCancel?.();
190
+ }
191
+ });
192
+ setIsOpen(true);
193
+ } catch (err) {
194
+ const next = err instanceof Error ? err : new Error("Failed to open PaySky Lightbox");
195
+ setError(next);
196
+ setIsOpen(false);
197
+ throw next;
198
+ } finally {
199
+ setIsLoading(false);
200
+ }
201
+ }, []);
202
+ return {
203
+ openPayment,
204
+ closePayment,
205
+ isOpen,
206
+ isLoading,
207
+ error
208
+ };
209
+ }
210
+
211
+ // src/PaySkyCheckout.tsx
212
+ var import_jsx_runtime = require("react/jsx-runtime");
213
+ function PaySkyCheckout({
214
+ children = "Pay with PaySky",
215
+ className,
216
+ disabled,
217
+ ...options
218
+ }) {
219
+ const { openPayment, isLoading, error } = usePaySky();
220
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
221
+ "button",
222
+ {
223
+ type: "button",
224
+ className,
225
+ disabled: disabled || isLoading,
226
+ "aria-busy": isLoading || void 0,
227
+ "data-error": error ? error.message : void 0,
228
+ onClick: () => {
229
+ void openPayment(options);
230
+ },
231
+ children
232
+ }
233
+ );
234
+ }
235
+ // Annotate the CommonJS export names for ESM import in node:
236
+ 0 && (module.exports = {
237
+ PaySkyCheckout,
238
+ SCRIPT_URLS,
239
+ closeLightbox,
240
+ openLightbox,
241
+ resolveScriptUrl,
242
+ usePaySky
243
+ });
244
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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://grey.paysky.io:9006/invchost/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":[]}
@@ -0,0 +1,91 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ type PaySkyEnvironment = 'sandbox' | 'production';
5
+ /** Payment session prepared by the merchant backend (includes SecureHash). */
6
+ interface PaySkySession {
7
+ mid: string;
8
+ tid: string;
9
+ /** Amount in smallest currency unit (e.g. 100 = 1 EGP). */
10
+ amountTrxn: number;
11
+ merchantReference?: string;
12
+ /** Format yyyyMMddHHmm — required when secureHash is sent. */
13
+ trxDateTime?: string;
14
+ secureHash?: string;
15
+ }
16
+ interface PaySkyCompleteData {
17
+ TxnDate: string;
18
+ SystemReference: string;
19
+ NetworkReference: string;
20
+ MerchantReference?: string;
21
+ Amount: string;
22
+ Currency: string;
23
+ PaidThrough: string;
24
+ PayerAccount: string;
25
+ PayerName?: string;
26
+ ProviderSchemeName?: string;
27
+ SecureHash: string;
28
+ }
29
+ interface PaySkyErrorData {
30
+ error: string;
31
+ DateTimeLocalTrxn: string;
32
+ MerchantReference?: string;
33
+ Amount?: string;
34
+ SecureHash: string;
35
+ }
36
+ type PaySkyNotifyEvent = 'complete' | 'error';
37
+ interface PaySkyNotifyPayload {
38
+ event: PaySkyNotifyEvent;
39
+ data: PaySkyCompleteData | PaySkyErrorData;
40
+ }
41
+ interface PaySkyOpenOptions {
42
+ session: PaySkySession;
43
+ environment?: PaySkyEnvironment;
44
+ /** Overrides the default sandbox/production script URL. */
45
+ scriptUrl?: string;
46
+ /** Optional merchant backend URL to POST callback payloads. */
47
+ notifyUrl?: string;
48
+ notifyHeaders?: Record<string, string>;
49
+ onSuccess?: (data: PaySkyCompleteData) => void;
50
+ onError?: (error: PaySkyErrorData) => void;
51
+ onCancel?: () => void;
52
+ onNotifyError?: (error: unknown) => void;
53
+ }
54
+ interface PaySkyCheckoutProps extends PaySkyOpenOptions {
55
+ children?: ReactNode;
56
+ className?: string;
57
+ disabled?: boolean;
58
+ }
59
+ /** Minimal typing for the global PaySky Lightbox script. */
60
+ interface PaySkyLightboxCheckout {
61
+ configure: Record<string, unknown>;
62
+ showLightbox: () => void;
63
+ closeLightbox: () => void;
64
+ }
65
+ interface PaySkyLightboxGlobal {
66
+ Checkout: PaySkyLightboxCheckout;
67
+ }
68
+ declare global {
69
+ interface Window {
70
+ Lightbox?: PaySkyLightboxGlobal;
71
+ }
72
+ }
73
+
74
+ declare const SCRIPT_URLS: Record<PaySkyEnvironment, string>;
75
+ declare function resolveScriptUrl(environment?: PaySkyEnvironment, scriptUrl?: string): string;
76
+
77
+ declare function openLightbox(options: PaySkyOpenOptions): Promise<void>;
78
+ declare function closeLightbox(): void;
79
+
80
+ interface UsePaySkyResult {
81
+ openPayment: (options: PaySkyOpenOptions) => Promise<void>;
82
+ closePayment: () => void;
83
+ isOpen: boolean;
84
+ isLoading: boolean;
85
+ error: Error | null;
86
+ }
87
+ declare function usePaySky(): UsePaySkyResult;
88
+
89
+ declare function PaySkyCheckout({ children, className, disabled, ...options }: PaySkyCheckoutProps): react.JSX.Element;
90
+
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 };
@@ -0,0 +1,91 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ type PaySkyEnvironment = 'sandbox' | 'production';
5
+ /** Payment session prepared by the merchant backend (includes SecureHash). */
6
+ interface PaySkySession {
7
+ mid: string;
8
+ tid: string;
9
+ /** Amount in smallest currency unit (e.g. 100 = 1 EGP). */
10
+ amountTrxn: number;
11
+ merchantReference?: string;
12
+ /** Format yyyyMMddHHmm — required when secureHash is sent. */
13
+ trxDateTime?: string;
14
+ secureHash?: string;
15
+ }
16
+ interface PaySkyCompleteData {
17
+ TxnDate: string;
18
+ SystemReference: string;
19
+ NetworkReference: string;
20
+ MerchantReference?: string;
21
+ Amount: string;
22
+ Currency: string;
23
+ PaidThrough: string;
24
+ PayerAccount: string;
25
+ PayerName?: string;
26
+ ProviderSchemeName?: string;
27
+ SecureHash: string;
28
+ }
29
+ interface PaySkyErrorData {
30
+ error: string;
31
+ DateTimeLocalTrxn: string;
32
+ MerchantReference?: string;
33
+ Amount?: string;
34
+ SecureHash: string;
35
+ }
36
+ type PaySkyNotifyEvent = 'complete' | 'error';
37
+ interface PaySkyNotifyPayload {
38
+ event: PaySkyNotifyEvent;
39
+ data: PaySkyCompleteData | PaySkyErrorData;
40
+ }
41
+ interface PaySkyOpenOptions {
42
+ session: PaySkySession;
43
+ environment?: PaySkyEnvironment;
44
+ /** Overrides the default sandbox/production script URL. */
45
+ scriptUrl?: string;
46
+ /** Optional merchant backend URL to POST callback payloads. */
47
+ notifyUrl?: string;
48
+ notifyHeaders?: Record<string, string>;
49
+ onSuccess?: (data: PaySkyCompleteData) => void;
50
+ onError?: (error: PaySkyErrorData) => void;
51
+ onCancel?: () => void;
52
+ onNotifyError?: (error: unknown) => void;
53
+ }
54
+ interface PaySkyCheckoutProps extends PaySkyOpenOptions {
55
+ children?: ReactNode;
56
+ className?: string;
57
+ disabled?: boolean;
58
+ }
59
+ /** Minimal typing for the global PaySky Lightbox script. */
60
+ interface PaySkyLightboxCheckout {
61
+ configure: Record<string, unknown>;
62
+ showLightbox: () => void;
63
+ closeLightbox: () => void;
64
+ }
65
+ interface PaySkyLightboxGlobal {
66
+ Checkout: PaySkyLightboxCheckout;
67
+ }
68
+ declare global {
69
+ interface Window {
70
+ Lightbox?: PaySkyLightboxGlobal;
71
+ }
72
+ }
73
+
74
+ declare const SCRIPT_URLS: Record<PaySkyEnvironment, string>;
75
+ declare function resolveScriptUrl(environment?: PaySkyEnvironment, scriptUrl?: string): string;
76
+
77
+ declare function openLightbox(options: PaySkyOpenOptions): Promise<void>;
78
+ declare function closeLightbox(): void;
79
+
80
+ interface UsePaySkyResult {
81
+ openPayment: (options: PaySkyOpenOptions) => Promise<void>;
82
+ closePayment: () => void;
83
+ isOpen: boolean;
84
+ isLoading: boolean;
85
+ error: Error | null;
86
+ }
87
+ declare function usePaySky(): UsePaySkyResult;
88
+
89
+ declare function PaySkyCheckout({ children, className, disabled, ...options }: PaySkyCheckoutProps): react.JSX.Element;
90
+
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 };
package/dist/index.js ADDED
@@ -0,0 +1,212 @@
1
+ // src/constants.ts
2
+ var SCRIPT_URLS = {
3
+ sandbox: "https://grey.paysky.io:9006/invchost/JS/LightBox.js",
4
+ production: "https://cube.paysky.io:6006/js/LightBox.js"
5
+ };
6
+ function resolveScriptUrl(environment = "sandbox", scriptUrl) {
7
+ return scriptUrl ?? SCRIPT_URLS[environment];
8
+ }
9
+
10
+ // src/loadScript.ts
11
+ var loaders = /* @__PURE__ */ new Map();
12
+ function loadPaySkyScript(url) {
13
+ const existing = loaders.get(url);
14
+ if (existing) {
15
+ return existing;
16
+ }
17
+ const promise = new Promise((resolve, reject) => {
18
+ const script = document.createElement("script");
19
+ script.src = url;
20
+ script.async = true;
21
+ script.addEventListener("load", () => resolve(), { once: true });
22
+ script.addEventListener(
23
+ "error",
24
+ () => {
25
+ loaders.delete(url);
26
+ reject(new Error(`Failed to load PaySky Lightbox script: ${url}`));
27
+ },
28
+ { once: true }
29
+ );
30
+ document.head.appendChild(script);
31
+ });
32
+ loaders.set(url, promise);
33
+ return promise;
34
+ }
35
+
36
+ // src/notify.ts
37
+ async function postNotify(notifyUrl, payload, headers) {
38
+ const response = await fetch(notifyUrl, {
39
+ method: "POST",
40
+ headers: {
41
+ "Content-Type": "application/json",
42
+ ...headers
43
+ },
44
+ body: JSON.stringify(payload)
45
+ });
46
+ if (!response.ok) {
47
+ throw new Error(
48
+ `PaySky notify failed: ${response.status} ${response.statusText}`
49
+ );
50
+ }
51
+ }
52
+
53
+ // src/checkout.ts
54
+ var openInProgress = false;
55
+ function assertSession(session) {
56
+ if (!session.mid?.trim()) {
57
+ throw new Error("PaySky session.mid is required");
58
+ }
59
+ if (!session.tid?.trim()) {
60
+ throw new Error("PaySky session.tid is required");
61
+ }
62
+ if (typeof session.amountTrxn !== "number" || !Number.isFinite(session.amountTrxn) || session.amountTrxn <= 0) {
63
+ throw new Error("PaySky session.amountTrxn must be a positive number");
64
+ }
65
+ }
66
+ function getCheckout() {
67
+ const checkout = window.Lightbox?.Checkout;
68
+ if (!checkout) {
69
+ throw new Error(
70
+ "PaySky Lightbox is not available on window.Lightbox after script load"
71
+ );
72
+ }
73
+ return checkout;
74
+ }
75
+ async function openLightbox(options) {
76
+ const {
77
+ session,
78
+ environment = "sandbox",
79
+ scriptUrl,
80
+ notifyUrl,
81
+ notifyHeaders,
82
+ onSuccess,
83
+ onError,
84
+ onCancel,
85
+ onNotifyError
86
+ } = options;
87
+ assertSession(session);
88
+ if (openInProgress) {
89
+ throw new Error("PaySky Lightbox is already open");
90
+ }
91
+ const url = resolveScriptUrl(environment, scriptUrl);
92
+ await loadPaySkyScript(url);
93
+ const checkout = getCheckout();
94
+ openInProgress = true;
95
+ const safeNotify = (event, data) => {
96
+ if (!notifyUrl) return;
97
+ void postNotify(notifyUrl, { event, data }, notifyHeaders).catch(
98
+ (error) => {
99
+ onNotifyError?.(error);
100
+ }
101
+ );
102
+ };
103
+ checkout.configure = {
104
+ MID: session.mid,
105
+ TID: session.tid,
106
+ AmountTrxn: session.amountTrxn,
107
+ ...session.merchantReference != null ? { MerchantReference: session.merchantReference } : {},
108
+ ...session.trxDateTime != null ? { TrxDateTime: session.trxDateTime } : {},
109
+ ...session.secureHash != null ? { SecureHash: session.secureHash } : {},
110
+ completeCallback: (data) => {
111
+ openInProgress = false;
112
+ onSuccess?.(data);
113
+ safeNotify("complete", data);
114
+ },
115
+ errorCallback: (error) => {
116
+ openInProgress = false;
117
+ onError?.(error);
118
+ safeNotify("error", error);
119
+ },
120
+ cancelCallback: () => {
121
+ openInProgress = false;
122
+ onCancel?.();
123
+ }
124
+ };
125
+ checkout.showLightbox();
126
+ }
127
+ function closeLightbox() {
128
+ openInProgress = false;
129
+ window.Lightbox?.Checkout?.closeLightbox();
130
+ }
131
+
132
+ // src/usePaySky.ts
133
+ import { useCallback, useState } from "react";
134
+ function usePaySky() {
135
+ const [isOpen, setIsOpen] = useState(false);
136
+ const [isLoading, setIsLoading] = useState(false);
137
+ const [error, setError] = useState(null);
138
+ const closePayment = useCallback(() => {
139
+ closeLightbox();
140
+ setIsOpen(false);
141
+ }, []);
142
+ const openPayment = useCallback(async (options) => {
143
+ setIsLoading(true);
144
+ setError(null);
145
+ try {
146
+ await openLightbox({
147
+ ...options,
148
+ onSuccess: (data) => {
149
+ setIsOpen(false);
150
+ options.onSuccess?.(data);
151
+ },
152
+ onError: (err) => {
153
+ setIsOpen(false);
154
+ options.onError?.(err);
155
+ },
156
+ onCancel: () => {
157
+ setIsOpen(false);
158
+ options.onCancel?.();
159
+ }
160
+ });
161
+ setIsOpen(true);
162
+ } catch (err) {
163
+ const next = err instanceof Error ? err : new Error("Failed to open PaySky Lightbox");
164
+ setError(next);
165
+ setIsOpen(false);
166
+ throw next;
167
+ } finally {
168
+ setIsLoading(false);
169
+ }
170
+ }, []);
171
+ return {
172
+ openPayment,
173
+ closePayment,
174
+ isOpen,
175
+ isLoading,
176
+ error
177
+ };
178
+ }
179
+
180
+ // src/PaySkyCheckout.tsx
181
+ import { jsx } from "react/jsx-runtime";
182
+ function PaySkyCheckout({
183
+ children = "Pay with PaySky",
184
+ className,
185
+ disabled,
186
+ ...options
187
+ }) {
188
+ const { openPayment, isLoading, error } = usePaySky();
189
+ return /* @__PURE__ */ jsx(
190
+ "button",
191
+ {
192
+ type: "button",
193
+ className,
194
+ disabled: disabled || isLoading,
195
+ "aria-busy": isLoading || void 0,
196
+ "data-error": error ? error.message : void 0,
197
+ onClick: () => {
198
+ void openPayment(options);
199
+ },
200
+ children
201
+ }
202
+ );
203
+ }
204
+ export {
205
+ PaySkyCheckout,
206
+ SCRIPT_URLS,
207
+ closeLightbox,
208
+ openLightbox,
209
+ resolveScriptUrl,
210
+ usePaySky
211
+ };
212
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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://grey.paysky.io:9006/invchost/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":[]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "react-paysky-lightbox",
3
+ "version": "0.1.0",
4
+ "description": "React hook and component for PaySky OMNI Gateway Lightbox",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "sideEffects": false,
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "typecheck": "tsc --noEmit"
27
+ },
28
+ "peerDependencies": {
29
+ "react": ">=18",
30
+ "react-dom": ">=18"
31
+ },
32
+ "devDependencies": {
33
+ "@testing-library/react": "^16.0.0",
34
+ "@types/react": "^18.3.0",
35
+ "@types/react-dom": "^18.3.0",
36
+ "jsdom": "^24.0.0",
37
+ "react": "^18.3.0",
38
+ "react-dom": "^18.3.0",
39
+ "tsup": "^8.0.0",
40
+ "typescript": "^5.5.0",
41
+ "vitest": "^2.0.0"
42
+ },
43
+ "keywords": [
44
+ "paysky",
45
+ "lightbox",
46
+ "react",
47
+ "payments",
48
+ "egypt"
49
+ ]
50
+ }