@oleq-ai/pay 0.4.2

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.
@@ -0,0 +1,353 @@
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/react/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ useOleqPayCheckout: () => useOleqPayCheckout
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/react/use-oleqpay-checkout.ts
28
+ var import_react = require("react");
29
+
30
+ // src/encode.ts
31
+ function encodePaymentData(payload) {
32
+ const json = JSON.stringify(payload);
33
+ const firstEncode = utf8ToBase64(json);
34
+ return utf8ToBase64(firstEncode);
35
+ }
36
+ function utf8ToBase64(input) {
37
+ const bytes = new TextEncoder().encode(input);
38
+ let binary = "";
39
+ bytes.forEach((byte) => {
40
+ binary += String.fromCharCode(byte);
41
+ });
42
+ return btoa(binary);
43
+ }
44
+ function buildCheckoutUrl(baseUrl, payload, options) {
45
+ const qp = encodePaymentData(payload);
46
+ const locale = options?.locale ?? "en";
47
+ const path = options?.path ?? "p";
48
+ const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
49
+ const url = new URL(`${locale}/${path}`, normalizedBase);
50
+ url.searchParams.set("qp", qp);
51
+ return url.toString();
52
+ }
53
+
54
+ // src/errors.ts
55
+ var OleqPayCheckoutError = class extends Error {
56
+ constructor(message, code) {
57
+ super(message);
58
+ this.name = "OleqPayCheckoutError";
59
+ this.code = code;
60
+ }
61
+ };
62
+
63
+ // src/modal.ts
64
+ var OVERLAY_ID = "oleqpay-checkout-overlay";
65
+ function openCheckoutModal(url, onCloseRequested) {
66
+ removeExistingModal();
67
+ const overlay = document.createElement("div");
68
+ overlay.id = OVERLAY_ID;
69
+ Object.assign(overlay.style, {
70
+ position: "fixed",
71
+ inset: "0",
72
+ zIndex: "2147483000",
73
+ display: "flex",
74
+ alignItems: "center",
75
+ justifyContent: "center",
76
+ background: "rgba(15, 23, 42, 0.55)",
77
+ backdropFilter: "blur(2px)"
78
+ });
79
+ const panel = document.createElement("div");
80
+ Object.assign(panel.style, {
81
+ position: "relative",
82
+ width: "min(440px, calc(100vw - 32px))",
83
+ height: "min(720px, calc(100vh - 32px))",
84
+ borderRadius: "16px",
85
+ overflow: "hidden",
86
+ boxShadow: "0 20px 60px rgba(0,0,0,0.35)",
87
+ background: "#fff"
88
+ });
89
+ const closeBtn = document.createElement("button");
90
+ closeBtn.setAttribute("aria-label", "Close checkout");
91
+ closeBtn.textContent = "\xD7";
92
+ Object.assign(closeBtn.style, {
93
+ position: "absolute",
94
+ top: "8px",
95
+ right: "8px",
96
+ width: "32px",
97
+ height: "32px",
98
+ borderRadius: "9999px",
99
+ border: "none",
100
+ background: "rgba(0,0,0,0.06)",
101
+ fontSize: "20px",
102
+ lineHeight: "1",
103
+ cursor: "pointer",
104
+ zIndex: "1"
105
+ });
106
+ closeBtn.onclick = () => onCloseRequested();
107
+ const iframe = document.createElement("iframe");
108
+ iframe.src = url;
109
+ iframe.title = "OleqPay Checkout";
110
+ iframe.setAttribute(
111
+ "allow",
112
+ "payment *; clipboard-write; camera; publickey-credentials-get"
113
+ );
114
+ Object.assign(iframe.style, {
115
+ width: "100%",
116
+ height: "100%",
117
+ border: "0"
118
+ });
119
+ panel.appendChild(closeBtn);
120
+ panel.appendChild(iframe);
121
+ overlay.appendChild(panel);
122
+ document.body.appendChild(overlay);
123
+ document.body.style.overflow = "hidden";
124
+ const onKeydown = (e) => {
125
+ if (e.key === "Escape") onCloseRequested();
126
+ };
127
+ window.addEventListener("keydown", onKeydown);
128
+ return {
129
+ iframe,
130
+ close: () => {
131
+ window.removeEventListener("keydown", onKeydown);
132
+ document.body.style.overflow = "";
133
+ overlay.remove();
134
+ }
135
+ };
136
+ }
137
+ function removeExistingModal() {
138
+ const existing = document.getElementById(OVERLAY_ID);
139
+ if (!existing) return;
140
+ existing.remove();
141
+ document.body.style.overflow = "";
142
+ }
143
+
144
+ // src/api-key.ts
145
+ function assertPublicApiKey(apikey) {
146
+ if (!apikey) {
147
+ throw new OleqPayCheckoutError(
148
+ "OleqPayCheckout: apikey is required.",
149
+ "invalid_api_key"
150
+ );
151
+ }
152
+ if (apikey.startsWith("sk_")) {
153
+ throw new OleqPayCheckoutError(
154
+ "OleqPayCheckout: this looks like a secret key (sk_\u2026). Only public keys belong in browser code \u2014 mint a session server-side with buildUrl() and pass the resulting URL to openIframe()/redirect() instead.",
155
+ "invalid_api_key"
156
+ );
157
+ }
158
+ }
159
+
160
+ // src/payload.ts
161
+ function assertValidPayload(payload) {
162
+ assertPublicApiKey(payload.apikey);
163
+ if (payload.apikey.length < 10) {
164
+ throw new OleqPayCheckoutError(
165
+ "OleqPayCheckout: apikey must be at least 10 characters.",
166
+ "invalid_api_key"
167
+ );
168
+ }
169
+ if (payload.orderid === void 0 || payload.orderid === "") {
170
+ throw new OleqPayCheckoutError(
171
+ "OleqPayCheckout: orderid is required \u2014 the checkout page rejects payloads without it.",
172
+ "invalid_payload"
173
+ );
174
+ }
175
+ if (!payload.reference) {
176
+ throw new OleqPayCheckoutError(
177
+ "OleqPayCheckout: reference is required \u2014 the checkout page rejects payloads without it.",
178
+ "invalid_payload"
179
+ );
180
+ }
181
+ const amountValue = typeof payload.amount === "number" ? payload.amount : parseFloat(payload.amount);
182
+ if (!Number.isFinite(amountValue) || amountValue <= 0) {
183
+ throw new OleqPayCheckoutError(
184
+ "OleqPayCheckout: amount must be a number greater than 0.",
185
+ "invalid_payload"
186
+ );
187
+ }
188
+ if (payload.callbackurl && !isValidUrl(payload.callbackurl)) {
189
+ throw new OleqPayCheckoutError(
190
+ "OleqPayCheckout: callbackurl must be a valid URL.",
191
+ "invalid_payload"
192
+ );
193
+ }
194
+ if (payload.returnurl && !isValidUrl(payload.returnurl)) {
195
+ throw new OleqPayCheckoutError(
196
+ "OleqPayCheckout: returnurl must be a valid URL.",
197
+ "invalid_payload"
198
+ );
199
+ }
200
+ }
201
+ function isValidUrl(value) {
202
+ try {
203
+ new URL(value);
204
+ return true;
205
+ } catch {
206
+ return false;
207
+ }
208
+ }
209
+
210
+ // src/types.ts
211
+ var DEFAULT_CHECKOUT_BASE_URL = "https://my.olefi.co";
212
+
213
+ // src/client.ts
214
+ var OleqPayCheckout = class {
215
+ constructor(options = {}) {
216
+ this.activeModal = null;
217
+ this.messageHandler = null;
218
+ this.checkoutBaseUrl = options.checkoutBaseUrl ?? DEFAULT_CHECKOUT_BASE_URL;
219
+ this.locale = options.locale ?? "en";
220
+ }
221
+ /**
222
+ * Build a hosted-checkout URL from a payload.
223
+ * Safe on the server (no DOM) — use this to mint session URLs in Nest/backends.
224
+ */
225
+ buildUrl(payload) {
226
+ assertValidPayload(payload);
227
+ return buildCheckoutUrl(this.checkoutBaseUrl, payload, {
228
+ locale: this.locale
229
+ });
230
+ }
231
+ /** Opens checkout in a modal iframe. Accepts either a raw payload or a pre-built session URL. */
232
+ openIframe(payloadOrUrl, options = {}) {
233
+ if (typeof window === "undefined") {
234
+ throw new OleqPayCheckoutError(
235
+ "OleqPayCheckout.openIframe() can only run in a browser.",
236
+ "ssr_unsupported"
237
+ );
238
+ }
239
+ this.close();
240
+ const url = this.resolveUrl(payloadOrUrl);
241
+ const targetOrigin = new URL(url).origin;
242
+ const onUserClose = () => {
243
+ this.close();
244
+ options.onClose?.();
245
+ };
246
+ this.activeModal = openCheckoutModal(url, onUserClose);
247
+ this.messageHandler = (event) => {
248
+ if (event.origin !== targetOrigin) return;
249
+ const data = event.data;
250
+ if (!data || typeof data !== "object" || !("type" in data)) return;
251
+ switch (data.type) {
252
+ case "oleqpay:success":
253
+ options.onSuccess?.(data);
254
+ this.close();
255
+ break;
256
+ case "oleqpay:failed":
257
+ options.onError?.(data);
258
+ break;
259
+ case "oleqpay:close":
260
+ onUserClose();
261
+ break;
262
+ default:
263
+ break;
264
+ }
265
+ };
266
+ window.addEventListener("message", this.messageHandler);
267
+ }
268
+ /** Full-page redirect to hosted checkout — mirrors Stripe Checkout / Paystack redirect flow. */
269
+ redirect(payloadOrUrl) {
270
+ if (typeof window === "undefined") {
271
+ throw new OleqPayCheckoutError(
272
+ "OleqPayCheckout.redirect() can only run in a browser.",
273
+ "ssr_unsupported"
274
+ );
275
+ }
276
+ window.location.href = this.resolveUrl(payloadOrUrl);
277
+ }
278
+ /** Programmatically close an open iframe modal. */
279
+ close() {
280
+ this.activeModal?.close();
281
+ this.teardown();
282
+ }
283
+ resolveUrl(payloadOrUrl) {
284
+ if (typeof payloadOrUrl === "string") return payloadOrUrl;
285
+ return this.buildUrl(payloadOrUrl);
286
+ }
287
+ teardown() {
288
+ if (this.messageHandler) {
289
+ window.removeEventListener("message", this.messageHandler);
290
+ this.messageHandler = null;
291
+ }
292
+ this.activeModal = null;
293
+ }
294
+ };
295
+
296
+ // src/react/use-oleqpay-checkout.ts
297
+ function useOleqPayCheckout(options = {}) {
298
+ const { checkoutBaseUrl, locale, onSuccess, onError } = options;
299
+ const client = (0, import_react.useMemo)(
300
+ () => new OleqPayCheckout({ checkoutBaseUrl, locale }),
301
+ [checkoutBaseUrl, locale]
302
+ );
303
+ const [isOpen, setIsOpen] = (0, import_react.useState)(false);
304
+ const [error, setError] = (0, import_react.useState)(null);
305
+ const clearError = (0, import_react.useCallback)(() => setError(null), []);
306
+ const openIframe = (0, import_react.useCallback)(
307
+ (payloadOrUrl) => {
308
+ setError(null);
309
+ try {
310
+ client.openIframe(payloadOrUrl, {
311
+ onSuccess: (event) => {
312
+ setIsOpen(false);
313
+ onSuccess?.(event);
314
+ },
315
+ onError: (event) => {
316
+ setError(event.message ?? "Payment failed");
317
+ onError?.(event);
318
+ },
319
+ onClose: () => setIsOpen(false)
320
+ });
321
+ setIsOpen(true);
322
+ } catch (err) {
323
+ setError(
324
+ err instanceof Error ? err.message : "Unable to open checkout"
325
+ );
326
+ }
327
+ },
328
+ [client, onSuccess, onError]
329
+ );
330
+ const redirect = (0, import_react.useCallback)(
331
+ (payloadOrUrl) => {
332
+ setError(null);
333
+ try {
334
+ client.redirect(payloadOrUrl);
335
+ } catch (err) {
336
+ setError(
337
+ err instanceof Error ? err.message : "Unable to redirect to checkout"
338
+ );
339
+ }
340
+ },
341
+ [client]
342
+ );
343
+ const close = (0, import_react.useCallback)(() => {
344
+ client.close();
345
+ setIsOpen(false);
346
+ }, [client]);
347
+ (0, import_react.useEffect)(() => () => client.close(), [client]);
348
+ return { openIframe, redirect, close, isOpen, error, clearError };
349
+ }
350
+ // Annotate the CommonJS export names for ESM import in node:
351
+ 0 && (module.exports = {
352
+ useOleqPayCheckout
353
+ });
@@ -0,0 +1,326 @@
1
+ // src/react/use-oleqpay-checkout.ts
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
3
+
4
+ // src/encode.ts
5
+ function encodePaymentData(payload) {
6
+ const json = JSON.stringify(payload);
7
+ const firstEncode = utf8ToBase64(json);
8
+ return utf8ToBase64(firstEncode);
9
+ }
10
+ function utf8ToBase64(input) {
11
+ const bytes = new TextEncoder().encode(input);
12
+ let binary = "";
13
+ bytes.forEach((byte) => {
14
+ binary += String.fromCharCode(byte);
15
+ });
16
+ return btoa(binary);
17
+ }
18
+ function buildCheckoutUrl(baseUrl, payload, options) {
19
+ const qp = encodePaymentData(payload);
20
+ const locale = options?.locale ?? "en";
21
+ const path = options?.path ?? "p";
22
+ const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
23
+ const url = new URL(`${locale}/${path}`, normalizedBase);
24
+ url.searchParams.set("qp", qp);
25
+ return url.toString();
26
+ }
27
+
28
+ // src/errors.ts
29
+ var OleqPayCheckoutError = class extends Error {
30
+ constructor(message, code) {
31
+ super(message);
32
+ this.name = "OleqPayCheckoutError";
33
+ this.code = code;
34
+ }
35
+ };
36
+
37
+ // src/modal.ts
38
+ var OVERLAY_ID = "oleqpay-checkout-overlay";
39
+ function openCheckoutModal(url, onCloseRequested) {
40
+ removeExistingModal();
41
+ const overlay = document.createElement("div");
42
+ overlay.id = OVERLAY_ID;
43
+ Object.assign(overlay.style, {
44
+ position: "fixed",
45
+ inset: "0",
46
+ zIndex: "2147483000",
47
+ display: "flex",
48
+ alignItems: "center",
49
+ justifyContent: "center",
50
+ background: "rgba(15, 23, 42, 0.55)",
51
+ backdropFilter: "blur(2px)"
52
+ });
53
+ const panel = document.createElement("div");
54
+ Object.assign(panel.style, {
55
+ position: "relative",
56
+ width: "min(440px, calc(100vw - 32px))",
57
+ height: "min(720px, calc(100vh - 32px))",
58
+ borderRadius: "16px",
59
+ overflow: "hidden",
60
+ boxShadow: "0 20px 60px rgba(0,0,0,0.35)",
61
+ background: "#fff"
62
+ });
63
+ const closeBtn = document.createElement("button");
64
+ closeBtn.setAttribute("aria-label", "Close checkout");
65
+ closeBtn.textContent = "\xD7";
66
+ Object.assign(closeBtn.style, {
67
+ position: "absolute",
68
+ top: "8px",
69
+ right: "8px",
70
+ width: "32px",
71
+ height: "32px",
72
+ borderRadius: "9999px",
73
+ border: "none",
74
+ background: "rgba(0,0,0,0.06)",
75
+ fontSize: "20px",
76
+ lineHeight: "1",
77
+ cursor: "pointer",
78
+ zIndex: "1"
79
+ });
80
+ closeBtn.onclick = () => onCloseRequested();
81
+ const iframe = document.createElement("iframe");
82
+ iframe.src = url;
83
+ iframe.title = "OleqPay Checkout";
84
+ iframe.setAttribute(
85
+ "allow",
86
+ "payment *; clipboard-write; camera; publickey-credentials-get"
87
+ );
88
+ Object.assign(iframe.style, {
89
+ width: "100%",
90
+ height: "100%",
91
+ border: "0"
92
+ });
93
+ panel.appendChild(closeBtn);
94
+ panel.appendChild(iframe);
95
+ overlay.appendChild(panel);
96
+ document.body.appendChild(overlay);
97
+ document.body.style.overflow = "hidden";
98
+ const onKeydown = (e) => {
99
+ if (e.key === "Escape") onCloseRequested();
100
+ };
101
+ window.addEventListener("keydown", onKeydown);
102
+ return {
103
+ iframe,
104
+ close: () => {
105
+ window.removeEventListener("keydown", onKeydown);
106
+ document.body.style.overflow = "";
107
+ overlay.remove();
108
+ }
109
+ };
110
+ }
111
+ function removeExistingModal() {
112
+ const existing = document.getElementById(OVERLAY_ID);
113
+ if (!existing) return;
114
+ existing.remove();
115
+ document.body.style.overflow = "";
116
+ }
117
+
118
+ // src/api-key.ts
119
+ function assertPublicApiKey(apikey) {
120
+ if (!apikey) {
121
+ throw new OleqPayCheckoutError(
122
+ "OleqPayCheckout: apikey is required.",
123
+ "invalid_api_key"
124
+ );
125
+ }
126
+ if (apikey.startsWith("sk_")) {
127
+ throw new OleqPayCheckoutError(
128
+ "OleqPayCheckout: this looks like a secret key (sk_\u2026). Only public keys belong in browser code \u2014 mint a session server-side with buildUrl() and pass the resulting URL to openIframe()/redirect() instead.",
129
+ "invalid_api_key"
130
+ );
131
+ }
132
+ }
133
+
134
+ // src/payload.ts
135
+ function assertValidPayload(payload) {
136
+ assertPublicApiKey(payload.apikey);
137
+ if (payload.apikey.length < 10) {
138
+ throw new OleqPayCheckoutError(
139
+ "OleqPayCheckout: apikey must be at least 10 characters.",
140
+ "invalid_api_key"
141
+ );
142
+ }
143
+ if (payload.orderid === void 0 || payload.orderid === "") {
144
+ throw new OleqPayCheckoutError(
145
+ "OleqPayCheckout: orderid is required \u2014 the checkout page rejects payloads without it.",
146
+ "invalid_payload"
147
+ );
148
+ }
149
+ if (!payload.reference) {
150
+ throw new OleqPayCheckoutError(
151
+ "OleqPayCheckout: reference is required \u2014 the checkout page rejects payloads without it.",
152
+ "invalid_payload"
153
+ );
154
+ }
155
+ const amountValue = typeof payload.amount === "number" ? payload.amount : parseFloat(payload.amount);
156
+ if (!Number.isFinite(amountValue) || amountValue <= 0) {
157
+ throw new OleqPayCheckoutError(
158
+ "OleqPayCheckout: amount must be a number greater than 0.",
159
+ "invalid_payload"
160
+ );
161
+ }
162
+ if (payload.callbackurl && !isValidUrl(payload.callbackurl)) {
163
+ throw new OleqPayCheckoutError(
164
+ "OleqPayCheckout: callbackurl must be a valid URL.",
165
+ "invalid_payload"
166
+ );
167
+ }
168
+ if (payload.returnurl && !isValidUrl(payload.returnurl)) {
169
+ throw new OleqPayCheckoutError(
170
+ "OleqPayCheckout: returnurl must be a valid URL.",
171
+ "invalid_payload"
172
+ );
173
+ }
174
+ }
175
+ function isValidUrl(value) {
176
+ try {
177
+ new URL(value);
178
+ return true;
179
+ } catch {
180
+ return false;
181
+ }
182
+ }
183
+
184
+ // src/types.ts
185
+ var DEFAULT_CHECKOUT_BASE_URL = "https://my.olefi.co";
186
+
187
+ // src/client.ts
188
+ var OleqPayCheckout = class {
189
+ constructor(options = {}) {
190
+ this.activeModal = null;
191
+ this.messageHandler = null;
192
+ this.checkoutBaseUrl = options.checkoutBaseUrl ?? DEFAULT_CHECKOUT_BASE_URL;
193
+ this.locale = options.locale ?? "en";
194
+ }
195
+ /**
196
+ * Build a hosted-checkout URL from a payload.
197
+ * Safe on the server (no DOM) — use this to mint session URLs in Nest/backends.
198
+ */
199
+ buildUrl(payload) {
200
+ assertValidPayload(payload);
201
+ return buildCheckoutUrl(this.checkoutBaseUrl, payload, {
202
+ locale: this.locale
203
+ });
204
+ }
205
+ /** Opens checkout in a modal iframe. Accepts either a raw payload or a pre-built session URL. */
206
+ openIframe(payloadOrUrl, options = {}) {
207
+ if (typeof window === "undefined") {
208
+ throw new OleqPayCheckoutError(
209
+ "OleqPayCheckout.openIframe() can only run in a browser.",
210
+ "ssr_unsupported"
211
+ );
212
+ }
213
+ this.close();
214
+ const url = this.resolveUrl(payloadOrUrl);
215
+ const targetOrigin = new URL(url).origin;
216
+ const onUserClose = () => {
217
+ this.close();
218
+ options.onClose?.();
219
+ };
220
+ this.activeModal = openCheckoutModal(url, onUserClose);
221
+ this.messageHandler = (event) => {
222
+ if (event.origin !== targetOrigin) return;
223
+ const data = event.data;
224
+ if (!data || typeof data !== "object" || !("type" in data)) return;
225
+ switch (data.type) {
226
+ case "oleqpay:success":
227
+ options.onSuccess?.(data);
228
+ this.close();
229
+ break;
230
+ case "oleqpay:failed":
231
+ options.onError?.(data);
232
+ break;
233
+ case "oleqpay:close":
234
+ onUserClose();
235
+ break;
236
+ default:
237
+ break;
238
+ }
239
+ };
240
+ window.addEventListener("message", this.messageHandler);
241
+ }
242
+ /** Full-page redirect to hosted checkout — mirrors Stripe Checkout / Paystack redirect flow. */
243
+ redirect(payloadOrUrl) {
244
+ if (typeof window === "undefined") {
245
+ throw new OleqPayCheckoutError(
246
+ "OleqPayCheckout.redirect() can only run in a browser.",
247
+ "ssr_unsupported"
248
+ );
249
+ }
250
+ window.location.href = this.resolveUrl(payloadOrUrl);
251
+ }
252
+ /** Programmatically close an open iframe modal. */
253
+ close() {
254
+ this.activeModal?.close();
255
+ this.teardown();
256
+ }
257
+ resolveUrl(payloadOrUrl) {
258
+ if (typeof payloadOrUrl === "string") return payloadOrUrl;
259
+ return this.buildUrl(payloadOrUrl);
260
+ }
261
+ teardown() {
262
+ if (this.messageHandler) {
263
+ window.removeEventListener("message", this.messageHandler);
264
+ this.messageHandler = null;
265
+ }
266
+ this.activeModal = null;
267
+ }
268
+ };
269
+
270
+ // src/react/use-oleqpay-checkout.ts
271
+ function useOleqPayCheckout(options = {}) {
272
+ const { checkoutBaseUrl, locale, onSuccess, onError } = options;
273
+ const client = useMemo(
274
+ () => new OleqPayCheckout({ checkoutBaseUrl, locale }),
275
+ [checkoutBaseUrl, locale]
276
+ );
277
+ const [isOpen, setIsOpen] = useState(false);
278
+ const [error, setError] = useState(null);
279
+ const clearError = useCallback(() => setError(null), []);
280
+ const openIframe = useCallback(
281
+ (payloadOrUrl) => {
282
+ setError(null);
283
+ try {
284
+ client.openIframe(payloadOrUrl, {
285
+ onSuccess: (event) => {
286
+ setIsOpen(false);
287
+ onSuccess?.(event);
288
+ },
289
+ onError: (event) => {
290
+ setError(event.message ?? "Payment failed");
291
+ onError?.(event);
292
+ },
293
+ onClose: () => setIsOpen(false)
294
+ });
295
+ setIsOpen(true);
296
+ } catch (err) {
297
+ setError(
298
+ err instanceof Error ? err.message : "Unable to open checkout"
299
+ );
300
+ }
301
+ },
302
+ [client, onSuccess, onError]
303
+ );
304
+ const redirect = useCallback(
305
+ (payloadOrUrl) => {
306
+ setError(null);
307
+ try {
308
+ client.redirect(payloadOrUrl);
309
+ } catch (err) {
310
+ setError(
311
+ err instanceof Error ? err.message : "Unable to redirect to checkout"
312
+ );
313
+ }
314
+ },
315
+ [client]
316
+ );
317
+ const close = useCallback(() => {
318
+ client.close();
319
+ setIsOpen(false);
320
+ }, [client]);
321
+ useEffect(() => () => client.close(), [client]);
322
+ return { openIframe, redirect, close, isOpen, error, clearError };
323
+ }
324
+ export {
325
+ useOleqPayCheckout
326
+ };