# How to Build a Coupon and Discount Code System in Bubble

- Tool: Bubble
- Difficulty: Beginner
- Time required: 25-30 min
- Compatibility: All Bubble plans
- Last updated: March 2026

## TL;DR

A coupon system in Bubble requires a Coupon Data Type with fields for code, discount type, value, expiry date, and usage limits. You create a validation workflow that checks the entered code against your database, verifies it has not expired or exceeded its limit, then applies the discount to the order total. This tutorial covers the full system from code generation to checkout application.

## Overview: Building a Coupon System in Bubble

This tutorial walks through creating a complete coupon and discount code system for your Bubble e-commerce app. You will build the data structure, create an admin interface for generating codes, implement validation logic for expiry and usage limits, and apply percentage or flat discounts to order totals at checkout.

## Before you start

- A Bubble app with a basic e-commerce or checkout flow
- An Order or Cart Data Type already set up
- Understanding of Bubble workflows and conditional logic
- Familiarity with Data Types and fields in Bubble

## Step-by-step guide

### 1. Create the Coupon Data Type with required fields

Go to the Data tab and click 'New type' to create 'Coupon'. Add fields: 'code' (text) for the unique string; 'discount_type' (text) for 'percentage' or 'flat'; 'discount_value' (number) for the amount; 'expiry_date' (date); 'max_uses' (number); 'current_uses' (number, default 0); 'is_active' (yes/no, default yes); and 'min_order_amount' (number) for minimum order requirements.

> Pro tip: Add a 'created_by' field linked to User so you can track which admin created each coupon.

**Expected result:** A Coupon Data Type exists with all fields for code, discount configuration, usage tracking, and activation status.

### 2. Build an admin page to create and manage coupons

Create a new page called 'admin-coupons'. Add input fields for each Coupon field: text input for code, dropdown for discount type (percentage/flat), number input for value, date picker for expiry, and number inputs for max uses and minimum order. Add a 'Generate Random Code' button that sets the code input to a random string using 'Calculate random string' (length 8, alphanumeric). Add a 'Create Coupon' button with a workflow that creates a new Coupon Thing. Below, add a Repeating Group showing all coupons with a 'Deactivate' button per row.

**Expected result:** An admin page where you can create coupons with custom or random codes, set parameters, and manage existing coupons.

### 3. Add a coupon input field to your checkout page

On your checkout page, add a text input labeled 'Coupon Code' and an 'Apply' button next to it. Add a text element below for validation messages (initially hidden). Create custom states on the page: 'applied_coupon' (type Coupon) and 'discount_amount' (type number). These store the validated coupon and calculated discount for the order total.

**Expected result:** The checkout page has a coupon input, Apply button, message area, and custom states to hold applied coupon data.

### 4. Create the coupon validation workflow

On the Apply button click, create a workflow. Step 1: Do a Search for Coupons where code equals the input value (use :lowercase on both sides), is_active is yes, expiry_date is greater than Current date/time, and current_uses is less than max_uses. Only when search count is greater than 0. Step 2: Set 'applied_coupon' state to the first result. Step 3: Calculate discount — if discount_type is 'percentage', set discount_amount to subtotal times discount_value divided by 100; if 'flat', set to discount_value. Step 4: Show success message. Add a second workflow for when search count equals 0 showing an error.

> Pro tip: Add a check that the order subtotal meets the coupon's min_order_amount with a specific error like 'Minimum order of $50 required'.

**Expected result:** Valid coupons show a success message and store the discount; invalid or expired codes show an error message.

### 5. Apply the discount to the order total display

Update your order total text element's dynamic expression to: Subtotal minus page's discount_amount (or 0 if empty). Add a conditional: 'When applied_coupon is not empty' — show the original price with strikethrough and the discounted total. Add a 'Remove coupon' link that resets both custom states to empty.

**Expected result:** The checkout shows the original price crossed out and the discounted total when a valid coupon is applied.

### 6. Increment coupon usage count on successful order

In your order completion workflow (payment success or order placed), add 'Make changes to a Thing' targeting the applied_coupon. Set current_uses to current_uses plus 1. If current_uses now equals max_uses, also set is_active to no. Store the coupon reference on the Order by adding a 'coupon_used' field (type Coupon) to the Order Data Type.

