GatePass supports multiple African payment gateways including Paystack and Flutterwave for seamless fiat transactions.
Supported Payment Gateways
GatePass integrates with leading African payment processors:
Paystack
Accept payments via cards, bank transfers, and mobile money in Nigeria and Ghana.
Flutterwave
Process payments across 30+ African countries with multiple payment methods.
Configuration
Environment Variables
Set up your payment gateway credentials:
# Paystack Configuration
VITE_PAYSTACK_PUBLIC_KEY=pk_test_your_paystack_public_key
PAYSTACK_SECRET_KEY=sk_test_your_paystack_secret_key
# Flutterwave Configuration
VITE_FLUTTERWAVE_PUBLIC_KEY=FLWPUBK_TEST-your_flutterwave_public_key
FLW_SECRET_KEY=FLWSECK_TEST-your_flutterwave_secret_key
FLW_SECRET_HASH=your_webhook_secret_hash
# Frontend URL for redirects
FRONTEND_URL=http://localhost:5173
Never expose secret keys in your frontend code. Use VITE_ prefix only for public keys.
Payment Flow
The payment process follows these steps:
Initialize Order
Create an order with event details and user information. Returns an order ID and payment reference.
Process Payment
Redirect user to payment gateway hosted page or initialize inline checkout.
Webhook Verification
Payment gateway sends webhook to confirm payment status.
Ticket Minting
Upon successful payment, NFT tickets are minted to user’s wallet (if configured).
Initialize Order
Create a new ticket order before processing payment.
const response = await fetch('https://api.gatepass.app/api/orders/initialize', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
eventId: 'clx123abc',
tierId: 'tier_vip_001',
quantity: 2,
paymentMethod: 'FIAT',
gateway: 'flutterwave', // or 'paystack'
customerEmail: '[email protected]',
customerName: 'John Doe'
})
});
const data = await response.json();
console.log(data.orderId); // Order ID for tracking
console.log(data.checkoutUrl); // Redirect URL (Flutterwave)
console.log(data.reference); // Payment reference (Paystack)
Order Parameters
| Parameter | Type | Required | Description |
|---|
eventId | string | Yes | Event identifier |
tierId | string | Yes | Ticket tier identifier |
quantity | number | Yes | Number of tickets (minimum 1) |
paymentMethod | string | Yes | Payment type: FIAT or CRYPTO |
gateway | string | Yes* | Gateway: paystack, flutterwave, or mpesa |
customerEmail | string | No | Buyer’s email (defaults to authenticated user) |
customerName | string | No | Buyer’s name (defaults to authenticated user) |
Platform Fee: GatePass charges a 2.5% platform fee on top of the ticket price.
Paystack Integration
Initialize Inline Payment
Use Paystack’s inline popup for seamless checkout experience.
<script src="https://js.paystack.co/v2/inline.js"></script>
Verify Payment
Paystack webhook will automatically verify payments, but you can also manually verify:
const verifyPayment = async (reference: string) => {
const response = await fetch(
`https://api.paystack.co/transaction/verify/${reference}`,
{
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`
}
}
);
const data = await response.json();
if (data.data.status === 'success') {
console.log('Payment verified');
// Update order status
}
};
Flutterwave Integration
Hosted Payment Page
Redirect users to Flutterwave’s hosted checkout page.
async function initiateFlutterwavePayment(orderData) {
// Step 1: Initialize order on backend
const response = await fetch('https://api.gatepass.app/api/orders/initialize', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
eventId: orderData.eventId,
tierId: orderData.tierId,
quantity: orderData.quantity,
paymentMethod: 'FIAT',
gateway: 'flutterwave',
customerEmail: orderData.email,
customerName: orderData.name
})
});
const { ok, orderId, checkoutUrl } = await response.json();
if (ok && checkoutUrl) {
// Step 2: Redirect to Flutterwave hosted page
window.location.href = checkoutUrl;
}
}
Verify Transaction
Verify Flutterwave payment after redirect:
// On payment success page
const urlParams = new URLSearchParams(window.location.search);
const transactionId = urlParams.get('transaction_id');
const txRef = urlParams.get('tx_ref');
if (transactionId && txRef) {
const response = await fetch(
`/api/orders/verify-flutterwave?transaction_id=${transactionId}&tx_ref=${txRef}`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
const { ok, status } = await response.json();
if (ok && status === 'COMPLETED') {
console.log('Payment verified successfully');
// Show success message and tickets
}
}
Webhook Integration
Webhooks automatically update order status when payments are confirmed.
Always verify webhook signatures to prevent fraudulent requests.
Paystack Webhook
// From: ~/workspace/source/src/packages/server/src/routes/webhooks.ts:79-102
router.post('/paystack', async (req, res) => {
const secret = process.env.PAYSTACK_SECRET_KEY;
const signature = req.headers['x-paystack-signature'];
// Verify signature
const hash = crypto
.createHmac('sha512', secret)
.update(JSON.stringify(req.body))
.digest('hex');
if (hash !== signature) {
return res.status(401).json({ error: 'Invalid signature' });
}
const body = req.body;
if (body.event === 'charge.success') {
const order = await prisma.order.findFirst({
where: { paystackReference: body.data.reference }
});
if (order) {
// Update order status to COMPLETED
// Mint NFT tickets if wallet connected
await finalizeOrder(order.id, String(body.data.id));
}
}
res.json({ ok: true });
});
Flutterwave Webhook
// From: ~/workspace/source/src/packages/server/src/routes/webhooks.ts:104-123
router.post('/flutterwave', async (req, res) => {
const signature = req.headers['verif-hash'];
const secretHash = process.env.FLW_SECRET_HASH;
// Verify signature
if (signature !== secretHash) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { event, data } = req.body;
if (event === 'charge.completed' && data.status === 'successful') {
const order = await prisma.order.findFirst({
where: { flutterwaveReference: data.tx_ref }
});
if (order) {
// Update order and mint tickets
await finalizeOrder(order.id, String(data.id));
}
}
res.json({ ok: true });
});
Webhook Security
Signature Verification Process
Paystack: Creates HMAC SHA512 hash of request body using secret key.const hash = crypto
.createHmac('sha512', PAYSTACK_SECRET_KEY)
.update(JSON.stringify(req.body))
.digest('hex');
const isValid = hash === req.headers['x-paystack-signature'];
Flutterwave: Compares secret hash from dashboard with header value.const isValid = req.headers['verif-hash'] === FLW_SECRET_HASH;
Order Finalization
When payment is successful, the system automatically:
Update Order Status
Mark order as COMPLETED with payment transaction ID.
Mint NFT Tickets
If user has a connected wallet, mint NFT tickets to their address.
Send Notification
Create in-app notification about successful purchase.
Generate Tickets
Create ticket records with blockchain transaction hash.
// From: ~/workspace/source/src/packages/server/src/routes/webhooks.ts:13-77
async function finalizeOrder(orderId: string, txId: string) {
const order = await prisma.order.findUnique({
where: { id: orderId },
include: { event: true }
});
if (!order || order.paymentStatus === 'COMPLETED') return;
// Update payment status
await prisma.order.update({
where: { id: order.id },
data: {
paymentStatus: 'COMPLETED',
paymentTxId: txId,
updatedAt: new Date()
}
});
// Mint NFT tickets if wallet connected
const user = await prisma.user.findUnique({ where: { id: order.userId } });
const walletAddress = user?.walletAddress;
if (walletAddress && order.event.contractAddress) {
try {
const { txHash, tokenIds } = await mintTicketsFor(
order.event.contractAddress,
abi,
walletAddress,
order.quantity
);
// Save blockchain transaction
await prisma.order.update({
where: { id: order.id },
data: { blockchainTxHash: txHash }
});
// Create ticket records
for (const tokenId of tokenIds) {
await prisma.ticket.create({
data: {
tokenId,
contractAddress: order.event.contractAddress,
chainId: order.event.chainId,
txHash,
eventId: order.eventId,
orderId: order.id
}
});
}
} catch (err) {
console.error('Blockchain minting failed:', err);
}
}
// Send notification
await prisma.notification.create({
data: {
userId: order.userId,
title: 'Ticket Purchase Successful',
message: `You successfully purchased ${order.quantity} ticket(s).`,
type: 'SUCCESS'
}
});
}
Testing
Test Credentials
Public Key: pk_test_xxx
Secret Key: sk_test_xxx
# Test Cards
Card Number: 4084084084084081
CVV: 408
Expiry: 12/28
PIN: 0000
OTP: 123456
Public Key: FLWPUBK_TEST-xxx
Secret Key: FLWSECK_TEST-xxx
# Test Cards
Card Number: 5531886652142950
CVV: 564
Expiry: 09/32
PIN: 3310
OTP: 12345
Error Handling
Common payment errors:
| Error | Description | Solution |
|---|
| Invalid signature | Webhook signature mismatch | Verify secret key configuration |
| Transaction not found | Payment reference invalid | Check order initialization |
| Insufficient funds | Customer has insufficient balance | Ask customer to fund account |
| Network timeout | Payment gateway unreachable | Retry with exponential backoff |
For complete payment integration code, refer to:
~/workspace/source/src/packages/server/src/routes/orders.ts
~/workspace/source/src/packages/server/src/routes/webhooks.ts
~/workspace/source/src/packages/server/src/utils/flutterwave.ts