Initializing Payments
This guide covers everything you need to know about initializing payments, verifying transactions, and handling payment responses with Voltax.
Payment Flow Overview
Section titled “Payment Flow Overview”All Voltax payment providers follow the same general flow:
- Initiate Payment: Call
initiatePayment()with payment details - Redirect User: Redirect the customer to the
authorizationUrl - Customer Completes Payment: Customer enters payment details on the provider’s page
- Callback: Provider redirects customer back to your
callbackUrl - Verify Transaction: Call
verifyTransaction()to confirm payment status
Payment DTOs
Section titled “Payment DTOs”Each provider has its own typed payment DTO that extends a base schema. The base fields are shared across all providers:
import { Currency } from "@noelzappy/voltax";
// Base fields (shared by all providers)interface BasePaymentDTO { amount: number; // Amount in major currency units (e.g., 100.50) email: string; // Customer's email address currency: Currency; // Currency code (NGN, GHS, USD, KES, ZAR)
// Optional fields reference?: string; // Your unique transaction reference mobileNumber?: string; // Customer's mobile number (10-15 digits) description?: string; // Transaction description (max 255 chars) callbackUrl?: string; // URL to redirect after payment (Used as webhook for Hubtel) metadata?: Record<string, any>; // Custom data to attach}
// Provider-specific options are added at the top level of each DTO// e.g., PaystackPaymentDTO adds: channels, subaccount, splitCode, etc.// e.g., HubtelPaymentDTO adds: returnUrl (required), cancellationUrl, etc.Required Fields
Section titled “Required Fields”| Field | Type | Description |
|---|---|---|
amount | number | Payment amount in major units (e.g., 100 for 100 NGN) |
email | string | Valid email address of the customer |
currency | Currency | One of: Currency.NGN, Currency.GHS, Currency.USD, Currency.KES, Currency.ZAR |
Optional Fields
Section titled “Optional Fields”| Field | Type | Description |
|---|---|---|
reference | string | Unique identifier for the transaction. Required by some providers |
mobileNumber | string | Customer phone number (10-15 characters) |
description | string | Brief description of the payment (max 255 chars) |
callbackUrl | string | URL to redirect after payment completion |
metadata | Record<string, any> | Custom key-value data attached to the transaction |
Initiate a Payment
Section titled “Initiate a Payment”Basic Example
Section titled “Basic Example”import Voltax, { Currency } from "@noelzappy/voltax";
const paystack = Voltax("paystack", { secretKey: process.env.PAYSTACK_SECRET_KEY!,});
const payment = await paystack.initiatePayment({ amount: 5000, email: "customer@example.com", currency: Currency.NGN, reference: `order-${Date.now()}`, callbackUrl: "https://yoursite.com/payment/callback",});
console.log(payment);// {// status: 'PENDING',// reference: 'order-1234567890',// authorizationUrl: 'https://checkout.paystack.com/xxx',// externalReference: 'order-1234567890',// raw: { ... }// }With Metadata
Section titled “With Metadata”Attach custom data to track orders, users, or any relevant information:
const payment = await paystack.initiatePayment({ amount: 2500, email: "customer@example.com", currency: Currency.NGN, reference: "order-123", metadata: { orderId: "ORD-12345", userId: "USR-67890", productName: "Premium Subscription", quantity: 1, },});With Provider-Specific Options
Section titled “With Provider-Specific Options”Each provider has its own payment DTO with specific options at the top level:
import { Voltax, PaystackChannel, Currency } from '@noelzappy/voltax';
const paystack = Voltax('paystack', {secretKey: process.env.PAYSTACK_SECRET_KEY!,});
const payment = await paystack.initiatePayment({amount: 5000,email: 'customer@example.com',currency: Currency.NGN,// Paystack-specific options at top levelchannels: [PaystackChannel.CARD, PaystackChannel.BANK_TRANSFER],subaccount: 'ACCT_xxxxxxxx',transactionCharge: 100,plan: 'PLN_xxxxxxxx',});import { Voltax, Currency } from '@noelzappy/voltax';
const flutterwave = Voltax('flutterwave', { secretKey: process.env.FLUTTERWAVE_SECRET_KEY!,});
const payment = await flutterwave.initiatePayment({ amount: 5000, email: 'customer@example.com', currency: Currency.NGN, reference: 'order-123', // Required for Flutterwave // Flutterwave-specific options at top level customerName: 'John Doe', pageTitle: 'My Store Checkout', logoUrl: 'https://yoursite.com/logo.png', sessionDuration: 30, // Minutes (1-1440) maxRetryAttempts: 3, // Max retry attempts (1-10) paymentOptions: 'card,banktransfer,ussd',});import { Voltax, Currency } from '@noelzappy/voltax';
const hubtel = Voltax('hubtel', {clientId: process.env.HUBTEL_CLIENT_ID!,clientSecret: process.env.HUBTEL_CLIENT_SECRET!,merchantAccountNumber: process.env.HUBTEL_MERCHANT_ACCOUNT!,});
const payment = await hubtel.initiatePayment({amount: 100,email: 'customer@example.com',currency: Currency.GHS,reference: 'order-123', // Required for HubtelcallbackUrl: 'https://yoursite.com/webhook', // Required for Hubtel// Hubtel-specific options at top levelreturnUrl: 'https://yoursite.com/success', // Required for HubtelcancellationUrl: 'https://yoursite.com/cancelled',});The VoltaxPaymentResponse
Section titled “The VoltaxPaymentResponse”All payment operations return a standardized response:
interface VoltaxPaymentResponse {status: PaymentStatus; // SUCCESS, PENDING, or FAILEDreference: string; // Your transaction referenceauthorizationUrl?: string; // URL to redirect customer (for initialization)externalReference?: string; // Provider's internal referenceraw?: any; // Original provider response}PaymentStatus Enum
Section titled “PaymentStatus Enum”enum PaymentStatus { SUCCESS = "SUCCESS", // Payment completed successfully PENDING = "PENDING", // Payment is processing or awaiting action FAILED = "FAILED", // Payment failed or was cancelled}Verify a Transaction
Section titled “Verify a Transaction”After the customer completes payment, verify the transaction status:
import { PaymentStatus } from "@noelzappy/voltax";
const result = await paystack.verifyTransaction("order-123");
switch (result.status) { case PaymentStatus.SUCCESS: // Payment successful - fulfill the order console.log("Payment completed!"); console.log("Provider reference:", result.externalReference); break;
case PaymentStatus.PENDING: // Payment still processing console.log("Payment is still pending..."); break;
case PaymentStatus.FAILED: // Payment failed console.log("Payment failed"); break;}Get Payment Status
Section titled “Get Payment Status”For a quick status check without full transaction details:
const status = await paystack.getPaymentStatus("order-123");
if (status === PaymentStatus.SUCCESS) { console.log("Order is paid!");}Working with Multiple Providers
Section titled “Working with Multiple Providers”Use VoltaxAdapter to manage multiple providers in the same application:
import { VoltaxAdapter, Currency } from "@noelzappy/voltax";
const voltax = new VoltaxAdapter({ paystack: { secretKey: process.env.PAYSTACK_SECRET_KEY! }, flutterwave: { secretKey: process.env.FLUTTERWAVE_SECRET_KEY! }, hubtel: { clientId: process.env.HUBTEL_CLIENT_ID!, clientSecret: process.env.HUBTEL_CLIENT_SECRET!, merchantAccountNumber: process.env.HUBTEL_MERCHANT_ACCOUNT!, },});
// Use Paystack for Nigerian customersconst ngPayment = await voltax.paystack.initiatePayment({ amount: 5000, email: "customer@ng.example.com", currency: Currency.NGN,});
// Use Hubtel for Ghanaian customersconst ghPayment = await voltax.hubtel.initiatePayment({ amount: 100, email: "customer@gh.example.com", currency: Currency.GHS, reference: "gh-order-123", callbackUrl: "https://yoursite.com/webhook", returnUrl: "https://yoursite.com/success",});Best Practices
Section titled “Best Practices”Example: Robust Payment Flow
Section titled “Example: Robust Payment Flow”import { Voltax, Currency, PaymentStatus, VoltaxValidationError, VoltaxGatewayError,} from "@noelzappy/voltax";import { randomUUID } from "crypto";
async function processPayment(orderDetails: { amount: number; email: string; currency: Currency;}) { const paystack = Voltax("paystack", { secretKey: process.env.PAYSTACK_SECRET_KEY!, });
const reference = `order-${randomUUID()}`;
try { // Step 1: Initiate payment const payment = await paystack.initiatePayment({ ...orderDetails, reference, callbackUrl: `https://yoursite.com/callback?ref=${reference}`, });
// Step 2: Store reference in your database await savePaymentReference(reference, orderDetails);
// Step 3: Return authorization URL to client return { success: true, checkoutUrl: payment.authorizationUrl, reference, }; } catch (error) { if (error instanceof VoltaxValidationError) { return { success: false, error: "Invalid payment details" }; } if (error instanceof VoltaxGatewayError) { return { success: false, error: "Payment provider error" }; } throw error; }}
async function handleCallback(reference: string) { const paystack = Voltax("paystack", { secretKey: process.env.PAYSTACK_SECRET_KEY!, });
// Always verify server-side const result = await paystack.verifyTransaction(reference);
if (result.status === PaymentStatus.SUCCESS) { await fulfillOrder(reference); return { success: true }; }
return { success: false, status: result.status };}Next Steps
Section titled “Next Steps”- Learn about Error Handling for robust payment processing
- Check provider-specific guides for advanced features: