Skip to main content
RapidDev - Software Development Agency
bubble-tutorial

How to handle transactions in Bubble

Manage financial transactions in Bubble by creating a transaction data structure, recording payment events linked to orders and users, displaying transaction history in a filterable table, and implementing refund workflows. This tutorial covers the full lifecycle of transaction data from creation through reconciliation and refunds.

What you'll learn

  • How to design a Transaction Data Type with proper fields for payment tracking
  • How to record transactions automatically when payments succeed
  • How to display transaction history with filters and search
  • How to implement a refund workflow that updates transaction status
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner7 min read20-25 minAll Bubble plansMarch 2026RapidDev Engineering Team
TL;DR

Manage financial transactions in Bubble by creating a transaction data structure, recording payment events linked to orders and users, displaying transaction history in a filterable table, and implementing refund workflows. This tutorial covers the full lifecycle of transaction data from creation through reconciliation and refunds.

Overview: Handling Transactions in Bubble

This tutorial teaches you how to build a transaction management system in Bubble. You will create a data structure for recording payments, link transactions to users and orders, display a searchable transaction history, and handle refunds. This is essential for any app that processes payments and needs an audit trail.

Prerequisites

  • A Bubble account with a new or existing app
  • A payment integration set up such as the Stripe plugin or API Connector
  • An Order or Product Data Type already in your database
  • Basic familiarity with Bubble workflows

Step-by-step guide

1

Create the Transaction Data Type

Go to the Data tab and create a new Data Type called Transaction. Add these fields: Amount (number), Currency (text, default USD), Status (text), Payment Method (text), Stripe Payment ID (text), User (User), Order (your Order Data Type), Refund Amount (number), and Notes (text). This structure gives you a complete record of every payment event in your app.

Pro tip: Use an Option Set for Status (Succeeded, Pending, Refunded, Failed) and Currency to ensure consistency across all transactions.

Expected result: A Transaction Data Type with all listed fields appears in the Data tab.

2

Record transactions automatically after successful payment

In the workflow where you process a payment such as after a Stripe Checkout session completes, add a Create a new thing action for Transaction. Set Amount to the payment amount, Status to Succeeded, Payment Method to the card type returned by Stripe, Stripe Payment ID to the Stripe charge ID, User to Current User, and Order to the order being paid. If you use Stripe webhooks via a Backend Workflow, add the Create Transaction action there to capture payments even if the user closes the browser.

Expected result: A new Transaction record is created in the database every time a payment succeeds.

3

Build the transaction history page

Create a new page called transactions. Add a Repeating Group with Type of content set to Transaction and Data source set to Do a Search for Transactions constrained by User equals Current User, sorted by Created Date descending. In each cell, display the transaction date formatted nicely, Amount with currency symbol, Status with conditional coloring (green for Succeeded, red for Failed, yellow for Pending, grey for Refunded), the Order reference, and a truncated Stripe Payment ID. Add pagination at the bottom.

Expected result: A table-like view showing all of the current user's transactions, sorted newest first, with color-coded statuses.

4

Add filters for date range and status

Above the Repeating Group, add a Row container with two Date Pickers for Start Date and End Date, and a Dropdown for Status filter. Modify the Repeating Group's data source search to include constraints: Created Date is greater than or equal to Start Date's value and Created Date is less than or equal to End Date's value, and Status equals Dropdown Status's value. Check Ignore empty constraints on each so unfilled filters are skipped.

Expected result: Users can filter their transaction list by date range and status. Empty filters show all transactions.

5

Implement the refund workflow

In the admin panel or on the transaction detail view, add a Refund button. Create a workflow: When Button Refund is clicked, first trigger the Stripe refund via API Connector using a POST to Stripe's refund endpoint with the charge ID, then Make changes to the Transaction setting Status to Refunded, Refund Amount to the refunded amount, and Notes to a message like Refunded by admin on merged with the current date. Add an Only when condition so you cannot refund an already-refunded transaction.

Pro tip: Always process the Stripe refund first, then update the Bubble database. If the API call fails, the transaction record stays unchanged and accurate.

Expected result: Clicking Refund processes the refund with Stripe and updates the transaction status to Refunded in the database.

6

Create a transaction summary dashboard for admins

On an admin page, add Text elements that display aggregate data. Use expressions like Do a Search for Transactions where Status equals Succeeded, then get each item's Amount summed to show total revenue. Display counts using the count operator for total transactions and filtered counts for refunds. Calculate average transaction value using sum divided by count. Add conditional formatting to highlight when refund rates exceed a threshold like 5 percent.

