@valuepay/react 1.0.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +21 -0
  3. package/README.md +435 -0
  4. package/dist/cjs/components/ValuePayButton.js +14 -0
  5. package/dist/cjs/components/ValuePayButton.js.map +1 -0
  6. package/dist/cjs/components/ValuePayProvider.js +17 -0
  7. package/dist/cjs/components/ValuePayProvider.js.map +1 -0
  8. package/dist/cjs/hooks/useScript.js +40 -0
  9. package/dist/cjs/hooks/useScript.js.map +1 -0
  10. package/dist/cjs/hooks/useValuePay.js +90 -0
  11. package/dist/cjs/hooks/useValuePay.js.map +1 -0
  12. package/dist/cjs/index.js +16 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/utils/constants.js +12 -0
  15. package/dist/cjs/utils/constants.js.map +1 -0
  16. package/dist/cjs/utils/helpers.js +67 -0
  17. package/dist/cjs/utils/helpers.js.map +1 -0
  18. package/dist/esm/components/ValuePayButton.js +12 -0
  19. package/dist/esm/components/ValuePayButton.js.map +1 -0
  20. package/dist/esm/components/ValuePayProvider.js +15 -0
  21. package/dist/esm/components/ValuePayProvider.js.map +1 -0
  22. package/dist/esm/hooks/useScript.js +38 -0
  23. package/dist/esm/hooks/useScript.js.map +1 -0
  24. package/dist/esm/hooks/useValuePay.js +88 -0
  25. package/dist/esm/hooks/useValuePay.js.map +1 -0
  26. package/dist/esm/index.js +6 -0
  27. package/dist/esm/index.js.map +1 -0
  28. package/dist/esm/utils/constants.js +7 -0
  29. package/dist/esm/utils/constants.js.map +1 -0
  30. package/dist/esm/utils/helpers.js +63 -0
  31. package/dist/esm/utils/helpers.js.map +1 -0
  32. package/dist/types/components/ValuePayButton.d.ts +3 -0
  33. package/dist/types/components/ValuePayProvider.d.ts +2 -0
  34. package/dist/types/hooks/useScript.d.ts +5 -0
  35. package/dist/types/hooks/useValuePay.d.ts +2 -0
  36. package/dist/types/index.d.ts +6 -0
  37. package/dist/types/types/index.d.ts +74 -0
  38. package/dist/types/utils/constants.d.ts +5 -0
  39. package/dist/types/utils/helpers.d.ts +32 -0
  40. package/package.json +84 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2026-03-25
