Build a virtual gift card system with a gift_cards collection storing a unique 16-character code, initial and current balance, recipient email, and design template. Users select an amount, choose a design, and pay via Stripe Checkout. A Cloud Function generates a cryptographically random code and emails it to the recipient. Recipients redeem by entering the code at checkout, which deducts from the currentBalance. A balance check page shows remaining value.
Building a Virtual Gift Card System in FlutterFlow
Virtual gift cards let your users purchase stored-value cards as gifts. This tutorial covers the full lifecycle: card design selection, Stripe payment, secure code generation, email delivery to recipients, code redemption at checkout with partial balance support, and balance lookup. It is ideal for e-commerce apps, service platforms, or any business that wants to offer gift cards.
Prerequisites
- A FlutterFlow project on the Pro plan with Cloud Functions
- Stripe account with API keys configured
- Firebase project with Firestore, Authentication, and a transactional email service (SendGrid or Firebase Extensions)
- Basic understanding of Stripe Checkout and Firestore transactions
Step-by-step guide
Create the gift cards schema and design templates
Create the gift cards schema and design templates
Create a gift_cards collection with fields: code (String, unique 16-character alphanumeric), initialBalance (Double), currentBalance (Double), purchasedBy (String, userId), recipientEmail (String), recipientName (String), designTemplate (String, references a design ID), isActivated (Boolean, default true), expiresAt (Timestamp, nullable), and createdAt (Timestamp). Create a gift_card_transactions collection with: cardId (String), type (String: 'purchase', 'redemption'), amount (Double), orderId (String, nullable), and timestamp (Timestamp). Create a card_designs collection with: name (String), imageUrl (String), and category (String: 'Birthday', 'Thank You', 'Holiday').
Expected result: Firestore has gift_cards, gift_card_transactions, and card_designs collections. Gift card codes are unique 16-character strings.
Build the gift card purchase page with design selection and amount
Build the gift card purchase page with design selection and amount
Create a PurchaseGiftCard page. At the top, add a horizontal ListView showing card_designs with preview images. Tapping a design stores the designId in Page State. Below, add preset amount buttons as a Row of Containers ($25, $50, $75, $100) that set Page State selectedAmount. Add a TextField for a custom amount if preset values do not fit. Below, add recipient fields: recipientName TextField, recipientEmail TextField, and an optional personal message TextField. At the bottom, add a 'Purchase Gift Card' Button that validates all fields (amount > 0, valid email) before proceeding.
Expected result: Users select a card design, choose an amount (preset or custom), enter recipient details, and tap Purchase to proceed to payment.
Process payment with Stripe and generate the gift card code
Process payment with Stripe and generate the gift card code
On the Purchase button tap, call a Cloud Function called purchaseGiftCard that receives amount, recipientEmail, recipientName, designTemplate, and purchasedBy. The function creates a Stripe Checkout session with mode: 'payment' and the selected amount. Return the session URL. In FlutterFlow, open the URL via Launch URL. After payment, a Stripe webhook triggers a second Cloud Function that generates a 16-character cryptographically random alphanumeric code using crypto.randomBytes, creates the gift_card document, and sends the code to the recipient via email.
1// Cloud Function: handleGiftCardPayment (Stripe webhook)2const functions = require('firebase-functions');3const admin = require('firebase-admin');4const crypto = require('crypto');5const stripe = require('stripe')(functions.config().stripe.secret_key);6admin.initializeApp();78function generateCode() {9 return crypto.randomBytes(8).toString('hex').toUpperCase().slice(0, 16);10}1112exports.giftCardWebhook = functions.https.onRequest(async (req, res) => {13 const sig = req.headers['stripe-signature'];14 let event;15 try {16 event = stripe.webhooks.constructEvent(17 req.rawBody, sig, functions.config().stripe.gc_webhook_secret18 );19 } catch (err) {20 return res.status(400).send(`Webhook Error: ${err.message}`);21 }2223 if (event.type === 'checkout.session.completed') {24 const session = event.data.object;25 const meta = session.metadata;26 const code = generateCode();27 const amount = session.amount_total / 100;2829 await admin.firestore().collection('gift_cards').add({30 code,31 initialBalance: amount,32 currentBalance: amount,33 purchasedBy: meta.userId,34 recipientEmail: meta.recipientEmail,35 recipientName: meta.recipientName,36 designTemplate: meta.designTemplate,37 isActivated: true,38 expiresAt: null,39 createdAt: admin.firestore.FieldValue.serverTimestamp(),40 });4142 // Send email via your email service43 await admin.firestore().collection('mail').add({44 to: meta.recipientEmail,45 message: {46 subject: `You received a $${amount} gift card!`,47 html: `<p>Hi ${meta.recipientName},</p>48 <p>You have received a $${amount} gift card. Your code is: <strong>${code}</strong></p>49 <p>${meta.personalMessage || ''}</p>`,50 },51 });52 }53 res.status(200).send('OK');54});Expected result: After Stripe payment, a unique gift card code is generated, stored in Firestore, and emailed to the recipient automatically.
Build the gift card redemption flow at checkout
Build the gift card redemption flow at checkout
On your checkout page, add a TextField labeled 'Gift Card Code' with an Apply button. On tap, query gift_cards where code equals the entered value. If not found, show 'Invalid code'. If found but currentBalance is 0, show 'This card has no remaining balance'. If found and has balance, calculate the discount: min(currentBalance, orderTotal). Display the discount amount and the remaining order total. On order completion, call a Cloud Function that atomically deducts the used amount from currentBalance and creates a gift_card_transaction with type 'redemption'. If the gift card covers the full order, skip Stripe payment.
Expected result: Users enter a gift card code at checkout. Valid codes apply a discount up to the remaining balance. Partial balances leave a remainder for future use.
Add a balance check page
Add a balance check page
Create a CheckBalance page with a TextField for the gift card code and a 'Check Balance' Button. On tap, query gift_cards where code matches. If found, display a Container styled as the gift card design showing: the card design image, remaining balance in large text, initial balance, and a transaction history ListView querying gift_card_transactions where cardId matches. If the card is expired (expiresAt < now), show 'This card has expired' with a red badge. If not found, show 'Card not found'.
Expected result: Anyone can enter a gift card code to see the remaining balance, transaction history, and expiry status.
Complete working example
1// Cloud Functions: Gift Card System2const functions = require('firebase-functions');3const admin = require('firebase-admin');4const crypto = require('crypto');5const stripe = require('stripe')(functions.config().stripe.secret_key);6admin.initializeApp();78function generateCode() {9 return crypto.randomBytes(8).toString('hex').toUpperCase().slice(0, 16);10}1112// Create Stripe Checkout for gift card purchase13exports.purchaseGiftCard = functions.https.onCall(async (data, context) => {14 if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required');15 const { amount, recipientEmail, recipientName, designTemplate, personalMessage } = data;16 if (amount < 5 || amount > 500) {17 throw new functions.https.HttpsError('invalid-argument', 'Amount must be $5-$500');18 }19 const session = await stripe.checkout.sessions.create({20 mode: 'payment',21 line_items: [{22 price_data: {23 currency: 'usd',24 product_data: { name: `$${amount} Gift Card` },25 unit_amount: amount * 100,26 },27 quantity: 1,28 }],29 metadata: {30 userId: context.auth.uid, recipientEmail, recipientName,31 designTemplate, personalMessage: personalMessage || '',32 },33 success_url: 'https://yourapp.com/gift-card-success',34 cancel_url: 'https://yourapp.com/gift-cards',35 });36 return { url: session.url };37});3839// Webhook: create card + send email after payment40exports.giftCardWebhook = functions.https.onRequest(async (req, res) => {41 const sig = req.headers['stripe-signature'];42 let event;43 try {44 event = stripe.webhooks.constructEvent(45 req.rawBody, sig, functions.config().stripe.gc_webhook_secret46 );47 } catch (err) { return res.status(400).send(`Error: ${err.message}`); }4849 if (event.type === 'checkout.session.completed') {50 const s = event.data.object;51 const m = s.metadata;52 const code = generateCode();53 const amt = s.amount_total / 100;54 await admin.firestore().collection('gift_cards').add({55 code, initialBalance: amt, currentBalance: amt,56 purchasedBy: m.userId, recipientEmail: m.recipientEmail,57 recipientName: m.recipientName, designTemplate: m.designTemplate,58 isActivated: true, expiresAt: null,59 createdAt: admin.firestore.FieldValue.serverTimestamp(),60 });61 await admin.firestore().collection('mail').add({62 to: m.recipientEmail,63 message: {64 subject: `You received a $${amt} gift card!`,65 html: `<p>Hi ${m.recipientName}, your gift card code is: <strong>${code}</strong></p>`,66 },67 });68 }69 res.status(200).send('OK');70});7172// Redeem gift card at checkout73exports.redeemGiftCard = functions.https.onCall(async (data, context) => {74 if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required');75 const { code, amount } = data;76 const db = admin.firestore();77 const snap = await db.collection('gift_cards').where('code', '==', code).limit(1).get();78 if (snap.empty) throw new functions.https.HttpsError('not-found', 'Invalid code');79 const cardRef = snap.docs[0].ref;8081 return db.runTransaction(async (tx) => {82 const card = await tx.get(cardRef);83 const bal = card.data().currentBalance;84 if (bal <= 0) throw new functions.https.HttpsError('failed-precondition', 'No balance');85 const deduction = Math.min(bal, amount);86 tx.update(cardRef, { currentBalance: admin.firestore.FieldValue.increment(-deduction) });87 tx.create(db.collection('gift_card_transactions').doc(), {88 cardId: card.id, type: 'redemption', amount: deduction,89 timestamp: admin.firestore.FieldValue.serverTimestamp(),90 });91 return { deducted: deduction, remaining: bal - deduction };92 });93});Common mistakes when creating a Virtual Gift Card System in FlutterFlow
Why it's a problem: Generating predictable gift card codes using sequential numbers or timestamps
How to avoid: Generate codes using crypto.randomBytes in the Cloud Function for cryptographically random 16-character alphanumeric strings.
Why it's a problem: Deducting gift card balance on the client side without a transaction
How to avoid: Use a Firestore transaction in a Cloud Function that reads the current balance, validates sufficient funds, and deducts atomically.
Why it's a problem: Creating the gift card document before Stripe payment confirmation
How to avoid: Only create the gift card document inside the Stripe webhook handler after receiving a confirmed checkout.session.completed event.
Best practices
- Generate gift card codes in Cloud Functions using cryptographic randomness, never on the client
- Support partial redemption so a $100 card can be used across multiple orders
- Log every balance change in gift_card_transactions for audit and dispute resolution
- Display the gift card design template visually on the balance check page for a premium feel
- Add expiry dates for regulatory compliance and set up a scheduled Cloud Function to deactivate expired cards
- Allow gift card recipients to add the card to their account for easier redemption at future checkouts
- Validate minimum ($5) and maximum ($500) amounts during purchase to prevent misuse
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to build a virtual gift card system in FlutterFlow. Show me the Firestore schema for gift cards with balances and codes, the Cloud Function for Stripe purchase with secure code generation and email delivery, and the redemption flow with atomic balance deduction.
Create a gift card purchase page where users select a card design, choose an amount, and enter recipient details. Add a checkout integration that creates the gift card after Stripe payment. Build a balance check page where users enter a code to see remaining balance.
Frequently asked questions
Can gift cards be sent via SMS instead of email?
Yes. Replace the email sending logic in the Cloud Function with an SMS service like Twilio. Send the gift card code and amount via text message to the recipient's phone number.
How do I handle refunds for gift card purchases?
Process the Stripe refund via a Cloud Function. On refund webhook, check if the gift card has been partially used. If unused, deactivate it. If partially used, refund only the remaining balance amount.
Can users buy gift cards for themselves?
Yes. Set the recipientEmail to the current user's email. The card is created and emailed to them. They can also view their purchased cards from their account page.
How do I prevent the same gift card code from being used in two simultaneous checkouts?
The Firestore transaction in the redeemGiftCard Cloud Function handles this. If two checkouts try to deduct simultaneously, the transaction serializes them and the second one sees the reduced balance.
Can I add custom gift card designs that users can create?
Yes. Add a Custom Widget that lets users upload a photo or choose from templates. Store the selected or uploaded design URL on the gift card document and use it in the email template.
Can RapidDev help build a complete gift card platform?
Yes. RapidDev can implement gift card management with bulk purchasing, corporate gifting, white-label designs, analytics dashboards, and integration with POS systems for in-store redemption.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation