@valuepay/react
v2.4.0
Published
Official React SDK for ValuePay payment gateway
Readme
@valuepay/react
Official React SDK for ValuePay — accept payments in your React app with minimal setup.
Table of Contents
- Installation
- Quick Start
- Redirect Mode
- API Reference
- Dynamic Amounts
- Payment Channels
- Customizing the Checkout Modal
- Server-Side Verification
- TypeScript
- Browser Support
- Security
- Contributing
- License
Installation
npm install @valuepay/react
# or
yarn add @valuepay/react
# or
pnpm add @valuepay/reactRequirements
- React
>=16.8.0(hooks support required) - React DOM
>=16.8.0
Quick Start
Option 1: Drop-in Button (Simplest)
import { ValuePayButton } from "@valuepay/react";
function Checkout() {
return (
<ValuePayButton
publicKey="KP_your_public_key"
amount={5000}
customer={{ email: "[email protected]", fullName: "Jane Doe" }}
onSuccess={(response) => {
console.log("Payment successful!", response.ref, response.status);
}}
onClose={(response) => {
console.log("Checkout closed", response);
}}
onCancelled={(response) => {
console.log("Payment cancelled", response);
}}
/>
);
}The button renders as "Pay ₦5,000 Now" by default. Customize with text, className, style, or pass children.
Option 2: Hook (Full Control)
import { useValuePay } from "@valuepay/react";
function Checkout() {
const { initialize, isReady, isProcessing } = useValuePay({
publicKey: "KP_your_public_key",
amount: 10000,
customer: { email: "[email protected]", fullName: "John Doe" },
onSuccess: (response) => {
console.log("Paid!", response);
// response.ref, response.status, response.amount, etc.
},
onCancelled: (response) => console.log("Cancelled", response),
onClose: (response) => console.log("Closed", response),
});
return (
<button onClick={() => initialize()} disabled={!isReady || isProcessing}>
{isProcessing ? "Processing..." : "Pay ₦10,000"}
</button>
);
}Option 3: Provider with Ref (Hidden Component)
import { useRef } from "react";
import { ValuePayProvider, ValuePayProviderRef } from "@valuepay/react";
function Checkout() {
const payRef = useRef<ValuePayProviderRef>(null);
const handlePay = () => {
payRef.current?.initialize();
};
return (
<>
<ValuePayProvider
ref={payRef}
config={{
publicKey: "KP_your_public_key",
amount: 7500,
customer: { email: "[email protected]", fullName: "Alice Smith" },
onSuccess: (response) => console.log("Success!", response),
onCancelled: (response) => console.log("Cancelled", response),
}}
/>
<button onClick={handlePay}>Checkout</button>
</>
);
}Redirect Mode
Instead of callbacks, you can redirect the user after payment:
<ValuePayButton
publicKey="KP_your_public_key"
amount={5000}
customer={{ email: "[email protected]", fullName: "Jane Doe" }}
redirectUrl="https://yoursite.com/payment/verify"
/>After payment, the user is redirected to:
https://yoursite.com/payment/verify?ref=VPS_TX_abc123&status=SUCCESSYour server should then verify the transaction using the ref query parameter via the ValuePay Verify API.
Important — Callback Precedence: If you provide any callback (
onSuccess,onCallback,onCancelled,onClose, oronError) alongsideredirectUrl, the callbacks will take precedence and the redirect will not fire.redirectUrlis only used as a fallback when no callbacks are defined. This lets you safely set aredirectUrlas a backup while still handling events in-app when callbacks are present.
onSuccess vs onCallback
These two callbacks serve different purposes — use one or the other, not both:
| Callback | Fires when | Use case |
|---|---|---|
| onSuccess | Payment succeeds (SUCCESS or COMPLETED) | You only care about successful payments |
| onCallback | Every payment event (success, failure, pending, etc.) | You want to handle all outcomes yourself |
// Option A: Use onSuccess for success-only handling
<ValuePayButton
publicKey="KP_xxx"
amount={5000}
customer={customer}
onSuccess={(res) => console.log("Paid!", res)}
onCancelled={(res) => console.log("Cancelled", res)}
onClose={(res) => console.log("Closed", res)}
/>
// Option B: Use onCallback for all-events handling
<ValuePayButton
publicKey="KP_xxx"
amount={5000}
customer={customer}
onCallback={(res) => {
console.log("Event:", res.status, res);
// Handle SUCCESS, FAILED, PENDING, etc. yourself
}}
onCancelled={(res) => console.log("Cancelled", res)}
onClose={(res) => console.log("Closed", res)}
/>Note:
onCancelled,onClose, andonErrorwork alongside either choice — they handle separate events (user cancellation, modal close, SDK errors).
API Reference
useValuePay(config: ValuePayConfig): UseValuePayReturn
| Return | Type | Description |
| -------------- | ----------------------- | -------------------------------------------- |
| initialize | (overrides?) => void | Opens the ValuePay checkout modal |
| isReady | boolean | true when the ValuePay script has loaded |
| isProcessing | boolean | true while a payment is in progress |
<ValuePayButton /> Props
Extends ValuePayConfig with:
| Prop | Type | Default | Description |
| -------------------- | ---------------- | ---------------------- | --------------------------------------------------------------------------- |
| text | string | "Pay ₦{amount} Now" | Button text |
| className | string | — | CSS class for the button element |
| style | CSSProperties | — | Inline styles for the button (color, font, padding, etc.) |
| containerStyle | CSSProperties | — | Inline styles for the outer container (background, border, radius, size, margin, etc.) |
| containerClassName | string | — | CSS class for the outer container div |
| disabled | boolean | false | Disable button |
| children | ReactNode | — | Custom button content |
Note: All button props are optional. When
containerStyleorcontainerClassNameis provided, the button is wrapped in a<div>with those styles. Otherwise, no wrapper is rendered.
Styling Example
<ValuePayButton
publicKey="KP_xxx"
amount={5000}
customer={{ email: "[email protected]", fullName: "User" }}
containerStyle={{
width: "100%",
maxWidth: "400px",
margin: "20px auto",
borderRadius: "12px",
border: "1px solid #e0e0e0",
backgroundColor: "#f9f9f9",
padding: "16px",
}}
style={{
width: "100%",
color: "#fff",
backgroundColor: "#4CAF50",
fontSize: "16px",
padding: "12px 24px",
borderRadius: "8px",
border: "none",
cursor: "pointer",
}}
onSuccess={(res) => console.log(res)}
/><ValuePayProvider /> Props
| Prop | Type | Description |
| -------- | ------------------------- | -------------------------------- |
| config | ValuePayConfig | Payment configuration |
| ref | Ref<ValuePayProviderRef>| Exposes initialize() method |
ValuePayConfig
| Prop | Type | Required | Default | Description |
| ---------------- | ---------------------------- | -------- | ------------------------------------ | ---------------------------------- |
| publicKey | string | Yes | — | Your ValuePay public key |
| amount | number | Yes | — | Amount in Naira |
| customer | { email, fullName, phone? }| Yes | — | Customer details |
| currency | string | — | "NGN" | Currency code |
| channels | PaymentChannel[] | — | ["card","transfer","qrcode","ussd"]| Enabled channels |
| transactionRef | string | — | Auto-generated | Custom transaction reference. VPS_TX_ prefix auto-prepended if missing |
| redirectUrl | string | — | — | Fallback redirect URL (only used when no callbacks are provided) |
| metaData | Record<string, unknown> | — | — | Custom metadata |
| customization | { title?, description?, logoUrl? } | — | — | Checkout modal branding |
| onSuccess | (response) => void | — | — | Called on successful payment. Use this or onCallback — not both |
| onClose | (response) => void | — | — | Called when modal is closed |
| onCancelled | (response) => void | — | — | Called when payment is cancelled |
| onCallback | (response) => void | — | — | Called on every payment event. Use this or onSuccess — not both |
| onError | (error) => void | — | — | Called on SDK errors |
| scriptUrl | string | — | Production CDN | Override ValuePay script URL |
ValuePayResponse
| Field | Type | Description |
| ---------------- | ------------------ | --------------------------------------------------------------------------------------------- |
| ref | string | Transaction reference |
| transactionRef | string | Same as ref |
| status | ValuePayStatus | "SUCCESS", "COMPLETED", "FAILED", "CANCELLED", "DUPLICATE", "PENDING" |
| amount | number | Transaction amount |
| currency | string | Currency code |
| customer | ValuePayCustomer | Customer details |
| paymentMethod | string? | Payment method used |
| validation | { status }? | Server-side validation result |
| raw | unknown? | Raw ValuePay response |
Dynamic Amounts
Override any config at call time:
const { initialize } = useValuePay({
publicKey: "KP_xxx",
amount: 0, // placeholder
customer: { email: "[email protected]", fullName: "User" },
onSuccess: (res) => console.log(res),
});
// Later, with dynamic amount:
initialize({ amount: selectedProduct.price });Custom Transaction References
By default, a unique reference is auto-generated for every payment. If your business needs to control references (e.g. mapping to internal order IDs), pass your own transactionRef:
<ValuePayButton
publicKey="KP_xxx"
amount={5000}
customer={{ email: "[email protected]", fullName: "User" }}
transactionRef="ORDER_12345"
onSuccess={(res) => console.log(res.ref)} // → "VPS_TX_ORDER_12345"
/>The VPS_TX_ prefix is always prepended automatically. If your reference already includes the prefix, it won't be doubled:
transactionRef="VPS_TX_ORDER_12345" // stays "VPS_TX_ORDER_12345"
transactionRef="ORDER_12345" // becomes "VPS_TX_ORDER_12345"This also works with the hook and provider:
// Hook — set at config time
const { initialize } = useValuePay({
...config,
transactionRef: "INV_9876",
});
// Or override at call time
initialize({ transactionRef: "INV_9876" });Payment Channels
| Channel | Value |
| -------------- | ---------------- |
| Card | "card" |
| Bank Transfer | "transfer" |
| QR Code | "qrcode" |
| USSD | "ussd" |
| Mobile Money | "mobile_money" |
Customizing the Checkout Modal
<ValuePayButton
publicKey="KP_xxx"
amount={2000}
customer={{ email: "[email protected]", fullName: "User" }}
customization={{
title: "My Store",
description: "Thanks for shopping with us!",
logoUrl: "https://mystore.com/logo.png",
}}
onSuccess={(res) => console.log(res)}
/>Server-Side Verification
Always verify transactions on your server. Never trust client-side callbacks alone. Use the ref from the response:
// Backend (Node.js example)
const response = await fetch(
"https://api.valuepayng.com/v1/transactions/verify",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${VALUEPAY_SECRET_KEY}`,
},
body: JSON.stringify({ tx_ref: transactionRef }),
},
);
const result = await response.json();TypeScript
All types are exported:
import type {
ValuePayConfig,
ValuePayResponse,
ValuePayCustomer,
ValuePayCustomization,
PaymentChannel,
ValuePayStatus,
UseValuePayReturn,
ValuePayButtonProps,
ValuePayProviderProps,
ValuePayProviderRef,
} from "@valuepay/react";Browser Support
| Browser | Minimum Version | Release Year | | -------------------- | --------------- | ------------ | | Chrome | 49+ | 2016 | | Firefox | 52+ | 2017 | | Safari | 10+ | 2016 | | Edge | 15+ | 2017 | | Opera | 36+ | 2016 | | Samsung Internet | 5.0+ | 2016 | | iOS Safari | 10+ | 2016 | | Android WebView | 49+ | 2016 | | Internet Explorer | 11 (with polyfills) | 2013 |
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.
SSR Compatibility
The SDK is SSR-safe and works with:
- Next.js (Pages Router and App Router)
- Remix
- Gatsby
- Any server-side rendering framework
All DOM access is guarded with typeof document !== "undefined" and typeof window !== "undefined" checks.
Security
- Never expose your secret key in client-side code. Only use your public key with this SDK.
- Always verify transactions server-side before fulfilling orders — client-side callbacks can be spoofed.
- Use HTTPS in production to prevent man-in-the-middle attacks.
- Generate unique transaction references per payment to prevent duplicate charges.
- The SDK loads the ValuePay checkout script from
https://www.valuepayng.com— ensure this domain is allowed in your Content Security Policy (CSP). - 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.
- Transaction references are generated using cryptographically sufficient randomness via
Math.random()with alphanumeric characters.
Content Security Policy (CSP)
If your application uses a Content Security Policy, add the following directives:
script-src 'self' https://www.valuepayng.com;
frame-src 'self' https://www.valuepayng.com;Error Handling
The SDK provides an onError callback for handling SDK-level errors:
<ValuePayButton
publicKey="KP_xxx"
amount={5000}
customer={{ email: "[email protected]", fullName: "User" }}
onError={(error) => {
console.error("Payment SDK error:", error.message);
// Handle error — e.g., show a toast notification
}}
onSuccess={(res) => console.log(res)}
/>Common error scenarios:
- ValuePay script failed to load (network issue, blocked by CSP)
window.ValuepayCheckoutis not available- Invalid configuration
Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
# Clone the repository
git clone https://github.com/valuepayment/react-sdk.git
cd react-sdk
# Install dependencies
npm install
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Lint
npm run lint
# Type check
npm run typecheck
# Build
npm run buildChangelog
See CHANGELOG.md for a list of changes in each release.
License
MIT (c) Value Payment Solutions Limited
