# How to handle payments in Bubble

- Tool: Bubble
- Difficulty: Beginner
- Time required: 20-30 min
- Compatibility: All Bubble plans (Stripe account required)
- Last updated: March 2026

## TL;DR

Bubble supports payment processing through the official Stripe plugin or the API Connector for other gateways. This tutorial covers setting up Stripe for one-time and recurring payments, configuring checkout flows with the Stripe.js element, and handling payment confirmation workflows so your app can accept real money from day one.

## Overview: Handling Payments in Bubble

This tutorial walks you through integrating payment processing into your Bubble app. We focus on Stripe — the most popular payment gateway for Bubble builders — covering both one-time charges and recurring subscriptions. You will learn how to install the Stripe plugin, configure your keys, build a checkout page, and create workflows that respond to successful or failed payments. This guide is ideal for non-technical founders building SaaS apps, marketplaces, or e-commerce platforms.

## Before you start

- A Bubble account with an app ready for payment integration
- A Stripe account (sign up at stripe.com)
- Your Stripe test API keys (publishable and secret)
- A page with a product or service to charge for
- Basic familiarity with Bubble workflows

## Step-by-step guide

### 1. Install and configure the Stripe plugin

Go to the Plugins tab in your Bubble editor. Click Add plugins and search for Stripe. Install the official Bubble-maintained Stripe plugin (it is free). Once installed, you will see configuration fields. Paste your Stripe Publishable Key into the Publishable key field and your Stripe Secret Key into the Secret key field. Use your test keys (starting with pk_test_ and sk_test_) while building. You can find these in your Stripe Dashboard under Developers → API keys.

> Pro tip: Keep your live keys separate from test keys. Only switch to live keys (pk_live_ and sk_live_) when you are ready to accept real payments.

**Expected result:** The Stripe plugin is installed and both API keys are saved in the plugin settings.

### 2. Add a Stripe Checkout button to your page

Go to the Design tab and open the page where users will make a payment. Click the + icon in the Element Palette and search for Stripe Checkout. Drag the Stripe Checkout element onto your page — this is a special button that triggers the Stripe payment modal. In its Property Editor, set the Currency (e.g., USD), the Amount in cents (e.g., 2999 for $29.99), and an optional Description. You can make these values dynamic by clicking Insert dynamic data and referencing your product's price from the database.

> Pro tip: Stripe amounts are always in the smallest currency unit — cents for USD, pence for GBP. A $50 charge is 5000, not 50.

**Expected result:** A Stripe Checkout button appears on your page with the correct amount and currency configured.

### 3. Create the payment success workflow

Go to the Workflow tab. You will see an event called Stripe Checkout payment completed (or When Stripe Checkout's payment is completed). Click it to create a new workflow. Add actions to handle a successful payment: Create a new Thing of type Order or Payment with fields like amount, user, date, and Stripe charge ID (available as the workflow event's charge ID). Then add a Navigate action or show a success message to the user using an Alert or by navigating to a confirmation page.

**Expected result:** When a test payment succeeds, a new record is created in your database and the user sees a confirmation.

### 4. Handle payment failures gracefully

Still in the Workflow tab, look for the event Stripe Checkout payment failed. Click it and add actions to show the user a clear error message — use the Alert element or set a Custom State to display an error text like Payment failed. Please check your card details and try again. Log the failure by creating a PaymentError record with fields for the user, timestamp, and error message so you can review failed payments later.

> Pro tip: Use Stripe's test card numbers to simulate failures: 4000000000000002 always declines, 4000000000009995 triggers insufficient funds.

**Expected result:** Failed payments show a user-friendly error message and are logged in your database for review.

### 5. Set up recurring subscription payments

For subscriptions, go back to the Plugins tab and open your Stripe plugin settings. Enable the Subscription features. Then in the Stripe Dashboard, go to Products and create a Product with a recurring Price (e.g., $29/month). Copy the Price ID (starts with price_). In your Bubble workflow, use the Stripe action Subscribe the user to a plan and paste the Price ID. Create a Subscription data type in your database to store the user, plan, status, and Stripe subscription ID. Update this record when the subscription event fires.

**Expected result:** Users can subscribe to a recurring plan and their subscription status is stored in your database.

### 6. Test your entire payment flow

Make sure you are using Stripe test keys. Open your app in Preview mode. Click the Stripe Checkout button. In the Stripe modal, use the test card number 4242 4242 4242 4242 with any future expiry date and any CVC. Complete the payment and verify that your success workflow runs — check the database for the new Payment or Order record. Also test a decline using 4000000000000002 and confirm your error handling works. Check your Stripe Dashboard → Payments to see the test transactions.

> Pro tip: When you are ready to go live, switch both Stripe keys to their live versions and deploy your app to Live. For complex payment setups like marketplaces or multi-currency billing, consider reaching out to RapidDev for expert Bubble development support.

**Expected result:** Test payments succeed and fail correctly, records are created in the database, and transactions appear in the Stripe Dashboard.

## Complete code example

File: `Workflow summary`

