The Transaction API allows you to accept payments from customers using various payment channels. This guide covers common transaction scenarios and best practices.
Overview
Transactions represent payment attempts from your customers. The typical flow involves:
- Initializing a transaction to get a payment URL
- Redirecting customers to complete payment
- Verifying the transaction after payment
- Handling the response
Initialize a Transaction
Create a payment session and get an authorization URL for your customer.
Import and initialize the SDK
import { Paystack } from '@efobi/paystack';
const paystack = new Paystack(process.env.PAYSTACK_SECRET_KEY!);
Initialize the transaction
const { data, error } = await paystack.transaction.initialize({
email: 'customer@email.com',
amount: '50000', // Amount in kobo (₦500.00)
currency: 'NGN',
reference: `TXN-${Date.now()}`, // Optional: unique reference
callback_url: 'https://yoursite.com/verify'
});
if (error) {
console.error('Initialization failed:', error);
return;
}
// Redirect customer to authorization_url
console.log('Payment URL:', data.data.authorization_url);
console.log('Access Code:', data.data.access_code);
console.log('Reference:', data.data.reference);
Redirect the customer
// In a web application
window.location.href = data.data.authorization_url;
// Or return the URL to your frontend
return Response.json({
paymentUrl: data.data.authorization_url,
reference: data.data.reference
});
Initialize Parameters
Amount to charge in kobo (multiply by 100 for naira)
Currency code: NGN, USD, GHS, ZAR, KES, or XOF
Unique transaction reference (auto-generated if not provided)
URL to redirect customer after payment
Payment channels to enable: card, bank, ussd, qr, mobile_money, bank_transfer, eft, apple_pay
Custom data to store with the transaction
Split payment configuration code
Verify a Transaction
After payment, verify the transaction status using the reference.
const { data, error } = await paystack.transaction.verify(reference);
if (error) {
console.error('Verification failed:', error);
return;
}
if (data.data.status === 'success') {
console.log('Payment successful!');
console.log('Amount paid:', data.data.amount / 100);
console.log('Customer:', data.data.customer.email);
console.log('Reference:', data.data.reference);
// Grant access to product/service
await fulfillOrder(data.data);
} else {
console.log('Payment status:', data.data.status);
}
Always verify transactions on your server before fulfilling orders. Never rely solely on frontend confirmations or webhooks without verification.
List Transactions
Retrieve a paginated list of transactions on your integration.
const { data, error } = await paystack.transaction.list({
perPage: 50,
page: 1,
status: 'success',
from: '2024-01-01',
to: '2024-12-31'
});
if (data) {
console.log('Total transactions:', data.meta.total);
data.data.forEach(transaction => {
console.log(`${transaction.reference}: ₦${transaction.amount / 100}`);
});
}
List Parameters
Number of transactions per page (max: 100)
Filter by status: success, failed, abandoned
Start date (ISO 8601 format)
End date (ISO 8601 format)
Get Single Transaction
Fetch details of a specific transaction by ID.
const transactionId = 12345678;
const { data, error } = await paystack.transaction.getTransactionById(
transactionId
);
if (data) {
console.log('Transaction details:', {
reference: data.data.reference,
amount: data.data.amount / 100,
status: data.data.status,
channel: data.data.channel,
paidAt: data.data.paidAt
});
}
Charge Authorization
Charge a customer using a previously authorized payment method.
Only use this for authorized recurring charges. Ensure you have the customer’s permission.
const { data, error } = await paystack.transaction.chargeAuthorization({
authorization_code: 'AUTH_xxxxxxxxx',
email: 'customer@email.com',
amount: 50000, // Amount in kobo
currency: 'NGN',
reference: `REC-${Date.now()}`,
metadata: {
subscription_id: 'sub_123',
billing_period: 'monthly'
}
});
if (data && data.data.status === 'success') {
console.log('Charge successful!');
console.log('Transaction ID:', data.data.id);
console.log('Amount charged:', data.data.amount / 100);
} else {
console.log('Charge failed:', data?.data.gateway_response);
}
You can get the authorization_code from successful transactions where authorization.reusable is true.
Transaction Timeline
View the event history of a transaction.
const { data, error } = await paystack.transaction.viewTxnTimeline(
'transaction_reference'
);
if (data) {
console.log('Timeline:', data.data.history);
data.data.history.forEach(event => {
console.log(`${event.type}: ${event.message}`);
});
}
Transaction Totals
Get total volume of transactions on your integration.
const { data, error } = await paystack.transaction.getTxnTotals({
perPage: 1,
page: 1
});
if (data) {
console.log('Total transactions:', data.data.total_transactions);
console.log('Total volume:', data.data.total_volume);
data.data.total_volume_by_currency.forEach(volume => {
console.log(`${volume.currency}: ${volume.amount / 100}`);
});
}
Export Transactions
Export transactions to a CSV file.
const { data, error } = await paystack.transaction.exportTxns({
perPage: 50,
page: 1,
from: '2024-01-01',
to: '2024-12-31'
});
if (data) {
console.log('Export path:', data.data.path);
console.log('Expires at:', data.data.expiresAt);
// Download the CSV file
const response = await fetch(data.data.path);
const csv = await response.text();
}
Partial Debit
Retrieve part of a payment from a customer.
This feature is typically used for split payments where the full amount isn’t needed upfront.
const { data, error } = await paystack.transaction.partialDebit({
authorization_code: 'AUTH_xxxxxxxxx',
email: 'customer@email.com',
amount: 25000, // Partial amount in kobo
currency: 'NGN',
reference: `PART-${Date.now()}`
});
if (data && data.data.status === 'success') {
console.log('Partial debit successful!');
console.log('Amount debited:', data.data.amount / 100);
console.log('Requested amount:', data.data.requested_amount / 100);
}
Error Handling
Handle transaction errors gracefully:
try {
const { data, error } = await paystack.transaction.initialize({
email: 'customer@email.com',
amount: '50000'
});
if (error) {
// Validation error from Zod
console.error('Validation error:', error.flatten());
return { success: false, message: 'Invalid transaction data' };
}
if (!data.status) {
// API error response
console.error('API error:', data.message);
return { success: false, message: data.message };
}
return { success: true, data: data.data };
} catch (err) {
// Network or unexpected error
console.error('Unexpected error:', err);
return { success: false, message: 'Something went wrong' };
}
Best Practices
- Always verify transactions on your server after payment
- Store transaction references in your database for reconciliation
- Use unique references for each transaction to prevent duplicates
- Handle webhooks for real-time payment notifications
- Log all transactions for audit trails and debugging
- Test with test keys before going live
- Implement proper error handling at every step
Common Scenarios
One-time Payment
async function processPayment(customerEmail: string, amount: number) {
// Step 1: Initialize transaction
const { data, error } = await paystack.transaction.initialize({
email: customerEmail,
amount: (amount * 100).toString(), // Convert to kobo
callback_url: `${process.env.APP_URL}/verify`
});
if (error || !data.status) {
throw new Error('Failed to initialize payment');
}
// Step 2: Store reference in database
await db.transactions.create({
reference: data.data.reference,
email: customerEmail,
amount: amount,
status: 'pending'
});
// Step 3: Return payment URL
return data.data.authorization_url;
}
// After customer pays and returns
async function verifyPayment(reference: string) {
const { data, error } = await paystack.transaction.verify(reference);
if (error) {
throw new Error('Verification failed');
}
// Update database
await db.transactions.update(
{ reference },
{
status: data.data.status,
paidAt: data.data.paidAt
}
);
if (data.data.status === 'success') {
// Fulfill order
await fulfillOrder(reference);
}
return data.data;
}
Recurring Charges
async function chargeSubscription(subscription: Subscription) {
const { data, error } = await paystack.transaction.chargeAuthorization({
authorization_code: subscription.authorizationCode,
email: subscription.customerEmail,
amount: subscription.amount * 100,
reference: `SUB-${subscription.id}-${Date.now()}`,
metadata: {
subscription_id: subscription.id,
billing_period: subscription.currentPeriod
}
});
if (error) {
// Handle payment failure
await notifyCustomer(subscription, 'payment_failed');
return false;
}
if (data.data.status === 'success') {
await recordPayment(subscription, data.data);
await extendSubscription(subscription);
return true;
}
return false;
}