interswitch-web-checkout
SKILL.md
Interswitch Web Checkout
Accept payments on your website using Interswitch's Web Checkout — either via the Inline Checkout JavaScript widget or the Web Redirect HTML form.
Checkout Methods
| Method | How It Works | Best For |
|---|---|---|
| Inline Checkout | JavaScript popup overlay on your page | Modern SPAs, no page redirect |
| Web Redirect | HTML form POST redirects to Interswitch | Traditional server-rendered apps |
Payment Parameters
| Parameter | Description | Required |
|---|---|---|
merchant_code |
Your merchant code (e.g., MX6072) |
Yes |
pay_item_id |
Payment item identifier (e.g., 9405967) |
Yes |
txn_ref |
Unique transaction reference (you generate) | Yes |
amount |
Amount in minor currency (kobo) — 10000 = ₦100 |
Yes |
currency |
Currency code: 566 (NGN) |
Yes |
cust_id |
Customer email or ID | Yes |
cust_name |
Customer name | No |
pay_item_name |
Item description | No |
site_redirect_url |
URL to redirect after payment | Web Redirect only |
Test vs Live URLs
| Environment | URL |
|---|---|
| Test | https://newwebpay.qa.interswitchng.com |
| Live | https://newwebpay.interswitchng.com |
Inline Checkout (Recommended)
Include the Script
<!-- Test -->
<script src="https://newwebpay.qa.interswitchng.com/inline-checkout.js"></script>
<!-- Live -->
<script src="https://newwebpay.interswitchng.com/inline-checkout.js"></script>
Initialize Payment
<button id="pay-btn">Pay ₦100</button>
<script>
document.getElementById('pay-btn').addEventListener('click', function () {
const request = {
merchant_code: 'MX6072',
pay_item_id: '9405967',
txn_ref: 'TXN_' + Date.now(),
amount: 10000, // ₦100 in kobo
currency: 566, // NGN
cust_id: 'customer@email.com',
onComplete: function (response) {
console.log('Payment response:', response);
// ALWAYS verify server-side before fulfilling order
verifyTransaction(response.txnref);
},
mode: 'TEST' // Remove for production
};
window.webpayCheckout(request);
});
</script>
React/Next.js Integration
'use client';
import { useEffect, useRef } from 'react';
declare global {
interface Window {
webpayCheckout: (request: InterswitchCheckoutRequest) => void;
}
}
interface InterswitchCheckoutRequest {
merchant_code: string;
pay_item_id: string;
txn_ref: string;
amount: number;
currency: number;
cust_id: string;
cust_name?: string;
pay_item_name?: string;
onComplete: (response: InterswitchPaymentResponse) => void;
mode?: 'TEST' | 'LIVE';
}
interface InterswitchPaymentResponse {
txnref: string;
payRef: string;
retRef: string;
cardNum: string;
apprAmt: number;
resp: string; // '00' = success
desc: string;
}
function useInterswitchCheckout(isTest = true) {
const loaded = useRef(false);
useEffect(() => {
if (loaded.current) return;
const script = document.createElement('script');
script.src = isTest
? 'https://newwebpay.qa.interswitchng.com/inline-checkout.js'
: 'https://newwebpay.interswitchng.com/inline-checkout.js';
script.async = true;
document.body.appendChild(script);
loaded.current = true;
}, [isTest]);
return (request: InterswitchCheckoutRequest) => {
if (window.webpayCheckout) {
window.webpayCheckout(request);
}
};
}
export default function CheckoutButton() {
const checkout = useInterswitchCheckout(true);
const handlePay = () => {
checkout({
merchant_code: 'MX6072',
pay_item_id: '9405967',
txn_ref: `TXN_${Date.now()}`,
amount: 10000,
currency: 566,
cust_id: 'customer@email.com',
onComplete: async (response) => {
if (response.resp === '00') {
// Verify server-side
const result = await fetch('/api/verify-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ txnRef: response.txnref }),
});
const data = await result.json();
if (data.verified) {
// Fulfill order
}
}
},
mode: 'TEST',
});
};
return <button onClick={handlePay}>Pay ₦100</button>;
}
Web Redirect
HTML Form POST
<!-- Test URL -->
<form method="POST" action="https://newwebpay.qa.interswitchng.com/collections/w/pay">
<input type="hidden" name="merchant_code" value="MX6072" />
<input type="hidden" name="pay_item_id" value="9405967" />
<input type="hidden" name="txn_ref" value="TXN_1234567890" />
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="currency" value="566" />
<input type="hidden" name="cust_id" value="customer@email.com" />
<input type="hidden" name="site_redirect_url" value="https://yoursite.com/payment/callback" />
<button type="submit">Pay ₦100</button>
</form>
Handle Redirect Callback
After payment, Interswitch redirects to your site_redirect_url with query params:
// GET /payment/callback?txnref=TXN_123&payRef=FBN|WEB|...&retRef=000123&resp=00&apprAmt=10000
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const txnRef = request.nextUrl.searchParams.get('txnref');
const resp = request.nextUrl.searchParams.get('resp');
if (!txnRef) {
return NextResponse.redirect('/payment/failed');
}
// ALWAYS requery — never trust redirect params alone
const verified = await requeryTransaction(txnRef);
if (verified.ResponseCode === '00') {
// Fulfill order
return NextResponse.redirect('/payment/success');
}
return NextResponse.redirect('/payment/failed');
}
Server-Side Transaction Requery (CRITICAL)
NEVER rely solely on client-side callbacks. Always verify payment status server-side:
interface RequeryResponse {
Amount: number;
ResponseCode: string;
ResponseDescription: string;
MerchantReference: string;
PaymentReference: string;
RetrievalReferenceNumber: string;
TransactionDate: string;
}
async function requeryTransaction(txnRef: string): Promise<RequeryResponse> {
const config = getConfig();
const headers = await getAuthHeaders();
const response = await fetch(
`${config.collectionsBaseUrl}/collections/api/v1/gettransaction.json` +
`?merchantcode=${config.merchantCode}&transactionreference=${txnRef}&amount=0`,
{ headers }
);
if (!response.ok) {
throw new Error(`Requery failed: ${response.status}`);
}
return response.json();
}
Response Codes
| Code | Description |
|---|---|
00 |
Approved / Successful |
Z0 |
Transaction pending |
Z6 |
Pending (OTP validation) |
Z8 |
Pending (bank redirect) |
Z9 |
Pending (3D Secure) |
51 |
Insufficient funds |
54 |
Expired card |
55 |
Incorrect PIN |
91 |
Issuer/switch inoperative |
Security Best Practices
- Always requery server-side — Client callbacks can be spoofed
- Generate unique txn_ref — Use UUID or timestamp-based references
- Validate amount — Compare requery amount with expected amount
- Use HTTPS — All redirect URLs must be HTTPS in production
- Store transaction state — Log all payment attempts in your database
Weekly Installs
1
Repository
rexedge/interswitchFirst Seen
3 days ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1