```text
PAYMENT FLOW WORKFLOW SUMMARY
==============================

PLUGIN SETUP:
  Plugins tab → Add plugins → Stripe
  Publishable key: pk_test_xxxx
  Secret key: sk_test_xxxx

DATA TYPES:
  Payment
    - amount (number)
    - currency (text)
    - user (User)
    - stripe_charge_id (text)
    - status (text: succeeded / failed)
    - created_date (date)

  Subscription
    - user (User)
    - stripe_subscription_id (text)
    - plan_name (text)
    - price_id (text)
    - status (text: active / canceled / past_due)
    - current_period_end (date)

PAGE ELEMENTS:
  - Stripe Checkout element
    Currency: USD
    Amount: dynamic (Current Page Product's price * 100)
    Description: dynamic (Current Page Product's name)

WORKFLOW 1: Payment Success
  Event: Stripe Checkout payment completed
  Action 1: Create a new Payment
    amount = Stripe Checkout's amount / 100
    currency = USD
    user = Current User
    stripe_charge_id = Stripe Checkout's charge ID
    status = succeeded
  Action 2: Navigate to page confirmation
    Send: Result of Step 1

WORKFLOW 2: Payment Failure
  Event: Stripe Checkout payment failed
  Action 1: Show Alert "Payment failed. Please try again."
  Action 2: Create a new Payment
    amount = Stripe Checkout's amount / 100
    user = Current User
    status = failed

WORKFLOW 3: Subscription
  Event: Button Subscribe is clicked
  Action 1: Subscribe the user to a plan
    Price ID: price_xxxx
  Action 2: Create a new Subscription
    user = Current User
    stripe_subscription_id = Result of Step 1's ID
    plan_name = Pro Plan
    status = active

TEST CARDS:
  Success: 4242 4242 4242 4242
  Decline: 4000 0000 0000 0002
  Insufficient: 4000 0000 0000 9995
```

## Common mistakes

- **Using live Stripe keys during development** — Live keys process real charges to real credit cards, which means you could accidentally charge yourself or testers real money Fix: Always use test keys (pk_test_ and sk_test_) during development. Switch to live keys only when deploying to production.
- **Entering the payment amount in dollars instead of cents** — Stripe expects amounts in the smallest currency unit. Entering 50 instead of 5000 charges $0.50 instead of $50.00 Fix: Multiply your dollar amount by 100 before passing it to the Stripe Checkout element. Use a dynamic expression like Product's price * 100.
- **Not storing the Stripe charge or subscription ID in the database** — Without the Stripe ID, you cannot look up, refund, or manage the payment later in the Stripe Dashboard or via API Fix: Always save the charge ID or subscription ID returned by the Stripe event into a field in your Payment or Subscription data type.
- **Forgetting to handle payment failure events** — If a payment fails and there is no failure workflow, the user gets no feedback and may think the payment went through Fix: Create a workflow for the payment failed event that shows a clear error message and logs the failure.

## Best practices

- Always use Stripe test keys during development and switch to live keys only at deployment
- Store every Stripe charge ID and subscription ID in your database for future reference and refunds
- Show clear confirmation messages on payment success and helpful error messages on failure
- Use Stripe's test card numbers to simulate all scenarios: success, decline, insufficient funds, and expired card
- Set up Privacy Rules on your Payment data type so users can only see their own payments
- For subscription apps, create a backend workflow triggered by Stripe webhooks to handle subscription status changes
- Display prices including currency symbol and format them consistently across your app
- Log all payment events in a separate data type for auditing and troubleshooting

## Frequently asked questions

### Which Stripe plugin should I use in Bubble?

Use the official Stripe plugin maintained by Bubble. It is free and supports Checkout, subscriptions, and basic payment flows. For advanced needs like Connect or metered billing, you may need the API Connector.

### Can I accept payments on Bubble's free plan?

You can build and test payment flows on the free plan, but you cannot deploy to a live URL. You need at least the Starter plan to publish a live app that accepts real payments.

### How do I handle refunds in Bubble?

Refunds are processed in the Stripe Dashboard or via the API Connector calling Stripe's Refund endpoint. Store the Stripe charge ID in your database so you can easily find the transaction to refund.

### Can I use PayPal instead of Stripe in Bubble?

Yes. You can integrate PayPal using the API Connector plugin to call PayPal's REST API, or use a community PayPal plugin from the Bubble marketplace. Stripe is more commonly used due to better plugin support.

### How do I test payments without using a real credit card?

Use Stripe test mode with test API keys. Stripe provides test card numbers like 4242 4242 4242 4242 for successful payments and 4000000000000002 for declines. No real money is charged in test mode.

### Is it safe to put Stripe keys in Bubble?

Yes. The publishable key is safe for client-side use. The secret key is stored in the plugin settings and Bubble keeps it server-side — it is never exposed to the browser. Never put your secret key in a visible element or URL parameter.

### Can RapidDev help with complex payment integrations?

Yes. RapidDev specializes in Bubble development and can help with advanced payment flows like marketplace payouts with Stripe Connect, subscription billing with usage metering, and multi-currency support.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/handle-payments-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/handle-payments-in-bubble