Expected result: An admin dashboard showing total revenue, transaction count, refund count, and average transaction value.

Complete working example

Workflow summary
1TRANSACTION MANAGEMENT WORKFLOW SUMMARY
2==========================================
3
4DATA TYPE: Transaction
5 - Amount (number)
6 - Currency (text, default: USD)
7 - Status (Option Set: Succeeded, Pending, Refunded, Failed)
8 - Payment Method (text)
9 - Stripe Payment ID (text)
10 - User (User)
11 - Order (Order)
12 - Refund Amount (number)
13 - Notes (text)
14
15WORKFLOW 1: Record Transaction on Payment Success
16 Trigger: Stripe checkout.session.completed webhook
17 Actions:
18 1. Create a new thing Transaction
19 Amount = event data's amount_total / 100
20 Status = Succeeded
21 Stripe Payment ID = event data's payment_intent
22 User = Search Users (email = event customer_email)
23 Order = Search Orders (ID = event metadata order_id)
24
25WORKFLOW 2: Process Refund
26 Trigger: Button Refund is clicked
27 Condition: Transaction's Status is Succeeded
28 Actions:
29 1. API Connector Stripe Create Refund
30 charge = Transaction's Stripe Payment ID
31 2. Make changes to Transaction
32 Status = Refunded
33 Refund Amount = Transaction's Amount
34 Notes = 'Refunded on ' + Current date/time
35
36TRANSACTION HISTORY PAGE:
37 Repeating Group (Type: Transaction)
38 Source: Search Transactions (User = Current User)
39 Sort: Created Date descending
40 Filters: Date range + Status dropdown
41 Ignore empty constraints = checked
42
43ADMIN DASHBOARD:
44 Total Revenue = Search Transactions (Succeeded):Amount:sum
45 Transaction Count = Search Transactions:count
46 Refund Count = Search Transactions (Refunded):count
47 Avg Value = Revenue / Count

Common mistakes when handling transactions in Bubble

Why it's a problem: Creating the transaction record before confirming the payment succeeded

How to avoid: Always wait for payment confirmation via webhook or callback before creating the Transaction record, or create it with Status Pending and update to Succeeded on confirmation

Why it's a problem: Storing amounts as text instead of numbers

How to avoid: Always use number fields for monetary amounts and store amounts in cents to avoid floating-point issues

Why it's a problem: Not storing the external payment ID

How to avoid: Always capture and store the payment provider's transaction identifier in your Transaction record

Best practices

  • Store monetary amounts in the smallest currency unit such as cents to avoid floating-point rounding errors
  • Always record the payment provider's transaction ID for reconciliation and refund processing
  • Use Option Sets for transaction statuses to prevent inconsistent status text
  • Create transactions via Backend Workflows triggered by webhooks so they record even if the user closes the browser
  • Add Privacy Rules so users can only view their own transactions and admins can view all
  • Log every status change in a Notes field for a complete audit trail
  • Display monetary values formatted with two decimal places and the correct currency symbol

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I'm building a transaction management system in Bubble.io. I need to record payments from Stripe, display a transaction history to users with filters, and let admins process refunds. Help me design the data structure and workflows.

Bubble Prompt

Create a transaction history page that shows all payments for the current user. Display amount, date, status with color coding, and order reference. Add date range and status filters above the list.

Frequently asked questions

Should I store amounts in dollars or cents?

Store in cents, the smallest currency unit. This avoids floating-point rounding issues. For example, store 1999 instead of 19.99. Divide by 100 when displaying to users.

How do I handle partial refunds?

Add a Refund Amount field separate from the original Amount. When processing a partial refund, pass the specific amount to the Stripe refund API and store it in Refund Amount. Keep the original Amount unchanged for accurate reporting.

Can I export transaction data for accounting?

Yes. Go to Data tab, then App data, select Transaction, and click the Export button. This downloads a CSV file with all transaction records that you can import into accounting software.

How do I handle failed payments?

Create the Transaction record with Status set to Failed when a payment fails. Store any error messages in the Notes field. This gives you a record of failed attempts for debugging and customer support.

Can RapidDev help with complex payment systems?

Yes. RapidDev specializes in building payment workflows including subscription billing, marketplace split payments, multi-currency support, and automated reconciliation systems.

How do I prevent duplicate transaction records?

Use the Stripe Payment ID as a unique identifier. Before creating a new Transaction, search for an existing one with the same Stripe Payment ID. Only create a new record if no match is found.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.