skills/paystack-transactions/SKILL.md
Paystack Transactions API — initialize payments, verify transactions, list/fetch transaction history, charge saved authorizations, view timelines, get totals, export data, and perform partial debits. Use this skill whenever building a checkout flow, verifying payment status, recharging a returning customer's saved card, pulling transaction reports or analytics, exporting transaction CSVs, or handling any transaction-related Paystack endpoint. Also use when you see references to /transaction/initialize, /transaction/verify, authorization_url, access_code, or charge_authorization in Paystack integrations.
npx skillsauth add rexedge/paystack paystack-transactionsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The Transactions API lets you create and manage payments on your integration. This covers the entire transaction lifecycle from initialization through verification.
Depends on: paystack-setup for the
paystackRequesthelper and environment config.
Initialize (server) → Complete (client/redirect) → Verify (server) + Webhook
| Method | Endpoint | Description |
| --- | --- | --- |
| POST | /transaction/initialize | Initialize a transaction |
| GET | /transaction/verify/:reference | Verify a transaction |
| GET | /transaction | List transactions |
| GET | /transaction/:id | Fetch a transaction |
| POST | /transaction/charge_authorization | Charge a saved authorization |
| GET | /transaction/timeline/:id_or_reference | View transaction timeline |
| GET | /transaction/totals | Get transaction totals |
| GET | /transaction/export | Export transactions as CSV |
| POST | /transaction/partial_debit | Partial debit on an authorization |
POST /transaction/initialize
Creates a payment and returns an authorization_url (for redirect) and access_code (for Popup/InlineJS).
| Param | Type | Required | Description |
| --- | --- | --- | --- |
| amount | string | Yes | Amount in subunits (kobo). "50000" = NGN 500 |
| email | string | Yes | Customer's email address |
| reference | string | No | Unique ref. Alphanumeric + - . = only |
| currency | string | No | NGN, USD, GHS, ZAR, KES. Defaults to integration currency |
| callback_url | string | No | Override the dashboard callback URL |
| channels | array | No | ["card","bank","ussd","qr","mobile_money","bank_transfer","eft","apple_pay"] |
| plan | string | No | Plan code for subscription-based payment |
| invoice_limit | integer | No | Max charges for subscription |
| metadata | string | No | Stringified JSON with custom_fields array |
| subaccount | string | No | Subaccount code e.g. ACCT_8f4s1eq7ml6rlzj |
| split_code | string | No | Split code e.g. SPL_98WF13Eb3w |
| transaction_charge | integer | No | Flat fee override for split (in subunits) |
| bearer | string | No | "account" or "subaccount" — who pays Paystack fees |
{
"status": true,
"message": "Authorization URL created",
"data": {
"authorization_url": "https://checkout.paystack.com/3ni8kdavz62431k",
"access_code": "3ni8kdavz62431k",
"reference": "re4lyvq3s3"
}
}
"use server";
import { paystackRequest } from "@/lib/paystack";
export async function initializePayment(email: string, amountInNaira: number) {
const result = await paystackRequest<{
authorization_url: string;
access_code: string;
reference: string;
}>("/transaction/initialize", {
method: "POST",
body: JSON.stringify({
email,
amount: Math.round(amountInNaira * 100).toString(),
callback_url: `${process.env.NEXT_PUBLIC_APP_URL}/payment/callback`,
}),
});
return result.data;
}
"use client";
import PaystackInline from "@paystack/inline-js";
function PayButton({ email, amount }: { email: string; amount: number }) {
const handlePay = async () => {
// 1. Initialize on server
const { access_code } = await initializePayment(email, amount);
// 2. Open Popup with access_code
const popup = new PaystackInline();
popup.openIframe({
accessCode: access_code,
onSuccess: (response) => {
// 3. Verify on server — NEVER trust this alone
verifyTransaction(response.reference);
},
onClose: () => console.log("Payment window closed"),
});
};
return <button onClick={handlePay}>Pay ₦{amount}</button>;
}
After initializing, redirect the user to authorization_url. Paystack redirects back to your callback_url with ?trxref=REFERENCE&reference=REFERENCE in the query string. Verify server-side immediately.
// app/payment/callback/page.tsx
import { paystackRequest } from "@/lib/paystack";
export default async function PaymentCallback({
searchParams,
}: {
searchParams: Promise<{ reference?: string }>;
}) {
const { reference } = await searchParams;
if (!reference) return <p>No reference provided</p>;
const result = await paystackRequest<{ status: string; amount: number }>(
`/transaction/verify/${encodeURIComponent(reference)}`
);
if (result.data.status === "success") {
return <p>Payment successful!</p>;
}
return <p>Payment failed: {result.data.status}</p>;
}
GET /transaction/verify/:reference
Always verify server-side after payment. The reference is in the callback URL query params or returned from Popup's onSuccess.
const result = await paystackRequest<{
id: number;
status: "success" | "failed" | "abandoned";
reference: string;
amount: number;
currency: string;
channel: string;
paid_at: string;
authorization: {
authorization_code: string;
reusable: boolean;
card_type: string;
last4: string;
bank: string;
};
customer: { email: string; id: number };
}>(`/transaction/verify/${encodeURIComponent(reference)}`);
Check result.data.status === "success" and validate result.data.amount matches expected amount before fulfilling the order.
GET /transaction
| Param | Type | Description |
| --- | --- | --- |
| perPage | integer | Records per page (default 50) |
| page | integer | Page number (default 1) |
| customer | integer | Filter by customer ID |
| terminalid | string | Filter by Terminal ID |
| status | string | "success", "failed", or "abandoned" |
| from | datetime | Start date e.g. 2024-01-01T00:00:00.000Z |
| to | datetime | End date |
| amount | integer | Filter by amount (in subunits) |
const params = new URLSearchParams({ status: "success", perPage: "20" });
const result = await paystackRequest<Transaction[]>(`/transaction?${params}`);
GET /transaction/:id
Fetch a single transaction by its numeric ID.
const result = await paystackRequest<Transaction>(`/transaction/${transactionId}`);
POST /transaction/charge_authorization
Charge a customer's previously saved card (where authorization.reusable === true).
| Param | Type | Required | Description |
| --- | --- | --- | --- |
| amount | string | Yes | Amount in subunits |
| email | string | Yes | Customer's email |
| authorization_code | string | Yes | e.g. AUTH_72btv547 |
| reference | string | No | Unique transaction reference |
| currency | string | No | Currency code |
| queue | boolean | No | Set true for scheduled/bulk charges |
const result = await paystackRequest<Transaction>(
"/transaction/charge_authorization",
{
method: "POST",
body: JSON.stringify({
authorization_code: "AUTH_72btv547",
email: "[email protected]",
amount: "20000",
}),
}
);
GET /transaction/timeline/:id_or_reference
Returns a step-by-step history of the transaction (attempted payment method, success/failure, time spent).
const result = await paystackRequest<{
start_time: number;
time_spent: number;
attempts: number;
errors: number;
success: boolean;
history: Array<{ type: string; message: string; time: number }>;
}>(`/transaction/timeline/${reference}`);
GET /transaction/totals
Returns aggregate volume and count. Supports from, to, perPage, page query params.
const result = await paystackRequest<{
total_transactions: number;
total_volume: number;
total_volume_by_currency: Array<{ currency: string; amount: number }>;
pending_transfers: number;
}>("/transaction/totals");
GET /transaction/export
Returns a CSV download link. Supports all list filters plus settled, settlement, payment_page.
const params = new URLSearchParams({ from: "2024-01-01", status: "success" });
const result = await paystackRequest<{ path: string }>(
`/transaction/export?${params}`
);
// result.data.path is a time-limited S3 URL to the CSV
POST /transaction/partial_debit
Debit a portion of a customer's available balance on an authorization. Useful for metered billing.
| Param | Type | Required | Description |
| --- | --- | --- | --- |
| authorization_code | string | Yes | Auth code from previous transaction |
| currency | string | Yes | NGN or GHS |
| amount | string | Yes | Amount in subunits |
| email | string | Yes | Customer email tied to the auth code |
| reference | string | No | Unique reference |
| at_least | string | No | Minimum amount to charge if full amount fails |
const result = await paystackRequest<Transaction>(
"/transaction/partial_debit",
{
method: "POST",
body: JSON.stringify({
authorization_code: "AUTH_72btv547",
currency: "NGN",
amount: "20000",
email: "[email protected]",
at_least: "10000",
}),
}
);
string in TypeScript to avoid precision lossresult.data.amount matches your expected amount after verificationauthorization_code from a successful transaction can be saved and reused for future charges if authorization.reusable === truecustom_fields for dashboard display — stringify the JSON before sendingdevelopment
Paystack webhook integration — signature validation with HMAC SHA512, event parsing, IP whitelisting, retry policy, and all supported event types. Use this skill whenever setting up a webhook endpoint for Paystack, validating x-paystack-signature headers, handling charge.success or transfer.success events, debugging webhook delivery failures, implementing idempotent event processing, or building any server-side Paystack event listener. Also use when encountering webhook timeout issues or needing the list of Paystack webhook IP addresses.
tools
Paystack Verification API — KYC verification tools for resolving bank accounts, validating account ownership, and looking up card BIN information. Use this skill whenever verifying bank account details before transfers, confirming account holder names, validating customer identity for compliance, looking up card brand/type/bank from BIN, or implementing KYC flows. Also use when you see references to /bank/resolve, /bank/validate, /decision/bin endpoints, or need to match account numbers to names.
development
Paystack Transfers API — send money to bank accounts and mobile wallets. Initiate single and bulk transfers, finalize OTP-verified transfers, list, fetch, and verify transfer status. Use this skill whenever implementing payouts, disbursements, vendor payments, withdrawal flows, or any feature that sends money from your Paystack balance to recipients. Also use when you see references to transfer_code, TRF_ prefixed codes, the /transfer endpoint, or need to handle transfer OTP verification.
development
Paystack Transfer Recipients API — create, list, fetch, update, and delete transfer recipients (beneficiaries) for payouts. Supports NUBAN (Nigeria), GHIPSS (Ghana), Mobile Money, BASA (South Africa), and authorization-based recipients. Use this skill whenever adding bank accounts or mobile wallets as payout destinations, creating transfer recipients before initiating transfers, managing beneficiary lists, or doing bulk recipient creation. Also use when you see references to recipient_code, RCP_ prefixed codes, or the /transferrecipient endpoint.