# How to Handle Credit Card Payments in Bubble

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

## TL;DR

Credit card payments in Bubble use the official Stripe plugin, which provides a card input element and workflow actions for charging cards. You install the plugin, add your Stripe API keys, place the card input on your checkout page, create a charge or checkout session in a workflow, handle success and failure states, and store payment records. This tutorial covers the complete Stripe credit card payment flow.

## Overview: Credit Card Payments in Bubble

This tutorial shows how to accept credit card payments in your Bubble app using the official Stripe plugin.

## Before you start

- A Bubble app with a checkout or payment flow
- A Stripe account (test mode for development)
- API Connector plugin for webhook handling (optional)

## Step-by-step guide

### 1. Install and configure the Stripe plugin

Go to the Plugins tab and search for 'Stripe' by Bubble. Install it. In the plugin settings, enter your Stripe API keys: Publishable Key (pk_test_... for testing, pk_live_... for production) and Secret Key (sk_test_... or sk_live_...). Get these from your Stripe Dashboard under Developers → API keys. Use test keys during development.

**Expected result:** The Stripe plugin is installed with API keys configured for your Stripe account.

### 2. Add the Stripe card input to your checkout page

On your checkout page, add the 'Stripe Token' or 'CardElement' visual element from the Stripe plugin. This renders a secure credit card input (card number, expiry, CVC) handled entirely by Stripe — card details never touch your Bubble servers. Style the input to match your app. Above it, display the order summary and total amount.

**Expected result:** A secure Stripe credit card input appears on your checkout page.

### 3. Create the payment workflow

Add a 'Pay Now' button. Its workflow: Step 1 — Use the Stripe plugin's 'Charge the current user' action. Set the amount in cents (e.g., 2999 for $29.99), the currency (usd), and a description. The plugin automatically uses the card data from the card input element. Step 2 — on success, create a Payment record in your database with: amount, stripe_charge_id, user, status = 'succeeded', date. Step 3 — show a success message and redirect to a confirmation page.

> Pro tip: Always specify amounts in the smallest currency unit — cents for USD, pence for GBP. $29.99 = 2999 cents.

**Expected result:** Clicking Pay Now charges the entered credit card and creates a payment record on success.

### 4. Handle payment failures

Stripe may decline payments for various reasons: insufficient funds, expired card, or fraud detection. The plugin's charge action throws an error on failure. Add error handling: use a second workflow triggered by the Stripe error event, or wrap the charge in a try/catch pattern using custom states. Show the user a clear error message: 'Your card was declined. Please try a different payment method.' Log the failure in your database for monitoring.

**Expected result:** Payment failures show clear error messages to users without crashing the checkout flow.

### 5. Store payment records and set up webhook handling

Create a 'Payment' Data Type with: user, amount, currency, stripe_charge_id, status (succeeded/failed/refunded), description, created_date. Store a record for every payment attempt. For reliable payment tracking, set up a Stripe webhook that sends events to a Bubble backend API endpoint. Subscribe to charge.succeeded and charge.refunded events. This ensures your database stays in sync even if the user's browser disconnects during payment.

**Expected result:** Payment records are stored in the database, and webhooks keep payment status accurate.

## Complete code example

File: `Workflow summary`

```text
CREDIT CARD PAYMENTS — SUMMARY
=================================

STRIPE PLUGIN SETUP:
  Install: Stripe by Bubble
  Publishable Key: pk_test_... (or pk_live_...)
  Secret Key: sk_test_... (or sk_live_...)

CHECKOUT PAGE:
  CardElement: Stripe card input
  Order summary + total display
  Pay Now button

PAYMENT WORKFLOW:
  Step 1: Stripe → Charge current user
    Amount: in cents (2999 = $29.99)
    Currency: 'usd'
    Description: 'Order #123'
  Step 2 (success): Create Payment record
    stripe_charge_id, amount, status = succeeded
  Step 3: Show confirmation, redirect

ERROR HANDLING:
  On Stripe error → show decline message
  Log failed attempt in Payment (status = failed)
  Suggest: try different card

WEBHOOKS (optional but recommended):
  Backend API workflow endpoint
  Subscribe: charge.succeeded, charge.refunded
  Update Payment status from events

TEST CARDS:
  Success: 4242 4242 4242 4242
  Decline: 4000 0000 0000 0002
  Any future exp, any 3-digit CVC
```

## Common mistakes

- **Using live Stripe keys during development** — Real credit cards get charged during testing, costing you and your test users real money Fix: Always use test API keys (pk_test_, sk_test_) during development. Switch to live keys only when deploying to production.
- **Specifying the amount in dollars instead of cents** — Stripe expects amounts in the smallest currency unit — sending 29.99 charges $0.30 instead of $29.99 Fix: Multiply dollar amounts by 100 to convert to cents: $29.99 = 2999
- **Not handling payment failures in the workflow** — An unhandled Stripe error stops the workflow silently, leaving the user on a frozen checkout page Fix: Add error handling that catches Stripe errors and shows a user-friendly decline message

## Best practices

- Use test API keys during development and test cards for all payment testing
- Always specify amounts in cents (smallest currency unit)
- Handle payment errors gracefully with clear user messages
- Store payment records for every attempt (successful and failed)
- Set up Stripe webhooks for reliable payment status tracking
- Never log or display full card numbers — the Stripe plugin handles PCI compliance
- Test with Stripe's test card numbers: 4242 4242 4242 4242 (success), 4000 0000 0000 0002 (decline)

## Frequently asked questions

### Is the Stripe plugin PCI compliant?

Yes. The Stripe plugin uses Stripe Elements which handles card data entirely on Stripe's servers. Card numbers never touch your Bubble app, maintaining PCI compliance.

### Can I accept payments without the Stripe plugin?

Yes. You can use Stripe Checkout (hosted payment page) via the API Connector, which redirects users to Stripe's hosted page and returns them after payment.

### How do I process refunds?

Use the API Connector to call Stripe's refund endpoint with the charge ID. Update the Payment record status to 'refunded'. You can refund full or partial amounts.

### Can I save cards for future payments?

Yes. The Stripe plugin supports creating Stripe Customers and attaching payment methods. This enables one-click purchases and subscription billing.

### Can RapidDev help set up payment processing in my Bubble app?

Yes. RapidDev can implement complete payment systems including card payments, subscriptions, invoicing, refunds, and financial reporting in Bubble.

---

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