GatePass supports multiple payment methods including fiat (via Paystack/Flutterwave), cryptocurrency, and mobile money (M-Pesa) to maximize accessibility for global audiences.
Overview
GatePass provides a flexible payment infrastructure that allows event organizers to accept payments through various channels, catering to different audience preferences and geographic locations.
Supported Payment Methods
Fiat Payments Credit/debit cards via Paystack and Flutterwave
Cryptocurrency Direct on-chain payments using MATIC or other tokens
Mobile Money M-Pesa and other mobile payment providers
Payment Architecture
Order Model
All payments flow through the Order model which tracks payment status across different methods:
model Order {
id String @id @default ( cuid ())
// Payment info
totalAmount Float
quantity Int
currency String
paymentMethod String
paymentStatus String @default ( "PENDING" )
// Payment processor data
stripePaymentId String ?
coinbaseChargeId String ?
blockchainTxHash String ?
paystackReference String ?
flutterwaveReference String ?
mpesaCheckoutRequestId String ?
paymentTxId String ?
// Customer info
customerEmail String
customerName String ?
billingAddress String ? // JSON string
createdAt DateTime @default ( now ())
updatedAt DateTime @updatedAt
// Relations
user User @relation ( fields : [ userId ], references : [ id ] )
userId String
event Event @relation ( fields : [ eventId ], references : [ id ] )
eventId String
tickets Ticket []
@@map ( "orders" )
@@index ( [ paymentStatus ] )
}
Payment Gateway Integration
Environment Configuration
Configure payment gateway credentials in your .env file:
# Payment gateways (required for initiating fiat checkouts)
VITE_PAYSTACK_PUBLIC_KEY = pk_test_your_paystack_key
VITE_FLUTTERWAVE_PUBLIC_KEY = FLWPUBK_TEST-your_flutterwave_key
# Blockchain options used by ticketing and wallet UX
VITE_CHAIN_ID = 137
VITE_RPC_URL = https://polygon-rpc.com
# Notes:
# - Paystack and Flutterwave keys are required for fiat payments from the browser.
# - If you plan to use M-Pesa, you need a server-side endpoint; the client will display a message until configured.
Payment Methods
1. Fiat Payments (Paystack & Flutterwave)
Paystack is integrated for card payments, particularly popular in Nigeria and other African countries. Initialize Payment router . post (
'/initialize' ,
authenticate ,
asyncHandler ( async ( req : AuthenticatedRequest , res : any ) => {
const { eventId , tierId , quantity , paymentMethod , gateway , customerEmail , customerName } = req . body
// Calculate total with platform fee
const subtotal = Number ( tier . price ) * quantity
const totalAmount = calcTotal ( subtotal ) // Adds 2.5% platform fee
const reference = `GP- ${ eventId } - ${ Date . now () } - ${ crypto . randomBytes ( 4 ). toString ( 'hex' ) } `
const order = await prisma . order . create ({
data: {
totalAmount ,
quantity ,
currency: 'NGN' ,
paymentMethod ,
paymentStatus: 'PENDING' ,
customerEmail: customerEmail || req . user ?. email || '[email protected] ' ,
customerName: customerName || req . user ?. name || 'Guest' ,
userId: req . user ?. id || 'guest' ,
eventId: eventId ,
paystackReference: reference
}
})
res . json ({ ok: true , orderId: order . id , reference })
})
)
Paystack automatically handles card verification and 3D Secure authentication.
Flutterwave provides broader payment options including cards, bank transfers, and mobile money. Initialize Payment if ( gateway === 'flutterwave' ) {
try {
const flwResponse = await initializeFlutterwavePayment ({
tx_ref: reference ,
amount: totalAmount ,
currency: 'NGN' ,
redirect_url: ` ${ process . env . FRONTEND_URL } /payment-success?orderId= ${ order . id } ` ,
customer: {
email: order . customerEmail ,
name: order . customerName || undefined
},
customizations: {
title: 'GatePass Tickets' ,
description: `Payment for ${ quantity } ticket(s) to ${ event . title } `
}
})
if ( flwResponse . status === 'success' ) {
return res . json ({
ok: true ,
orderId: order . id ,
checkoutUrl: flwResponse . data . link
})
}
} catch ( err : any ) {
logger . error ( 'Flutterwave Initialization Error:' , err . response ?. data || err . message )
throw createError ( 'Failed to initialize payment gateway' , 500 )
}
}
Verify Payment router . get (
'/verify-flutterwave' ,
authenticate ,
asyncHandler ( async ( req : AuthenticatedRequest , res : any ) => {
const { transaction_id , tx_ref } = req . query
if ( ! transaction_id ) throw createError ( 'Transaction ID is required' , 400 )
const flwData = await verifyFlutterwaveTransaction ( transaction_id )
if ( flwData . status === 'success' &&
flwData . data . tx_ref === tx_ref &&
flwData . data . status === 'successful' ) {
const order = await prisma . order . findFirst ({
where: { flutterwaveReference: tx_ref },
include: { event: true }
})
if ( ! order ) throw createError ( 'Order not found' , 404 )
if ( order . paymentStatus !== 'COMPLETED' ) {
await prisma . order . update ({
where: { id: order . id },
data: {
paymentStatus: 'COMPLETED' ,
paymentTxId: String ( transaction_id ),
updatedAt: new Date ()
}
})
// Mint tickets if wallet address exists
const user = await prisma . user . findUnique ({ where: { id: order . userId } })
if ( user ?. walletAddress && order . event . contractAddress ) {
// Blockchain minting logic here
}
await prisma . notification . create ({
data: {
userId: order . userId ,
title: 'Payment Successful' ,
message: `Your payment for ${ order . quantity } ticket(s) to ${ order . event . title } was successful!` ,
type: 'SUCCESS'
}
})
}
return res . json ({ ok: true , status: 'COMPLETED' })
}
res . json ({ ok: false , status: 'FAILED' })
})
)
2. Cryptocurrency Payments
Direct on-chain payments using cryptocurrency wallets:
Connect Wallet
User connects their Web3 wallet (MetaMask, WalletConnect, etc.)
Approve Transaction
User approves the transaction with the exact ticket price in MATIC or configured token
Mint Tickets
Smart contract mints tickets directly to the user’s wallet address
Record Transaction
Backend records the blockchain transaction hash for verification
// Crypto payment flow
const mintTickets = async ( eventContract : string , quantity : number , price : string ) => {
const contract = new ethers . Contract ( eventContract , EventTicketABI , signer )
const tx = await contract . mint ( quantity , {
value: ethers . parseEther ( price )
})
await tx . wait ()
// Update order with transaction hash
await prisma . order . update ({
where: { id: orderId },
data: {
paymentStatus: 'COMPLETED' ,
blockchainTxHash: tx . hash
}
})
}
Cryptocurrency payments provide instant settlement and eliminate chargeback risk.
3. Mobile Money (M-Pesa)
M-Pesa integration for mobile-first markets in Africa:
// M-Pesa payment initialization (server-side)
const initializeMpesaPayment = async ({
phoneNumber ,
amount ,
accountReference ,
transactionDesc
} : MpesaPaymentParams ) => {
const timestamp = new Date (). toISOString (). replace ( / [ ^ 0-9 ] / g , '' ). slice ( 0 , - 3 )
const password = Buffer . from (
` ${ MPESA_SHORTCODE }${ MPESA_PASSKEY }${ timestamp } `
). toString ( 'base64' )
const response = await axios . post (
'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest' ,
{
BusinessShortCode: MPESA_SHORTCODE ,
Password: password ,
Timestamp: timestamp ,
TransactionType: 'CustomerPayBillOnline' ,
Amount: amount ,
PartyA: phoneNumber ,
PartyB: MPESA_SHORTCODE ,
PhoneNumber: phoneNumber ,
CallBackURL: ` ${ process . env . API_URL } /webhooks/mpesa` ,
AccountReference: accountReference ,
TransactionDesc: transactionDesc
},
{
headers: {
Authorization: `Bearer ${ await getMpesaAccessToken () } `
}
}
)
return response . data
}
GatePass applies a 2.5% platform fee on all ticket sales:
function calcTotal ( amount : number ) {
const platformFee = amount * 0.025
return Number (( amount + platformFee ). toFixed ( 2 ))
}
Platform fees are automatically distributed during organizer withdrawals:
function withdraw () external onlyOwner nonReentrant {
uint256 balance = address ( this ).balance;
// Calculate platform fee (250 basis points = 2.5%)
uint256 platformFee = (balance * platformFeeBps) / 10000 ;
uint256 organizerAmount = balance - platformFee;
// Transfer platform fee
if (platformFee > 0 ) {
payable (platformFeeRecipient). transfer (platformFee);
}
// Transfer remaining to organizer
payable ( owner ()). transfer (organizerAmount);
}
Payment Status Flow
Currency Support
GatePass supports multiple currencies based on payment method and location:
NGN Nigerian Naira for Paystack/Flutterwave
USD US Dollar for international cards
MATIC Polygon native token for crypto payments
KES Kenyan Shilling for M-Pesa
ETH Ethereum for cross-chain payments
USDC Stablecoin for crypto payments
Webhook Handling
Handle payment confirmations from payment gateways:
router . post ( '/paystack' , asyncHandler ( async ( req , res ) => {
const event = req . body
// Verify webhook signature
const hash = crypto
. createHmac ( 'sha512' , process . env . PAYSTACK_SECRET_KEY ! )
. update ( JSON . stringify ( req . body ))
. digest ( 'hex' )
if ( hash !== req . headers [ 'x-paystack-signature' ]) {
return res . status ( 400 ). send ( 'Invalid signature' )
}
if ( event . event === 'charge.success' ) {
const reference = event . data . reference
const order = await prisma . order . findFirst ({
where: { paystackReference: reference }
})
if ( order && order . paymentStatus === 'PENDING' ) {
await prisma . order . update ({
where: { id: order . id },
data: {
paymentStatus: 'COMPLETED' ,
paymentTxId: event . data . id
}
})
// Mint tickets
await mintTicketsFor ( order )
}
}
res . status ( 200 ). send ( 'OK' )
}))
Best Practices
Never trust client-side payment confirmations. Always verify transactions server-side using webhook callbacks or direct API queries.
Ensure payment webhooks are idempotent to prevent duplicate ticket minting if the same webhook is received multiple times.
Set payment expiration times (e.g., 15 minutes) to free up reserved tickets if payment is not completed.
Provide Clear Status Updates
Keep users informed about payment status through notifications and real-time UI updates.
Implement a clear refund policy and process for handling payment disputes and event cancellations.
Security Considerations
Never expose secret keys or API credentials in client-side code. All payment processing should occur server-side.
Webhook Verification Always verify webhook signatures to ensure requests are from legitimate payment gateways
PCI Compliance Use payment gateways’ hosted checkout pages to avoid PCI compliance requirements
Secure Storage Never store full credit card numbers. Only store payment gateway references
Amount Validation Always validate payment amounts server-side to prevent price manipulation
Testing
Test Credentials
Paystack
Flutterwave
Crypto
# Test Public Key
pk_test_xxxxxxxxxxxxxxxxxxxxxxxx
# Test Secret Key
sk_test_xxxxxxxxxxxxxxxxxxxxxxxx
# Test Card
Card Number: 4084 0840 8408 4081
CVV: 408
Expiry: Any future date
PIN: 0000
OTP: 123456
# Test Public Key
FLWPUBK_TEST-xxxxxxxxxxxxxxxxxxxxxxxx
# Test Secret Key
FLWSECK_TEST-xxxxxxxxxxxxxxxxxxxxxxxx
# Test Card
Card Number: 5531 8866 5214 2950
CVV: 564
Expiry: 09/32
PIN: 3310
OTP: 12345
# Use Polygon Mumbai testnet
VITE_CHAIN_ID = 80001
VITE_RPC_URL = https://rpc-mumbai.maticvigil.com
# Get test MATIC from faucet
https://faucet.polygon.technology/
Next Steps
Event Management Learn how to create and configure events with different payment options
NFT Tickets Understand how tickets are minted after successful payment