Payments · Convex component
convex-flutterwave
Accept payments and subscriptions with Flutterwave in your Convex app. Reactive transactions, subscription state, and webhook ingestion.
npm install convex-flutterwaveDownloads
Download history
Build this
Build with convex-flutterwave
The install and setup steps from the package documentation.
- 1
Install
bashnpm install convex-flutterwaveRequirements: Convex v1.33.1 or later, Node.js 18+, a account
- 2
Quick Start
Five steps to add Flutterwave to your Convex app.
1. Add the component
In
convex/convex.config.ts:tsimport { defineApp } from "convex/server"; import convexFlutterwave from "convex-flutterwave/convex.config"; const app = defineApp(); app.use(convexFlutterwave); export default app;2. Set environment variables
bashnpx convex env set FLW_SECRET_KEY FLWSECK-xxxxxxxxxxxx npx convex env set FLW_WEBHOOK_SECRET_HASH your-chosen-secret-hashFLW_WEBHOOK_SECRET_HASHis a random value you choose and enter in the Flutterwave dashboard — it's separate from your API secret key.3. Mount the webhook handler
In
convex/http.ts:tsimport { httpRouter } from "convex/server"; import { components } from "./_generated/api"; import { Flutterwave } from "convex-flutterwave"; const flutterwave = new Flutterwave(components.convexFlutterwave, { secretKey: process.env.FLW_SECRET_KEY!, webhookSecretHash: process.env.FLW_WEBHOOK_SECRET_HASH!, }); const http = httpRouter(); http.route({ path: "/webhooks/flutterwave", method: "POST", handler: flutterwave.webhookHandler, }); export default http;4. Register the webhook in Flutterwave
- In Flutterwave Dashboard → Settings → Webhooks
- Set the webhook URL:
https://your-deployment.convex.site/webhooks/flutterwave - Set the same secret hash you stored as
FLW_WEBHOOK_SECRET_HASH - Save. Flutterwave sends every event to this URL — the handler ignores events it doesn't recognize.
Your Convex site URL is in the Convex dashboard under Settings → URL & Deploy Key — it ends in
.convex.site.5. Initialize the client
In
convex/payments.ts:tsimport { components } from "./_generated/api"; import { Flutterwave } from "convex-flutterwave"; export const flutterwave = new Flutterwave(components.convexFlutterwave, { secretKey: process.env.FLW_SECRET_KEY!, webhookSecretHash: process.env.FLW_WEBHOOK_SECRET_HASH!, });Import
flutterwavefrom this file in any Convex function that needs payments. - 3
Setup
convex/payments.ts— your central payments module:tsimport { components } from "./_generated/api"; import { Flutterwave } from "convex-flutterwave"; import { action } from "./_generated/server"; import { v } from "convex/values"; export const flutterwave = new Flutterwave(components.convexFlutterwave, { secretKey: process.env.FLW_SECRET_KEY!, webhookSecretHash: process.env.FLW_WEBHOOK_SECRET_HASH!, }); export const checkout = action({ args: { email: v.string(), amount: v.number(), redirectUrl: v.string() }, handler: async (ctx, args) => flutterwave.initializeTransaction(ctx, args), });convex/http.ts— webhook entry point (shown in ). - 4
Usage
Start a checkout
tsexport const checkout = action({ args: { email: v.string(), amount: v.number() }, handler: async (ctx, args) => { return await flutterwave.initializeTransaction(ctx, { email: args.email, amount: args.amount, // major currency unit — e.g. naira, not kobo redirectUrl: "https://yourapp.com/payment/callback", }); }, }); // Returns: { paymentLink, txRef } // Redirect the customer to paymentLink.Verify a transaction
tsexport const confirmPayment = action({ args: { transactionId: v.string() }, handler: async (ctx, args) => { return await flutterwave.verifyTransaction(ctx, args); }, }); // Returns: { status, txRef, flwRef, transactionId, amount, currency, customerEmail, ... }Flutterwave redirects to your
redirectUrlwithstatusandtx_refquery params on every outcome, andtransaction_idonly when a chargeable attempt was made — a card declined at the gateway or an abandoned checkout redirects back with juststatus=failedandtx_ref, notransaction_idto verify. Handle that case on your result screen rather than assumingtransaction_idis always present.Read a transaction reactively
tsexport const getTransaction = query({ args: { txRef: v.string() }, handler: async (ctx, args) => { return await flutterwave.getTransaction(ctx, args); }, }); // Returns: Transaction | nullList a customer's transactions
tsexport const getHistory = query({ args: { customerEmail: v.string() }, handler: async (ctx, args) => { return await flutterwave.listTransactions(ctx, { customerEmail: args.customerEmail, limit: 20, }); }, }); // Returns: Transaction[] ordered newest first