# How to integrate PayPal in Bubble

- Tool: Bubble
- Difficulty: Beginner
- Time required: 25-30 min
- Compatibility: All Bubble plans (live payments require Starter+)
- Last updated: March 2026

## TL;DR

Integrate PayPal payments into your Bubble app using the API Connector to call the PayPal Orders API. This tutorial walks through creating a PayPal developer app, configuring the API Connector with OAuth2 authentication, creating and capturing payment orders, and handling payment confirmation via webhooks — all configured visually in Bubble.

## Overview: Integrating PayPal in Bubble via API Connector

PayPal is one of the most widely used payment processors, and integrating it into your Bubble app gives customers an alternative to credit card payments. This tutorial uses the API Connector approach rather than a third-party plugin, giving you direct control over the PayPal Orders API. You will create a PayPal developer app, configure authentication, build the order creation and capture flow, and set up webhooks for payment confirmation.

## Before you start

- A Bubble account with an app
- A PayPal developer account (developer.paypal.com)
- Basic understanding of Bubble workflows and the API Connector plugin
- A page with a product or service that needs a payment button

## Step-by-step guide

### 1. Create a PayPal developer app and get credentials

Go to developer.paypal.com and log in. Navigate to Dashboard → My Apps & Credentials. Make sure you are in the Sandbox tab (not Live). Click 'Create App.' Name it (e.g., 'My Bubble App') and select Merchant as the type. After creation, you will see your Client ID and Secret. Copy both — you will need them in Bubble. The sandbox environment provides test buyer and seller accounts for testing payments without real money. Switch to the Live tab only when you are ready for production.

> Pro tip: PayPal sandbox provides default test accounts. Go to Dashboard → Sandbox → Accounts to see them and their credentials for testing.

**Expected result:** A PayPal app with a Client ID and Secret ready for use in Bubble's API Connector.

### 2. Configure the API Connector with PayPal authentication

In Bubble, go to the Plugins tab → API Connector → Add another API. Name it 'PayPal.' Set Authentication to 'None / Self-handled' — you will handle OAuth2 manually for more control. Create the first API call named 'Get Access Token.' Set method to POST, URL to https://api-m.sandbox.paypal.com/v1/oauth2/token. Add a Header: Content-Type = application/x-www-form-urlencoded. Add another Header: Authorization = Basic [base64 of ClientID:Secret]. Add a Body parameter: grant_type = client_credentials. Mark the Authorization header as Private. Set 'Use as' to Action. Click Initialize to get the access token response.

```
POST https://api-m.sandbox.paypal.com/v1/oauth2/token

Headers:
  Content-Type: application/x-www-form-urlencoded
  Authorization: Basic {base64(ClientID:Secret)}

Body:
  grant_type=client_credentials

Response:
{
  "scope": "...",
  "access_token": "A21AAF...",
  "token_type": "Bearer",
  "app_id": "APP-80W...",
  "expires_in": 32400
}
```

**Expected result:** The API Connector returns a PayPal access token that you will use for subsequent API calls.

### 3. Create a PayPal order via API call

Add a second API call in the PayPal API group named 'Create Order.' Set method to POST, URL to https://api-m.sandbox.paypal.com/v2/checkout/orders. Add Headers: Content-Type = application/json, Authorization = Bearer [access_token] (make this a dynamic parameter). For the Body, add a JSON payload with the order details including the amount and currency. Set 'Use as' to Action. Initialize with sample data. The response includes an order ID and an approval URL — the approval URL is where you redirect the customer to approve the payment on PayPal.

```
POST https://api-m.sandbox.paypal.com/v2/checkout/orders

Headers:
  Content-Type: application/json
  Authorization: Bearer <access_token>

Body:
{
  "intent": "CAPTURE",
  "purchase_units": [
    {
      "amount": {
        "currency_code": "USD",
        "value": "29.99"
      },
      "description": "Product purchase"
    }
  ],
  "application_context": {
    "return_url": "https://yourapp.bubbleapps.io/payment-success",
    "cancel_url": "https://yourapp.bubbleapps.io/payment-cancel"
  }
}
```

**Expected result:** The API returns an order ID and approval links. The 'approve' link is where you redirect the customer.

### 4. Build the payment workflow

On your payment page, add a Pay with PayPal button. Create a workflow: When button is clicked → Step 1: Call 'Get Access Token' action. Step 2: Call 'Create Order' action with the access_token from Result of step 1 and the dynamic amount from your product/cart. Step 3: Open an external website action → use the approval URL from Result of step 2 (the link with rel = 'approve'). The customer is redirected to PayPal to approve the payment. After approval, PayPal redirects them back to your return_url with a token parameter in the URL.

> Pro tip: Store the PayPal order ID in a Custom State or your Order Data Type before redirecting — you will need it to capture the payment when the customer returns.

**Expected result:** Clicking the payment button creates a PayPal order and redirects the customer to PayPal for payment approval.

### 5. Capture the payment after customer approval