**Expected result:** Each successful order increments usage count, and the coupon auto-deactivates when it reaches maximum uses.

## Complete code example

File: `Workflow summary`

```text
COUPON SYSTEM — COMPLETE WORKFLOW SUMMARY
==========================================

DATA TYPE: Coupon
  Fields:
    code (text) — unique coupon string
    discount_type (text) — 'percentage' or 'flat'
    discount_value (number)
    expiry_date (date)
    max_uses (number)
    current_uses (number, default 0)
    is_active (yes/no, default yes)
    min_order_amount (number)
    created_by (User)

PAGE CUSTOM STATES:
    applied_coupon (type: Coupon)
    discount_amount (type: number)

WORKFLOW: Apply Coupon Button Click
  Step 1 (Only when search count > 0):
    Search: Coupon where
      code:lowercase = Input value:lowercase
      is_active = yes
      expiry_date > Current date/time
      current_uses < max_uses
  Step 2: Set state applied_coupon = first item
  Step 3: Set state discount_amount =
    percentage: subtotal * discount_value / 100
    flat: discount_value
  Step 4: Show success message

WORKFLOW: Invalid Coupon
  Condition: search count = 0
  Show error: 'Invalid or expired code'

WORKFLOW: Order Completion
  Step 1: Create Order with coupon_used = applied_coupon
  Step 2: Make changes to applied_coupon:
    current_uses = current_uses + 1
    is_active = no (when current_uses = max_uses)

DISPLAY:
  Total = Subtotal - discount_amount
  Show strikethrough original when coupon applied
```

## Common mistakes

- **Not checking the expiry date in the validation search** — Without an expiry check, expired coupons still work and users can share old codes Fix: Add constraint: expiry_date greater than Current date/time
- **Forgetting to increment current_uses after a successful order** — Without tracking usage, single-use codes can be reused indefinitely Fix: Add a 'Make changes to Thing' step in your order completion workflow that increments current_uses
- **Using case-sensitive code comparison** — Users may type 'SAVE20' when the stored code is 'save20', causing valid coupons to be rejected Fix: Use the ':lowercase' operator on both the input value and search constraint
- **Applying the discount after payment instead of before** — The customer is charged the full amount and the discount never actually applies Fix: Calculate the discounted total before initiating payment and pass the discounted amount to the processor

## Best practices

- Use random alphanumeric codes (8+ characters) to prevent guessing
- Always validate coupons server-side in a workflow, not just client-side
- Store the used coupon reference on the Order for audit trail and analytics
- Add minimum order amounts to prevent tiny orders from using high-value coupons
- Create an admin dashboard showing coupon usage statistics
- Use ':lowercase' on both sides of code comparisons for case-insensitive matching
- Set Privacy Rules on the Coupon type so non-admin users cannot view discount values

## Frequently asked questions

### Can I create single-use coupons unique per customer?

Yes. Add a 'used_by' list field (type User) to Coupon. In validation, check the current user is not in used_by. After redemption, add the user to the list.

### How do I create bulk coupon codes for a promotion?

Use a recursive backend workflow that generates one coupon with a random code, then schedules itself until the desired count is reached. Trigger it from your admin page.

### Can I apply multiple coupons to a single order?

You can by changing the custom state to a list of Coupons and stacking discounts. Most apps limit to one coupon per order to simplify logic.

### How do I prevent coupon code abuse?

Combine protections: random 8+ character codes, per-user limits, expiry dates, minimum order amounts, and usage tracking.

### Can RapidDev help build a complete e-commerce system with coupons?

Yes. RapidDev can build your entire e-commerce backend including product catalog, cart, checkout, coupons, payments, and order management in Bubble.

### Should I use an Option Set or Data Type for discount types?

Use an Option Set for discount type choices (percentage, flat). Option Sets are cached and load instantly with zero WU cost, ideal for static dropdown values.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/set-up-coupon-and-discount-code-system-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/set-up-coupon-and-discount-code-system-in-bubble