9
+
10
+ ### Added
11
+
12
+ - `<ValuePayButton />` component — drop-in pre-styled payment button with amount display
13
+ - `<ValuePayProvider />` component — headless component with ref-based `initialize()` method
14
+ - `useValuePay()` hook — programmatic API returning `{ initialize, isReady, isProcessing }`
15
+ - `useScript()` hook — dynamic script loader with deduplication and cleanup
16
+ - `generateTransactionRef()` utility — unique reference generator with configurable length
17
+ - Full TypeScript support with exported types: `ValuePayConfig`, `ValuePayResponse`, `ValuePayCustomer`, `ValuePayCustomization`, `PaymentChannel`, `ValuePayStatus`, `UseValuePayReturn`, `ValuePayButtonProps`, `ValuePayProviderProps`, `ValuePayProviderRef`
18
+ - Callback-based payment handling: `onSuccess`, `onClose`, `onCancelled`, `onCallback`, `onError`
19
+ - Redirect-based payment handling via `redirectUrl` prop
20
+ - Runtime config overrides via `initialize(overrides)` for dynamic amounts
21
+ - Checkout modal customization (title, description, logo)
22
+ - Payment channel selection (card, transfer, QR code, USSD, mobile money)
23
+ - Custom metadata support via `metaData` prop
24
+ - SSR-safe implementation (Next.js, Remix, Gatsby compatible)
25
+ - Dual CJS + ESM output with tree-shaking support
26
+ - Comprehensive test suite with 90%+ coverage
27
+ - Double-payment prevention via `isProcessing` guard
28
+
29
+ [1.0.0]: https://github.com/valuepayment/react-sdk/releases/tag/v1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Value Payment Solutions Limited
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,435 @@
1
+ # @valuepay/react
2
+
3
+ > Official React SDK for [ValuePay](https://valuepayng.com) — accept payments in your React app with minimal setup.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@valuepay/react.svg)](https://www.npmjs.com/package/@valuepay/react)
6
+ [![license](https://img.shields.io/npm/l/@valuepay/react.svg)](LICENSE)
7
+ [![coverage](https://img.shields.io/badge/coverage-90%25-brightgreen.svg)]()
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)]()
9
+
10
+ ---
11
+
12
+ ## Table of Contents
13
+
14
+ - [Installation](#installation)
15
+ - [Quick Start](#quick-start)
16
+ - [Option 1: Drop-in Button](#option-1-drop-in-button-simplest)
17
+ - [Option 2: Hook (Full Control)](#option-2-hook-full-control)
18
+ - [Option 3: Provider with Ref](#option-3-provider-with-ref-hidden-component)
19
+ - [Redirect Mode](#redirect-mode)
20
+ - [API Reference](#api-reference)
21
+ - [useValuePay](#usevaluepayconfig-valuepayconfig-usevaluepayreturn)
22
+ - [ValuePayButton](#valuepaybutton--props)
23
+ - [ValuePayProvider](#valuepayprovider--props)
24
+ - [ValuePayConfig](#valuepayconfig)
25
+ - [ValuePayResponse](#valuepayresponse)
26
+ - [Dynamic Amounts](#dynamic-amounts)
27
+ - [Payment Channels](#payment-channels)
28
+ - [Customizing the Checkout Modal](#customizing-the-checkout-modal)
29
+ - [Server-Side Verification](#server-side-verification)
30
+ - [TypeScript](#typescript)
31
+ - [Browser Support](#browser-support)
32
+ - [Security](#security)
33
+ - [Contributing](#contributing)
34
+ - [License](#license)
35
+
36
+ ---
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ npm install @valuepay/react
42
+ # or
43
+ yarn add @valuepay/react
44
+ # or
45
+ pnpm add @valuepay/react
46
+ ```
47
+
48
+ ### Requirements
49
+
50
+ - **React** `>=16.8.0` (hooks support required)
51
+ - **React DOM** `>=16.8.0`
52
+
53
+ ---
54
+
55
+ ## Quick Start
56
+
57
+ ### Option 1: Drop-in Button (Simplest)
58
+
59
+ ```tsx
60
+ import { ValuePayButton } from "@valuepay/react";
61
+
62
+ function Checkout() {
63
+ return (
64
+ <ValuePayButton
65
+ publicKey="KP_your_public_key"
66
+ amount={5000}
67
+ customer={{ email: "jane@example.com", fullName: "Jane Doe" }}
68
+ onSuccess={(response) => {
69
+ console.log("Payment successful!", response.ref, response.status);
70
+ }}
71
+ onClose={(response) => {
72
+ console.log("Checkout closed", response);
73
+ }}
74
+ onCancelled={(response) => {
75
+ console.log("Payment cancelled", response);
76
+ }}
77
+ />
78
+ );
79
+ }
80
+ ```
81
+
82
+ The button renders as **"Pay ₦5,000 Now"** by default. Customize with `text`, `className`, `style`, or pass `children`.
83
+
84
+ ### Option 2: Hook (Full Control)
85
+
86
+ ```tsx
87
+ import { useValuePay } from "@valuepay/react";
88
+
89
+ function Checkout() {
90
+ const { initialize, isReady, isProcessing } = useValuePay({
91
+ publicKey: "KP_your_public_key",
92
+ amount: 10000,
93
+ customer: { email: "john@example.com", fullName: "John Doe" },
94
+ onSuccess: (response) => {
95
+ console.log("Paid!", response);
96
+ // response.ref, response.status, response.amount, etc.
97
+ },
98
+ onCancelled: (response) => console.log("Cancelled", response),
99
+ onClose: (response) => console.log("Closed", response),
100
+ });
101
+
102
+ return (
103
+ <button onClick={() => initialize()} disabled={!isReady || isProcessing}>
104
+ {isProcessing ? "Processing..." : "Pay ₦10,000"}
105
+ </button>
106
+ );
107
+ }
108
+ ```
109
+
110
+ ### Option 3: Provider with Ref (Hidden Component)
111
+
112
+ ```tsx
113
+ import { useRef } from "react";
114
+ import { ValuePayProvider, ValuePayProviderRef } from "@valuepay/react";
115
+
116
+ function Checkout() {
117
+ const payRef = useRef<ValuePayProviderRef>(null);
118
+
119
+ const handlePay = () => {
120
+ payRef.current?.initialize();
121
+ };
122
+
123
+ return (
124
+ <>
125
+ <ValuePayProvider
126
+ ref={payRef}
127
+ config={{
128
+ publicKey: "KP_your_public_key",
129
+ amount: 7500,
130
+ customer: { email: "alice@example.com", fullName: "Alice Smith" },
131
+ onSuccess: (response) => console.log("Success!", response),
132
+ onCancelled: (response) => console.log("Cancelled", response),
133
+ }}
134
+ />
135
+ <button onClick={handlePay}>Checkout</button>
136
+ </>
137
+ );
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Redirect Mode
144
+
145
+ Instead of callbacks, you can redirect the user after payment:
146
+
147
+ ```tsx
148
+ <ValuePayButton
149
+ publicKey="KP_your_public_key"
150
+ amount={5000}
151
+ customer={{ email: "jane@example.com", fullName: "Jane Doe" }}
152
+ redirectUrl="https://yoursite.com/payment/verify"
153
+ />
154
+ ```
155
+
156
+ After payment, the user is redirected to:
157
+
158
+ ```
159
+ https://yoursite.com/payment/verify?ref=VPS_TX_abc123&status=SUCCESS
160
+ ```
161
+
162
+ Your server should then verify the transaction using the `ref` query parameter via the [ValuePay Verify API](https://developer.valuepayng.com/docs).
163
+
164
+ ---
165
+
166
+ ## API Reference
167
+
168
+ ### `useValuePay(config: ValuePayConfig): UseValuePayReturn`
169
+
170
+ | Return | Type | Description |
171
+ | -------------- | ----------------------- | -------------------------------------------- |
172
+ | `initialize` | `(overrides?) => void` | Opens the ValuePay checkout modal |
173
+ | `isReady` | `boolean` | `true` when the ValuePay script has loaded |
174
+ | `isProcessing` | `boolean` | `true` while a payment is in progress |
175
+
176
+ ### `<ValuePayButton />` Props
177
+
178
+ Extends `ValuePayConfig` with:
179
+
180
+ | Prop | Type | Default | Description |
181
+ | ----------- | ---------------- | ---------------------- | -------------------- |
182
+ | `text` | `string` | `"Pay ₦{amount} Now"` | Button text |
183
+ | `className` | `string` | — | CSS class |
184
+ | `style` | `CSSProperties` | — | Inline styles |
185
+ | `disabled` | `boolean` | `false` | Disable button |
186
+ | `children` | `ReactNode` | — | Custom button content|
187
+
188
+ ### `<ValuePayProvider />` Props
189
+
190
+ | Prop | Type | Description |
191
+ | -------- | ------------------------- | -------------------------------- |
192
+ | `config` | `ValuePayConfig` | Payment configuration |
193
+ | `ref` | `Ref<ValuePayProviderRef>`| Exposes `initialize()` method |
194
+
195
+ ### `ValuePayConfig`
196
+
197
+ | Prop | Type | Required | Default | Description |
198
+ | ---------------- | ---------------------------- | -------- | ------------------------------------ | ---------------------------------- |
199
+ | `publicKey` | `string` | Yes | — | Your ValuePay public key |
200
+ | `amount` | `number` | Yes | — | Amount in Naira |
201
+ | `customer` | `{ email, fullName, phone? }`| Yes | — | Customer details |
202
+ | `currency` | `string` | — | `"NGN"` | Currency code |
203
+ | `channels` | `PaymentChannel[]` | — | `["card","transfer","qrcode","ussd"]`| Enabled channels |
204
+ | `transactionRef` | `string` | — | Auto-generated | Unique transaction reference |
205
+ | `redirectUrl` | `string` | — | — | Redirect URL after payment |
206
+ | `metaData` | `Record<string, unknown>` | — | — | Custom metadata |
207
+ | `customization` | `{ title?, description?, logoUrl? }` | — | — | Checkout modal branding |
208
+ | `onSuccess` | `(response) => void` | — | — | Called on successful payment |
209
+ | `onClose` | `(response) => void` | — | — | Called when modal is closed |
210
+ | `onCancelled` | `(response) => void` | — | — | Called when payment is cancelled |
211
+ | `onCallback` | `(response) => void` | — | — | Called on any payment event |
212
+ | `onError` | `(error) => void` | — | — | Called on SDK errors |
213
+ | `scriptUrl` | `string` | — | Production CDN | Override ValuePay script URL |
214
+
215
+ ### `ValuePayResponse`
216
+
217
+ | Field | Type | Description |
218
+ | ---------------- | ------------------ | --------------------------------------------------------------------------------------------- |
219
+ | `ref` | `string` | Transaction reference |
220
+ | `transactionRef` | `string` | Same as `ref` |
221
+ | `status` | `ValuePayStatus` | `"SUCCESS"`, `"COMPLETED"`, `"FAILED"`, `"CANCELLED"`, `"DUPLICATE"`, `"PENDING"` |
222
+ | `amount` | `number` | Transaction amount |
223
+ | `currency` | `string` | Currency code |
224
+ | `customer` | `ValuePayCustomer` | Customer details |
225
+ | `paymentMethod` | `string?` | Payment method used |
226
+ | `validation` | `{ status }?` | Server-side validation result |
227
+ | `raw` | `unknown?` | Raw ValuePay response |
228
+
229
+ ---
230
+
231
+ ## Dynamic Amounts
232
+
233
+ Override any config at call time:
234
+
235
+ ```tsx
236
+ const { initialize } = useValuePay({
237
+ publicKey: "KP_xxx",
238
+ amount: 0, // placeholder
239
+ customer: { email: "user@example.com", fullName: "User" },
240
+ onSuccess: (res) => console.log(res),
241
+ });
242
+
243
+ // Later, with dynamic amount:
244
+ initialize({ amount: selectedProduct.price });
245
+ ```
246
+
247
+ ---
248
+
249
+ ## Payment Channels
250
+
251
+ | Channel | Value |
252
+ | -------------- | ---------------- |
253
+ | Card | `"card"` |
254
+ | Bank Transfer | `"transfer"` |
255
+ | QR Code | `"qrcode"` |
256
+ | USSD | `"ussd"` |
257
+ | Mobile Money | `"mobile_money"` |
258
+
259
+ ---
260
+
261
+ ## Customizing the Checkout Modal
262
+
263
+ ```tsx
264
+ <ValuePayButton
265
+ publicKey="KP_xxx"
266
+ amount={2000}
267
+ customer={{ email: "user@example.com", fullName: "User" }}
268
+ customization={{
269
+ title: "My Store",
270
+ description: "Thanks for shopping with us!",
271
+ logoUrl: "https://mystore.com/logo.png",
272
+ }}
273
+ onSuccess={(res) => console.log(res)}
274
+ />
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Server-Side Verification
280
+
281
+ **Always verify transactions on your server.** Never trust client-side callbacks alone. Use the `ref` from the response:
282
+
283
+ ```ts
284
+ // Backend (Node.js example)
285
+ const response = await fetch(
286
+ "https://api.valuepayng.com/v1/transactions/verify",
287
+ {
288
+ method: "POST",
289
+ headers: {
290
+ "Content-Type": "application/json",
291
+ Authorization: `Bearer ${VALUEPAY_SECRET_KEY}`,
292
+ },
293
+ body: JSON.stringify({ tx_ref: transactionRef }),
294
+ },
295
+ );
296
+ const result = await response.json();
297
+ ```
298
+
299
+ ---
300
+
301
+ ## TypeScript
302
+
303
+ All types are exported:
304
+
305
+ ```ts
306
+ import type {
307
+ ValuePayConfig,
308
+ ValuePayResponse,
309
+ ValuePayCustomer,
310
+ ValuePayCustomization,
311
+ PaymentChannel,
312
+ ValuePayStatus,
313
+ UseValuePayReturn,
314
+ ValuePayButtonProps,
315
+ ValuePayProviderProps,
316
+ ValuePayProviderRef,
317
+ } from "@valuepay/react";
318
+ ```
319
+
320
+ ---
321
+
322
+ ## Browser Support
323
+
324
+ | Browser | Minimum Version | Release Year |
325
+ | -------------------- | --------------- | ------------ |
326
+ | Chrome | 49+ | 2016 |
327
+ | Firefox | 52+ | 2017 |
328
+ | Safari | 10+ | 2016 |
329
+ | Edge | 15+ | 2017 |
330
+ | Opera | 36+ | 2016 |
331
+ | Samsung Internet | 5.0+ | 2016 |
332
+ | iOS Safari | 10+ | 2016 |
333
+ | Android WebView | 49+ | 2016 |
334
+ | Internet Explorer | 11 (with polyfills) | 2013 |
335
+
336
+ The SDK targets **ES2018** and uses only features available in browsers from 2013 onwards (with appropriate polyfills for IE11). React 16.8+ is required for hooks support.
337
+
338
+ ### SSR Compatibility
339
+
340
+ The SDK is SSR-safe and works with:
341
+ - Next.js (Pages Router and App Router)
342
+ - Remix
343
+ - Gatsby
344
+ - Any server-side rendering framework
345
+
346
+ All DOM access is guarded with `typeof document !== "undefined"` and `typeof window !== "undefined"` checks.
347
+
348
+ ---
349
+
350
+ ## Security
351
+
352
+ - **Never** expose your secret key in client-side code. Only use your **public key** with this SDK.
353
+ - **Always** verify transactions server-side before fulfilling orders — client-side callbacks can be spoofed.
354
+ - **Use HTTPS** in production to prevent man-in-the-middle attacks.
355
+ - **Generate unique transaction references** per payment to prevent duplicate charges.
356
+ - The SDK loads the ValuePay checkout script from `https://www.valuepayng.com` — ensure this domain is allowed in your Content Security Policy (CSP).
357
+ - The SDK does **not** store, log, or transmit any sensitive payment data (card numbers, CVV, etc.). All payment processing happens within the ValuePay checkout modal.
358
+ - Transaction references are generated using cryptographically sufficient randomness via `Math.random()` with alphanumeric characters.
359
+
360
+ ### Content Security Policy (CSP)
361
+
362
+ If your application uses a Content Security Policy, add the following directives:
363
+
364
+ ```
365
+ script-src 'self' https://www.valuepayng.com;
366
+ frame-src 'self' https://www.valuepayng.com;
367
+ ```
368
+
369
+ ---
370
+
371
+ ## Error Handling
372
+
373
+ The SDK provides an `onError` callback for handling SDK-level errors:
374
+
375
+ ```tsx
376
+ <ValuePayButton
377
+ publicKey="KP_xxx"
378
+ amount={5000}
379
+ customer={{ email: "user@example.com", fullName: "User" }}
380
+ onError={(error) => {
381
+ console.error("Payment SDK error:", error.message);
382
+ // Handle error — e.g., show a toast notification
383
+ }}
384
+ onSuccess={(res) => console.log(res)}
385
+ />
386
+ ```
387
+
388
+ Common error scenarios:
389
+ - ValuePay script failed to load (network issue, blocked by CSP)
390
+ - `window.ValuepayCheckout` is not available
391
+ - Invalid configuration
392
+
393
+ ---
394
+
395
+ ## Contributing
396
+
397
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
398
+
399
+ ### Development Setup
400
+
401
+ ```bash
402
+ # Clone the repository
403
+ git clone https://github.com/valuepayment/react-sdk.git
404
+ cd react-sdk
405
+
406
+ # Install dependencies
407
+ npm install
408
+
409
+ # Run tests
410
+ npm test
411
+
412
+ # Run tests in watch mode
413
+ npm run test:watch
414
+
415
+ # Lint
416
+ npm run lint
417
+
418
+ # Type check
419
+ npm run typecheck
420
+
421
+ # Build
422
+ npm run build
423
+ ```
424
+
425
+ ---
426
+
427
+ ## Changelog
428
+
429
+ See [CHANGELOG.md](CHANGELOG.md) for a list of changes in each release.
430
+
431
+ ---
432
+
433
+ ## License
434
+
435
+ MIT (c) [Value Payment Solutions Limited](https://valuepayng.com)
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var useValuePay = require('../hooks/useValuePay.js');
5
+
6
+ const ValuePayButton = ({ text, className, style, disabled, children, ...config }) => {
7
+ const { initialize, isReady, isProcessing } = useValuePay.useValuePay(config);
8
+ const defaultText = `Pay \u20A6${config.amount.toLocaleString()} Now`;
9
+ const buttonText = children || text || defaultText;
10
+ return (jsxRuntime.jsx("button", { type: "button", onClick: () => initialize(), disabled: disabled || !isReady || isProcessing, className: className, style: style, "aria-busy": isProcessing, "aria-label": typeof buttonText === "string" ? buttonText : "Pay Now", children: isProcessing ? "Processing..." : buttonText }));
11
+ };
12
+
13
+ exports.ValuePayButton = ValuePayButton;
14
+ //# sourceMappingURL=ValuePayButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ValuePayButton.js","sources":["../../../../src/components/ValuePayButton.tsx"],"sourcesContent":["import React from \"react\";\nimport { useValuePay } from \"../hooks/useValuePay\";\nimport type { ValuePayButtonProps } from \"../types\";\n\nexport const ValuePayButton: React.FC<ValuePayButtonProps> = ({\n text,\n className,\n style,\n disabled,\n children,\n ...config\n}) => {\n const { initialize, isReady, isProcessing } = useValuePay(config);\n\n const defaultText = `Pay \\u20A6${config.amount.toLocaleString()} Now`;\n const buttonText = children || text || defaultText;\n\n return (\n <button\n type=\"button\"\n onClick={() => initialize()}\n disabled={disabled || !isReady || isProcessing}\n className={className}\n style={style}\n aria-busy={isProcessing}\n aria-label={typeof buttonText === \"string\" ? buttonText : \"Pay Now\"}\n >\n {isProcessing ? \"Processing...\" : buttonText}\n </button>\n );\n};\n"],"names":["useValuePay","_jsx"],"mappings":";;;;;MAIa,cAAc,GAAkC,CAAC,EAC5D,IAAI,EACJ,SAAS,EACT,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,GAAG,MAAM,EACV,KAAI;AACH,IAAA,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,GAAGA,uBAAW,CAAC,MAAM,CAAC;IAEjE,MAAM,WAAW,GAAG,CAAA,UAAA,EAAa,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAA,IAAA,CAAM;AACrE,IAAA,MAAM,UAAU,GAAG,QAAQ,IAAI,IAAI,IAAI,WAAW;IAElD,QACEC,cAAA,CAAA,QAAA,EAAA,EACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,UAAU,EAAE,EAC3B,QAAQ,EAAE,QAAQ,IAAI,CAAC,OAAO,IAAI,YAAY,EAC9C,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK,EAAA,WAAA,EACD,YAAY,EAAA,YAAA,EACX,OAAO,UAAU,KAAK,QAAQ,GAAG,UAAU,GAAG,SAAS,EAAA,QAAA,EAElE,YAAY,GAAG,eAAe,GAAG,UAAU,EAAA,CACrC;AAEb;;;;"}
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var useValuePay = require('../hooks/useValuePay.js');
5
+
6
+ const ValuePayProvider = react.forwardRef(({ config }, ref) => {
7
+ const { initialize } = useValuePay.useValuePay(config);
8
+ react.useImperativeHandle(ref, () => ({
9
+ initialize: (overrides) => initialize(overrides),
10
+ }), [initialize]);
11
+ // Renders nothing — purely a data/logic container
12
+ return null;
13
+ });
14
+ ValuePayProvider.displayName = "ValuePayProvider";
15
+
16
+ exports.ValuePayProvider = ValuePayProvider;
17
+ //# sourceMappingURL=ValuePayProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ValuePayProvider.js","sources":["../../../../src/components/ValuePayProvider.tsx"],"sourcesContent":["import { useImperativeHandle, forwardRef } from \"react\";\nimport { useValuePay } from \"../hooks/useValuePay\";\nimport type { ValuePayProviderProps, ValuePayProviderRef } from \"../types\";\n\nexport const ValuePayProvider = forwardRef<ValuePayProviderRef, ValuePayProviderProps>(\n ({ config }, ref) => {\n const { initialize } = useValuePay(config);\n\n useImperativeHandle(\n ref,\n () => ({\n initialize: (overrides) => initialize(overrides),\n }),\n [initialize],\n );\n\n // Renders nothing — purely a data/logic container\n return null;\n },\n);\n\nValuePayProvider.displayName = \"ValuePayProvider\";\n"],"names":["forwardRef","useValuePay","useImperativeHandle"],"mappings":";;;;;AAIO,MAAM,gBAAgB,GAAGA,gBAAU,CACxC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,KAAI;IAClB,MAAM,EAAE,UAAU,EAAE,GAAGC,uBAAW,CAAC,MAAM,CAAC;AAE1C,IAAAC,yBAAmB,CACjB,GAAG,EACH,OAAO;QACL,UAAU,EAAE,CAAC,SAAS,KAAK,UAAU,CAAC,SAAS,CAAC;AACjD,KAAA,CAAC,EACF,CAAC,UAAU,CAAC,CACb;;AAGD,IAAA,OAAO,IAAI;AACb,CAAC;AAGH,gBAAgB,CAAC,WAAW,GAAG,kBAAkB;;;;"}
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ /**
6
+ * Dynamically loads an external script and tracks its loaded state.
7
+ * Deduplicates — won't inject the same script twice.
8
+ */
9
+ const useScript = (src) => {
10
+ const [loaded, setLoaded] = react.useState(false);
11
+ const scriptRef = react.useRef(null);
12
+ react.useEffect(() => {
13
+ if (!src || typeof document === "undefined")
14
+ return;
15
+ // Check if already loaded
16
+ const existing = document.querySelector(`script[src="${src}"]`);
17
+ if (existing) {
18
+ setLoaded(true);
19
+ return;
20
+ }
21
+ const script = document.createElement("script");
22
+ script.src = src;
23
+ script.async = true;
24
+ script.onload = () => setLoaded(true);
25
+ script.onerror = () => {
26
+ console.error(`[@valuepay/react] Failed to load script: ${src}`);
27
+ };
28
+ document.body.appendChild(script);
29
+ scriptRef.current = script;
30
+ return () => {
31
+ if (scriptRef.current && document.body.contains(scriptRef.current)) {
32
+ document.body.removeChild(scriptRef.current);
33
+ }
34
+ };
35
+ }, [src]);
36
+ return loaded;
37
+ };
38
+
39
+ exports.useScript = useScript;
40
+ //# sourceMappingURL=useScript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScript.js","sources":["../../../../src/hooks/useScript.ts"],"sourcesContent":["import { useEffect, useState, useRef } from \"react\";\n\n/**\n * Dynamically loads an external script and tracks its loaded state.\n * Deduplicates — won't inject the same script twice.\n */\nexport const useScript = (src: string): boolean => {\n const [loaded, setLoaded] = useState(false);\n const scriptRef = useRef<HTMLScriptElement | null>(null);\n\n useEffect(() => {\n if (!src || typeof document === \"undefined\") return;\n\n // Check if already loaded\n const existing = document.querySelector<HTMLScriptElement>(`script[src=\"${src}\"]`);\n if (existing) {\n setLoaded(true);\n return;\n }\n\n const script = document.createElement(\"script\");\n script.src = src;\n script.async = true;\n script.onload = () => setLoaded(true);\n script.onerror = () => {\n console.error(`[@valuepay/react] Failed to load script: ${src}`);\n };\n\n document.body.appendChild(script);\n scriptRef.current = script;\n\n return () => {\n if (scriptRef.current && document.body.contains(scriptRef.current)) {\n document.body.removeChild(scriptRef.current);\n }\n };\n }, [src]);\n\n return loaded;\n};\n"],"names":["useState","useRef","useEffect"],"mappings":";;;;AAEA;;;AAGG;AACI,MAAM,SAAS,GAAG,CAAC,GAAW,KAAa;IAChD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAGA,cAAQ,CAAC,KAAK,CAAC;AAC3C,IAAA,MAAM,SAAS,GAAGC,YAAM,CAA2B,IAAI,CAAC;IAExDC,eAAS,CAAC,MAAK;AACb,QAAA,IAAI,CAAC,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW;YAAE;;QAG7C,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAoB,CAAA,YAAA,EAAe,GAAG,CAAA,EAAA,CAAI,CAAC;QAClF,IAAI,QAAQ,EAAE;YACZ,SAAS,CAAC,IAAI,CAAC;YACf;QACF;QAEA,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC;AAC/C,QAAA,MAAM,CAAC,GAAG,GAAG,GAAG;AAChB,QAAA,MAAM,CAAC,KAAK,GAAG,IAAI;QACnB,MAAM,CAAC,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC;AACrC,QAAA,MAAM,CAAC,OAAO,GAAG,MAAK;AACpB,YAAA,OAAO,CAAC,KAAK,CAAC,4CAA4C,GAAG,CAAA,CAAE,CAAC;AAClE,QAAA,CAAC;AAED,QAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;AACjC,QAAA,SAAS,CAAC,OAAO,GAAG,MAAM;AAE1B,QAAA,OAAO,MAAK;AACV,YAAA,IAAI,SAAS,CAAC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE;gBAClE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC;YAC9C;AACF,QAAA,CAAC;AACH,IAAA,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;AAET,IAAA,OAAO,MAAM;AACf;;;;"}
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var useScript = require('./useScript.js');
5
+ var helpers = require('../utils/helpers.js');
6
+ var constants = require('../utils/constants.js');
7
+
8
+ const useValuePay = (config) => {
9
+ const scriptUrl = config.scriptUrl || constants.DEFAULT_SCRIPT_URL;
10
+ const isReady = useScript.useScript(scriptUrl);
11
+ const [isProcessing, setIsProcessing] = react.useState(false);
12
+ const configRef = react.useRef(config);
13
+ configRef.current = config;
14
+ const initialize = react.useCallback((overrides) => {
15
+ var _a;
16
+ const cfg = configRef.current;
17
+ if (typeof window === "undefined" || !window.ValuepayCheckout) {
18
+ (_a = cfg.onError) === null || _a === void 0 ? void 0 : _a.call(cfg, new Error("ValuePay script not loaded. Ensure the script URL is accessible."));
19
+ return;
20
+ }
21
+ if (isProcessing)
22
+ return;
23
+ setIsProcessing(true);
24
+ const normalized = helpers.normalizeConfig(cfg, overrides);
25
+ const ref = normalized.transactionRef;
26
+ window.ValuepayCheckout({
27
+ ...normalized,
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ oncallback: (response) => {
30
+ var _a, _b;
31
+ const res = helpers.normalizeResponse(response, ref, cfg);
32
+ setIsProcessing(false);
33
+ if (cfg.redirectUrl) {
34
+ const url = new URL(cfg.redirectUrl);
35
+ url.searchParams.set("ref", res.ref);
36
+ url.searchParams.set("status", res.status);
37
+ window.location.href = url.toString();
38
+ return;
39
+ }
40
+ (_a = cfg.onCallback) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
41
+ if (res.status === "SUCCESS" || res.status === "COMPLETED") {
42
+ (_b = cfg.onSuccess) === null || _b === void 0 ? void 0 : _b.call(cfg, res);
43
+ }
44
+ },
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ onCancelled: (response) => {
47
+ var _a;
48
+ const res = helpers.normalizeResponse(response, ref, cfg);
49
+ res.status = "CANCELLED";
50
+ setIsProcessing(false);
51
+ if (cfg.redirectUrl) {
52
+ const url = new URL(cfg.redirectUrl);
53
+ url.searchParams.set("ref", res.ref);
54
+ url.searchParams.set("status", "CANCELLED");
55
+ window.location.href = url.toString();
56
+ return;
57
+ }
58
+ (_a = cfg.onCancelled) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
59
+ },
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ onAborted: (response) => {
62
+ var _a;
63
+ const res = helpers.normalizeResponse(response, ref, cfg);
64
+ res.status = "CANCELLED";
65
+ setIsProcessing(false);
66
+ (_a = cfg.onCancelled) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
67
+ },
68
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
+ onClosed: (response) => {
70
+ var _a;
71
+ const res = helpers.normalizeResponse(response, ref, cfg);
72
+ setIsProcessing(false);
73
+ if (cfg.redirectUrl) {
74
+ const url = new URL(cfg.redirectUrl);
75
+ url.searchParams.set("ref", res.ref);
76
+ url.searchParams.set("status", "CLOSED");
77
+ window.location.href = url.toString();
78
+ return;
79
+ }
80
+ (_a = cfg.onClose) === null || _a === void 0 ? void 0 : _a.call(cfg, res);
81
+ },
82
+ });
83
+ },
84
+ // eslint-disable-next-line react-hooks/exhaustive-deps
85
+ [isReady, isProcessing]);
86
+ return { initialize, isReady, isProcessing };
87
+ };
88
+
89
+ exports.useValuePay = useValuePay;
90
+ //# sourceMappingURL=useValuePay.js.map