On your payment-success page, add a workflow for the 'Page is loaded' event. Step 1: Get the PayPal token from the URL using 'Get data from page URL → parameter token.' Step 2: Call 'Get Access Token' action. Step 3: Create a third API call named 'Capture Order' — POST to https://api-m.sandbox.paypal.com/v2/checkout/orders/[order_id]/capture with the bearer token. Call this action with the order ID from your stored data and the access token from Step 2. Step 4: If the capture succeeds (status = COMPLETED), update your Order record's status to Paid and show a success message.

**Expected result:** When the customer returns from PayPal, the payment is captured automatically and the order status updates to Paid.

### 6. Set up PayPal webhooks for payment confirmation

In PayPal Developer Dashboard → My Apps → your app → Webhooks, click 'Add Webhook.' Enter your Bubble backend workflow URL. Select the event CHECKOUT.ORDER.APPROVED. In Bubble, create a Backend workflow called paypal-webhook. The webhook payload contains the order ID and payment status. Add actions to find the matching Order in your database and update its status. This is a safety net — even if the customer closes the browser before the return URL loads, the webhook confirms payment server-side. For complex payment flows with subscriptions or multi-currency support, RapidDev can help architect a robust PayPal integration.

**Expected result:** PayPal sends webhook notifications to your Bubble backend, ensuring payment confirmation even if the user closes their browser.

## Complete code example

File: `API Connector payload`

```json
{
  "PayPal API Calls": {
    "1_Get_Access_Token": {
      "method": "POST",
      "url": "https://api-m.sandbox.paypal.com/v1/oauth2/token",
      "headers": {
        "Content-Type": "application/x-www-form-urlencoded",
        "Authorization": "Basic {base64(ClientID:Secret)}"
      },
      "body": "grant_type=client_credentials",
      "use_as": "Action"
    },
    "2_Create_Order": {
      "method": "POST",
      "url": "https://api-m.sandbox.paypal.com/v2/checkout/orders",
      "headers": {
        "Content-Type": "application/json",
        "Authorization": "Bearer <dynamic_access_token>"
      },
      "body": {
        "intent": "CAPTURE",
        "purchase_units": [
          {
            "amount": {
              "currency_code": "USD",
              "value": "<dynamic_amount>"
            },
            "description": "<dynamic_description>"
          }
        ],
        "application_context": {
          "return_url": "https://yourapp.bubbleapps.io/payment-success",
          "cancel_url": "https://yourapp.bubbleapps.io/payment-cancel"
        }
      },
      "use_as": "Action"
    },
    "3_Capture_Order": {
      "method": "POST",
      "url": "https://api-m.sandbox.paypal.com/v2/checkout/orders/<order_id>/capture",
      "headers": {
        "Content-Type": "application/json",
        "Authorization": "Bearer <dynamic_access_token>"
      },
      "body": {},
      "use_as": "Action"
    }
  }
}
```

## Common mistakes

- **Using the Live PayPal API URL during testing** — Live API calls process real money. Sandbox URLs (api-m.sandbox.paypal.com) should be used for all testing. Fix: Use sandbox URLs during development. Only switch to api-m.paypal.com (without 'sandbox') when going live.
- **Not marking the Authorization header as Private** — Without the Private checkbox, the header value (containing your Client ID and Secret) is sent through the user's browser, exposing your credentials. Fix: Always check the 'Private' checkbox on the Authorization header in the API Connector.
- **Skipping the capture step after customer approval** — Creating an order and getting approval does not charge the customer. The capture step is required to actually collect the payment. Fix: Always call the Capture Order endpoint on the return URL page to finalize the payment.

## Best practices

- Always use sandbox credentials and URLs during development and testing
- Mark all authentication parameters as Private in the API Connector
- Store the PayPal order ID in your database before redirecting to PayPal
- Set up webhooks as a backup for payment confirmation in case the return redirect fails
- Test the full payment flow end-to-end with PayPal sandbox test buyer accounts
- Log all API responses (success and failure) to a database for troubleshooting
- Add clear loading indicators while the PayPal API calls are processing

## Frequently asked questions

### Can I use a PayPal plugin instead of the API Connector?

Yes, there are PayPal plugins on the Bubble marketplace. However, the API Connector approach gives you direct control over the PayPal API, does not depend on a third-party plugin developer, and avoids plugin page-load overhead.

### How do I switch from sandbox to live PayPal?

Change the API URL from api-m.sandbox.paypal.com to api-m.paypal.com, and replace your sandbox Client ID and Secret with your Live credentials from the PayPal developer dashboard.

### Does PayPal charge transaction fees?

Yes. PayPal charges approximately 2.9% + $0.30 per domestic transaction for standard processing. International transactions have higher fees. Check PayPal's current pricing page for exact rates.

### Can I accept PayPal and Stripe in the same Bubble app?

Yes. Offer both options on your payment page — a Pay with PayPal button and a Pay with Card (Stripe) button. Each triggers a different workflow. Store the payment method used on the Order record.

### What if the customer cancels on the PayPal approval page?

PayPal redirects them to your cancel_url. On that page, show a message like 'Payment was cancelled' and a button to return to the cart or try again.

### How do I handle refunds through PayPal in Bubble?

Create an additional API Connector call to PayPal's Refund endpoint (POST /v2/payments/captures/{capture_id}/refund). Call it from an admin workflow with the capture ID stored during the original payment.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/integrate-paypal-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/integrate-paypal-in-bubble
