# How to Create a Virtual Gift Card System in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 25-30 min
- Compatibility: FlutterFlow Pro+ (Cloud Functions required)
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

### 2. 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.

### 3. 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.

```
// Cloud Function: handleGiftCardPayment (Stripe webhook)
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const crypto = require('crypto');
const stripe = require('stripe')(functions.config().stripe.secret_key);
admin.initializeApp();

function generateCode() {
  return crypto.randomBytes(8).toString('hex').toUpperCase().slice(0, 16);
}

exports.giftCardWebhook = functions.https.onRequest(async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.rawBody, sig, functions.config().stripe.gc_webhook_secret
    );
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;
    const meta = session.metadata;
    const code = generateCode();
    const amount = session.amount_total / 100;

    await admin.firestore().collection('gift_cards').add({
      code,
      initialBalance: amount,
      currentBalance: amount,
      purchasedBy: meta.userId,
      recipientEmail: meta.recipientEmail,
      recipientName: meta.recipientName,
      designTemplate: meta.designTemplate,
      isActivated: true,
      expiresAt: null,
      createdAt: admin.firestore.FieldValue.serverTimestamp(),
    });

    // Send email via your email service
    await admin.firestore().collection('mail').add({
      to: meta.recipientEmail,
      message: {
        subject: `You received a $${amount} gift card!`,
        html: `<p>Hi ${meta.recipientName},</p>
               <p>You have received a $${amount} gift card. Your code is: <strong>${code}</strong></p>
               <p>${meta.personalMessage || ''}</p>`,
      },
    });
  }
  res.status(200).send('OK');
});
```

**Expected result:** After Stripe payment, a unique gift card code is generated, stored in Firestore, and emailed to the recipient automatically.

### 4. 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.

### 5. 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 code example

File: `giftCardWebhook.js`

```dart
// Cloud Functions: Gift Card System
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const crypto = require('crypto');
const stripe = require('stripe')(functions.config().stripe.secret_key);
admin.initializeApp();

function generateCode() {
  return crypto.randomBytes(8).toString('hex').toUpperCase().slice(0, 16);
}

// Create Stripe Checkout for gift card purchase
exports.purchaseGiftCard = functions.https.onCall(async (data, context) => {
  if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required');
  const { amount, recipientEmail, recipientName, designTemplate, personalMessage } = data;
  if (amount < 5 || amount > 500) {
    throw new functions.https.HttpsError('invalid-argument', 'Amount must be $5-$500');
  }
  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    line_items: [{
      price_data: {
        currency: 'usd',
        product_data: { name: `$${amount} Gift Card` },
        unit_amount: amount * 100,
      },
      quantity: 1,
    }],
    metadata: {
      userId: context.auth.uid, recipientEmail, recipientName,
      designTemplate, personalMessage: personalMessage || '',
    },
    success_url: 'https://yourapp.com/gift-card-success',
    cancel_url: 'https://yourapp.com/gift-cards',
  });
  return { url: session.url };
});

// Webhook: create card + send email after payment
exports.giftCardWebhook = functions.https.onRequest(async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.rawBody, sig, functions.config().stripe.gc_webhook_secret
    );
  } catch (err) { return res.status(400).send(`Error: ${err.message}`); }

  if (event.type === 'checkout.session.completed') {
    const s = event.data.object;
    const m = s.metadata;
    const code = generateCode();
    const amt = s.amount_total / 100;
    await admin.firestore().collection('gift_cards').add({
      code, initialBalance: amt, currentBalance: amt,
      purchasedBy: m.userId, recipientEmail: m.recipientEmail,
      recipientName: m.recipientName, designTemplate: m.designTemplate,
      isActivated: true, expiresAt: null,
      createdAt: admin.firestore.FieldValue.serverTimestamp(),
    });
    await admin.firestore().collection('mail').add({
      to: m.recipientEmail,
      message: {
        subject: `You received a $${amt} gift card!`,
        html: `<p>Hi ${m.recipientName}, your gift card code is: <strong>${code}</strong></p>`,
      },
    });
  }
  res.status(200).send('OK');
});

// Redeem gift card at checkout
exports.redeemGiftCard = functions.https.onCall(async (data, context) => {
  if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required');
  const { code, amount } = data;
  const db = admin.firestore();
  const snap = await db.collection('gift_cards').where('code', '==', code).limit(1).get();
  if (snap.empty) throw new functions.https.HttpsError('not-found', 'Invalid code');
  const cardRef = snap.docs[0].ref;

  return db.runTransaction(async (tx) => {
    const card = await tx.get(cardRef);
    const bal = card.data().currentBalance;
    if (bal <= 0) throw new functions.https.HttpsError('failed-precondition', 'No balance');
    const deduction = Math.min(bal, amount);
    tx.update(cardRef, { currentBalance: admin.firestore.FieldValue.increment(-deduction) });
    tx.create(db.collection('gift_card_transactions').doc(), {
      cardId: card.id, type: 'redemption', amount: deduction,
      timestamp: admin.firestore.FieldValue.serverTimestamp(),
    });
    return { deducted: deduction, remaining: bal - deduction };
  });
});
```

## Common mistakes

- **Generating predictable gift card codes using sequential numbers or timestamps** — Predictable codes allow attackers to guess valid codes and redeem gift cards they did not purchase. Fix: Generate codes using crypto.randomBytes in the Cloud Function for cryptographically random 16-character alphanumeric strings.
- **Deducting gift card balance on the client side without a transaction** — Two simultaneous redemption attempts can both succeed, deducting more than the remaining balance. Fix: Use a Firestore transaction in a Cloud Function that reads the current balance, validates sufficient funds, and deducts atomically.
- **Creating the gift card document before Stripe payment confirmation** — If payment fails or is cancelled, the gift card already exists with a code that could be discovered and used without payment. Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-virtual-gift-card-system-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-virtual-gift-card-system-in-flutterflow
