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
Create the Transaction Data Type
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.
Record transactions automatically after successful payment
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.
Build the transaction history page
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.
Add filters for date range and status
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.
Implement the refund workflow
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.
Create a transaction summary dashboard for admins
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
1TRANSACTION MANAGEMENT — WORKFLOW SUMMARY2==========================================34DATA TYPE: Transaction5 - 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)1415WORKFLOW 1: Record Transaction on Payment Success16 Trigger: Stripe checkout.session.completed webhook17 Actions:18 1. Create a new thing → Transaction19 Amount = event data's amount_total / 10020 Status = Succeeded21 Stripe Payment ID = event data's payment_intent22 User = Search Users (email = event customer_email)23 Order = Search Orders (ID = event metadata order_id)2425WORKFLOW 2: Process Refund26 Trigger: Button Refund is clicked27 Condition: Transaction's Status is Succeeded28 Actions:29 1. API Connector → Stripe Create Refund30 charge = Transaction's Stripe Payment ID31 2. Make changes to Transaction32 Status = Refunded33 Refund Amount = Transaction's Amount34 Notes = 'Refunded on ' + Current date/time3536TRANSACTION HISTORY PAGE:37 Repeating Group (Type: Transaction)38 Source: Search Transactions (User = Current User)39 Sort: Created Date descending40 Filters: Date range + Status dropdown41 Ignore empty constraints = checked4243ADMIN DASHBOARD:44 Total Revenue = Search Transactions (Succeeded):Amount:sum45 Transaction Count = Search Transactions:count46 Refund Count = Search Transactions (Refunded):count47 Avg Value = Revenue / CountCommon 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.
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.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation