
cackpass
A decentralized event ticketing platform with USDC payments on Arc and on-chain proof of purchase.
Demo
About
CACK-pass is a Web2/Web3 hybrid event ticketing platform that lets organizers create events, sell tickets, and settle revenue in USDC on Arc. We originally ran on Lisk with a Paystack fiat rail, but Lisk Mainnet is being deprecated, so we are migrating the whole platform to Arc — an EVM chain where USDC is the native gas token and Circle-native rails are first-class. Buyers can pay two ways: • Fiat via Flutterwave (card, USSD, bank transfer, mobile money) • Native USDC on Arc via Circle App Kit, using a Privy embedded wallet Every crypto payment is written on-chain by our CackPassArcPayment contract, which records the payer, amount, reference, event ID, ticket quantity, and status (Pending → Confirmed → Failed). That gives organizers and attendees a shared, tamper-proof audit trail for every ticket sale. Buyers without crypto can buy real USDC with a card through Privy's Fiat Onramp (NGN / USD / EUR / GBP), delivered to their embedded wallet on Base Mainnet. Tickets are stored on IPFS, verified by signed QR codes, and emailed to the customer after purchase — no login required for guest checkout.
How it's made
CACK-pass is a Next.js 16 (App Router) + TypeScript app deployed on Vercel. Auth and embedded wallets come from Privy — users sign in with email, Google, or Twitter and get a wallet with no seed phrase to manage. Payments — Fiat: We migrated the old Paystack rail to Flutterwave v3. The Flutterwave verify and webhook routes validate `verif-hash` signatures, create Order + Payment + MyTicket records in MongoDB, and trigger a shared ticket email. Card, USSD, bank transfer, and mobile money are all supported. Payments — Crypto (the hackathon core): • Contracts: A new Hardhat project (`arc-contracts/`) holds `CackPassArcPayment.sol`, deployed and verified on Arc Testnet (0x084622e6970BBcBA510454C6145313c2993ED9E4). It exposes initializePayment / confirmPayment / failPayment and emits PaymentInitiated / PaymentConfirmed / PaymentFailed events. • Server side: `lib/arc/client.ts` wraps the contract with ethers v6 — provider, signer, generatePaymentId, paymentIdToBytes32, initializeOnChainPayment, confirmOnChainPayment, and getOnChainPayment (with proper PaymentStatus enum decoding). • Client side: `lib/arc/app-kit.ts` wraps Circle App Kit — sendUSDCWithAppKit(provider, contractAddress, amount) uses the Privy embedded wallet's EIP-1193 provider to move USDC on Arc. Ticket price and gas are both paid in USDC because USDC is the native gas token. • UX: `components/payments/ArcPaymentButton.tsx` orchestrates it — gets the Privy provider, calls /api/payments/arc/initialize, invokes sendUSDCWithAppKit, then redirects to /payment/success?provider=arc&transaction_id=<hash>. Includes double-click guards, wallet-not-ready state, and insufficient-funds messaging. • Verification: /api/payments/arc/verify polls the contract, calls confirmPayment as the platform owner, retries until the status is Confirmed, mints MyTicket records, and sends the ticket email via a shared /api/email/ticket-confirmation endpoint. • Fiat → Crypto onramp: The wallet dashboard uses Privy's useFiatOnramp().fund() to buy real USDC with a card (NGN / USD / EUR / GBP) on Base Mainnet. Base and Arc balances are shown side by side in a dual-network dashboard so buyers can see exactly where their funds live. Partner tech benefits: • Arc — native USDC gas means zero-gas UX for buyers. • Circle App Kit — the fastest way to move USDC into a contract-driven payment flow. • Privy — passwordless auth + embedded wallets + fiat onramp in one SDK. • Flutterwave — reliable African fiat rails (card, USSD, bank, mobile money). Hacky bits worth mentioning: We solved a Mongoose strict-mode gotcha by resolving the buyer email from multiple fallbacks (Order → Payment → metadata.userEmail → User) so ticket emails never get dropped. We also made the Arc verify step idempotent — if a payment is marked completed but no ticket exists, we self-heal and re-process instead of silently failing.