# Square

- Tool: Bubble
- Difficulty: Advanced
- Time required: 3–5 hours
- Last updated: July 2026

## TL;DR

Connect Bubble to Square using the API Connector with your access token as a Private header for server-side payment calls, combined with Square's Web Payments SDK loaded in a Bubble HTML element for browser-side card tokenization. Square has no native Bubble plugin — this setup requires both components. Sandbox and production use completely separate credentials and SDK URLs.

## Why Square Requires a Fully Manual Setup in Bubble

Unlike Stripe, Square does not have a native plugin in Bubble's Plugin Marketplace. As of 2026, there is no officially maintained Square plugin equivalent to the free Stripe.js plugin. This means every Bubble app connecting to Square needs to build the integration from scratch using the API Connector and a custom HTML element — which is actually straightforward once you understand the two-component pattern.

The architecture mirrors how Stripe Connect works without a plugin: (1) a client-side JavaScript SDK handles card collection in the browser, tokenizing the card number into a one-time-use nonce; and (2) a server-side API call uses that nonce to charge the customer, with your secret access token protected in a Private API Connector header.

Square's Web Payments SDK is the client-side component. You embed it in a Bubble HTML element, initialize it with your Application ID and Location ID (both of which are safe to expose client-side — they are not secrets), and render a card input form. When the customer clicks Pay, you call card.tokenize() which returns a token object. The token.token value (the nonce) is what Square calls a source_id in the payment API.

The nonce passes from the HTML element's JavaScript back into Bubble state through the bubble_fn_ bridge function. A triggered workflow picks it up, calls the API Connector's POST /v2/payments, and processes the charge server-side with your access token safely in a Private header.

Square's most distinctive feature for Bubble founders is the Locations API — each physical store, restaurant location, or online presence has a Location ID. Every payment is attributed to a specific location. If you are building for a multi-location retail business, you will let users select their location and pass the corresponding Location ID with each payment.

One critical gotcha specific to Square: sandbox and production use completely separate credential sets and separate SDK URLs. The sandbox SDK URL (sandbox.web.squarecdn.com) will reject production access tokens, and vice versa. Many builders hit this after switching to production and forget to update the HTML element.

## Before you start

- A Square developer account — sign up at developer.squareup.com (free). Sandbox access is immediate with pre-loaded test accounts
- A Square application created in the Developer Dashboard — this gives you your Application ID, Access Token (sandbox and production), and Location ID
- Your sandbox Application ID (sq0idp-...), sandbox Access Token (EAAAl...), and sandbox Location ID — all found in Developer Dashboard → Applications → your app → Sandbox
- A Bubble app (any plan) for the API Connector and HTML element; a paid plan (Starter $32/month or above) if you want to receive Square webhooks via Backend Workflows
- Basic familiarity with Bubble Custom States — you will use them to pass the payment nonce from the HTML element to Bubble's workflow engine

## Step-by-step guide

### 1. Create a Square Developer Application and Collect Credentials

Square's developer setup is straightforward but involves collecting four distinct values — two of which differ between sandbox and production.

Go to developer.squareup.com and sign in (or create an account — it is free). Click 'Applications' in the left menu, then '+ Create your first application' (or 'Create an application' if you have existing ones). Give it a name like 'My Bubble App'.

Once created, you will land on your application's detail page. Navigate to the Sandbox section in the left sidebar of your application. Here you will find:

1. Application ID (starts with sq0idp-...): Your app's identifier. Safe to expose client-side in the HTML element — Square uses it to identify which app's card form is loading. Note: In Sandbox, the Application ID is the same for sandbox and production, but the Access Token differs.

2. Sandbox Access Token (starts with EAAAl... or similar): Your sandbox secret key. This goes into Bubble's API Connector as a Private header. Never expose this in the browser.

3. Location ID: Found under Sandbox → Locations. Every Square transaction must be attributed to a Location. Note the Location ID (starts with L...) for your sandbox.

For production credentials, click 'Production' in the left sidebar of your application. Production has its own separate Access Token and may have different Location IDs.

Keep all four sandbox values ready:
- Sandbox Application ID: sq0idp-...
- Sandbox Access Token: EAAAl...
- Sandbox Location ID: L...
- Sandbox SDK URL: https://sandbox.web.squarecdn.com/v1/square.js

```
// Square Developer Dashboard credentials to collect:

// Sandbox (for development):
// Application ID: sq0idp-YOUR_APP_ID  (safe for client-side use)
// Access Token:   EAAAl_SANDBOX_TOKEN  (PRIVATE - never expose in browser)
// Location ID:    LXXXXXXXXXXX         (safe for client-side use)
// SDK URL:        https://sandbox.web.squarecdn.com/v1/square.js

// Production (for live payments):
// Application ID: sq0idp-YOUR_APP_ID  (same as sandbox in most cases)
// Access Token:   EAAAl_LIVE_TOKEN     (PRIVATE - different from sandbox)
// Location ID:    LXXXXXXXXXXX         (your live location ID)
// SDK URL:        https://web.squarecdn.com/v1/square.js

// KEY DIFFERENCE: Sandbox and Production use different Access Tokens
// and different SDK URLs. Never mix them.
```

**Expected result:** You have your Sandbox Application ID, Access Token, and Location ID from the Square Developer Dashboard. You know the sandbox and production SDK URLs. You understand which values are safe client-side (Application ID, Location ID) versus secret (Access Token).

### 2. Configure the API Connector with a Private Access Token

Set up Bubble's API Connector with Square's access token as a Private header. This configuration handles all server-side Square operations: processing charges, looking up locations, and checking payment status.

In Bubble, click Plugins → Add plugins → search 'API Connector' → Install (free, by Bubble).

Click 'Add another API' and name the group 'Square'. In the shared headers section, add two headers:

Header 1:
- Key: Authorization
- Value: Bearer EAAAl_YOUR_SANDBOX_ACCESS_TOKEN
- Check Private: YES — this keeps your access token server-side

Header 2:
- Key: Square-Version
- Value: 2024-01-18 (Square requires an API version header — use the latest stable version from developer.squareup.com/docs/changelog)
- Private: NO (version strings are not sensitive)

Add Call 1 — Create Payment:
- Name: Create Payment
- Method: POST
- URL: https://connect.squareupsandbox.com/v2/payments
- Use as: Action
- Body parameters: source_id (text — the nonce from SDK), amount_money.amount (integer — cents), amount_money.currency (text — 'USD'), location_id (text), idempotency_key (text — unique per payment attempt)

Add Call 2 — List Locations:
- Name: List Locations
- Method: GET
- URL: https://connect.squareupsandbox.com/v2/locations
- Use as: Data
- No body parameters needed

Add Call 3 — Get Payment:
- Name: Get Payment
- Method: GET
- URL: https://connect.squareupsandbox.com/v2/payments/[payment_id]
- Use as: Data
- URL parameter: payment_id (text, dynamic — passed from stored payment ID)

Initialize the List Locations call first (GET is simpler to initialize — just click Initialize and Bubble will receive your location list). Then initialize Create Payment with test values.

```
{
  "api_group": "Square",
  "shared_headers": [
    {
      "key": "Authorization",
      "value": "Bearer EAAAl_YOUR_SANDBOX_ACCESS_TOKEN",
      "private": true
    },
    {
      "key": "Square-Version",
      "value": "2024-01-18",
      "private": false
    }
  ],
  "calls": [
    {
      "name": "Create Payment",
      "method": "POST",
      "url": "https://connect.squareupsandbox.com/v2/payments",
      "body": {
        "source_id": "<nonce from SDK>",
        "amount_money": {
          "amount": "<integer cents>",
          "currency": "USD"
        },
        "location_id": "<location_id>",
        "idempotency_key": "<unique UUID>"
      },
      "use_as": "Action"
    },
    {
      "name": "List Locations",
      "method": "GET",
      "url": "https://connect.squareupsandbox.com/v2/locations",
      "use_as": "Data"
    }
  ]
}
```

**Expected result:** A 'Square' API Connector group exists with a Private Authorization header containing your sandbox access token. The List Locations call is initialized and Bubble can see your sandbox location names and IDs.

### 3. Embed the Square Web Payments SDK in a Bubble HTML Element

Square's card collection requires their JavaScript SDK to run in the browser — this cannot be done through Bubble's native elements. You will embed an HTML element on your checkout page that loads the SDK and renders Square's card form.

Add an HTML element to your Bubble checkout page. Set the dimensions to accommodate a card input form (typically at least 300px wide, 200px tall for the card fields).

In the HTML element's content field, paste the Square Web Payments SDK setup code. The key values you will pass into the JavaScript are:
- APPLICATION_ID: your sandbox Application ID (sq0idp-...) — safe to include directly in the HTML
- LOCATION_ID: your sandbox Location ID (L...) — safe to include directly in the HTML

The card.tokenize() method is called when the user clicks the pay button. It returns a result object — if result.status is 'OK', the result.token contains the nonce (source_id for the payment API call).

To pass this nonce back into Bubble's workflow system, use Bubble's bubble_fn_ bridge. Create a Custom State on your page (or a Group element) called 'square_nonce' (type: text). In the JavaScript onTokenization callback, call bubble_fn_square_nonce(result.token) to set that state.

You then trigger a Bubble workflow: 'When Page's square_nonce is not empty' → call the API Connector to charge the card. After processing, reset the state to empty to allow the next payment.

IMPORTANT: The Square SDK URL in the script src must match your environment. Use https://sandbox.web.squarecdn.com/v1/square.js for sandbox, and https://web.squarecdn.com/v1/square.js for production. Using the wrong URL with the wrong credentials is the most common cause of 'Invalid application ID' errors.

```
<!-- Bubble HTML Element: Square Web Payments SDK -->
<!-- Environment: Sandbox -->

<div id="card-container"></div>
<button id="card-button" type="button">Pay Now</button>
<p id="payment-status"></p>

<script type="application/javascript"
  src="https://sandbox.web.squarecdn.com/v1/square.js">
</script>

<script>
  // Safe to expose client-side (not secrets):
  const appId = 'sq0idp-YOUR_APPLICATION_ID';
  const locationId = 'LYOUR_LOCATION_ID';

  async function initializeSquare() {
    const payments = Square.payments(appId, locationId);
    const card = await payments.card();
    await card.attach('#card-container');

    document.getElementById('card-button')
      .addEventListener('click', async function() {
        const result = await card.tokenize();
        if (result.status === 'OK') {
          // Pass nonce to Bubble state:
          bubble_fn_square_nonce(result.token);
        } else {
          const errors = result.errors
            .map(e => e.message).join(', ');
          document.getElementById('payment-status')
            .textContent = 'Error: ' + errors;
        }
      });
  }

  initializeSquare();
</script>

<!-- Production: change SDK URL to:
     https://web.squarecdn.com/v1/square.js
     and update appId and locationId to production values -->
```

**Expected result:** Your checkout page shows a Square card input form rendered by the Web Payments SDK. When a user enters a test card and clicks Pay Now, the form tokenizes the card and sets the Bubble Custom State 'square_nonce' to the nonce value (a string starting with 'cnon:...' in sandbox).

### 4. Build the Payment Workflow with Idempotency Key

With the nonce arriving in your Bubble Custom State, you now build the workflow that completes the payment — calling Square's POST /v2/payments server-side.

Create a new Bubble workflow: Trigger = 'When a state is changed' → select your Page's square_nonce state. Add a condition: Only when Page's square_nonce is not empty.

Action 1 — Generate an idempotency key: Square requires a unique idempotency_key on every payment call to prevent duplicate charges if the network request is retried. Bubble does not have a native UUID generator, but you can create one by combining the current date/time with a random element. Use the 'Create a random string' operator (in the Bubble expression editor) combined with the current datetime as a text string.

Action 2 — Convert price to cents: Just like Stripe, Square's amount field requires an integer in the smallest currency unit. For USD, multiply the price by 100 and round to zero decimal places. For $25.00, pass 2500.

Action 3 — Call the Square Create Payment API: Invoke 'Square - Create Payment' with:
- source_id: Page's square_nonce state (the nonce from the SDK)
- amount_money.amount: the cents value
- amount_money.currency: USD
- location_id: your Square Location ID (this can be hardcoded for single-location apps or pulled from a Dropdown selection for multi-location)
- idempotency_key: the value generated in Action 1

Action 4 — Handle the response: Square's payment response includes payment.status. Check if the status is 'COMPLETED' for a successful charge. Also save the response's id (Square's payment ID, prefixed with something like P...) to your database.

Action 5 — Create a Payment record in Bubble: Store the Square payment_id, amount, currency, location, and buyer's user reference in a 'Payment' data type. Set privacy rules so each user can only see their own payment records.

Action 6 — Reset the nonce state: Set the square_nonce Custom State back to empty. This prevents the workflow from firing twice if the state update has any delayed re-evaluation.

For the idempotency_key, store it in Bubble before making the API call. If the payment call fails and the user retries, generate a NEW idempotency_key for the retry — do not reuse the previous one. Reusing an idempotency_key with different parameters causes Square to return a 400 error.

```
// Workflow: When Page's square_nonce is not empty
// (Only when: Page's square_nonce is not empty)

// Step 1: Generate idempotency_key
// Expression: "bubble-" + Current date/time + "-" + Random string (10 chars)
// Store in Custom State 'payment_idempotency_key' (type: text)

// Step 2: Call API - Square Create Payment
{
  "source_id": "cnon:CBASEBsandbox_nonce_here",
  "amount_money": {
    "amount": 2500,
    "currency": "USD"
  },
  "location_id": "LYOUR_LOCATION_ID",
  "idempotency_key": "bubble-20240115123456-abc123xyz"
}

// Square API Response (success):
{
  "payment": {
    "id": "R4DYRh1yMkB7KJPvTF4eqCmF",
    "status": "COMPLETED",
    "amount_money": {
      "amount": 2500,
      "currency": "USD"
    },
    "source_type": "CARD",
    "location_id": "LYOUR_LOCATION_ID"
  }
}

// Step 3: Only when Result's payment status = 'COMPLETED'
// Create Payment record:
//   square_payment_id = Result's payment id
//   amount = 25.00 (divide by 100)
//   user = Current User
//   status = Completed

// Step 4: Set Page's square_nonce state to empty (reset)
```

**Expected result:** When the SDK passes a nonce to Bubble, the workflow fires, calls Square's payment API with a unique idempotency key, and on success ('COMPLETED' status) creates a Payment record in your Bubble database and clears the nonce state.

### 5. Switch to Production and Handle Environment Differences

Moving from Square sandbox to production is different from Stripe's test mode toggle — Square uses entirely separate credential sets and separate SDK URLs. Missing any of these three changes is the most common source of production failures.

The three things that must change when switching to production:

1. SDK URL in the HTML element: Change from https://sandbox.web.squarecdn.com/v1/square.js to https://web.squarecdn.com/v1/square.js. This is in your HTML element's script src tag.

2. Access Token in the API Connector: Go to Plugins → API Connector → Square → Authorization header. Change the value from your sandbox access token (EAAAl_sandbox...) to your production access token (EAAAl_live...) from the Square Developer Dashboard → your app → Production section. The Private checkbox stays checked.

3. API base URLs in every call: Change https://connect.squareupsandbox.com/v2/ to https://connect.squareup.com/v2/ in your API Connector calls. Do this for Create Payment, List Locations, and Get Payment.

4. Location ID in the HTML element (if it differs): Your production Location ID may differ from the sandbox Location ID. Update the LOCATION_ID constant in your HTML element JavaScript.

For a cleaner approach, use Bubble's App Settings: store the environment-specific values (SDK URL, Location ID, base URL) in Bubble App Settings fields (Settings → General → App Settings). Then reference these settings in your HTML element using Bubble's settings expression syntax, making environment switches a settings change rather than code edits.

After switching to production, test with a real card (a small $1.00 charge) before going live with real customers. Check the Square Dashboard under Payments to confirm the test charge appeared.

Note: Your Square account must be fully activated (business verification complete, bank account linked for settlements) before production payments will settle. Sandbox payments settle immediately in test data; production payments follow Square's normal settlement schedule (typically 1–2 business days).

```
// PRODUCTION ENVIRONMENT CHECKLIST:

// 1. HTML Element - update SDK URL:
// FROM: src="https://sandbox.web.squarecdn.com/v1/square.js"
// TO:   src="https://web.squarecdn.com/v1/square.js"

// 2. API Connector → Square → Authorization header:
// FROM: Bearer EAAAl_SANDBOX_ACCESS_TOKEN
// TO:   Bearer EAAAl_LIVE_ACCESS_TOKEN (from Developer Portal → Production)

// 3. API Connector → all call URLs:
// FROM: https://connect.squareupsandbox.com/v2/payments
// TO:   https://connect.squareup.com/v2/payments

// FROM: https://connect.squareupsandbox.com/v2/locations
// TO:   https://connect.squareup.com/v2/locations

// 4. HTML Element - update Location ID:
// const locationId = 'L_PRODUCTION_LOCATION_ID';

// ⚠️ Using production credentials with sandbox SDK URL causes:
// 'Invalid application ID' error
// ⚠️ Using sandbox credentials with production API URL causes:
// 401 Unauthorized
```

**Expected result:** Your Bubble app processes real Square payments in production. The Square Dashboard shows live transactions attributed to the correct Location. You can see settlement status and transaction details in the Square Seller Dashboard.

## Best practices

- Always mark the Authorization header as Private in the API Connector — Square's access token grants full access to payment processing and must never reach the browser.
- Always include a unique idempotency_key in every POST /v2/payments call — it is the only protection against duplicate charges if your workflow fires twice or the network request is retried automatically.
- Square amounts are integers in the smallest currency unit — $25.00 is 2500 for USD. Unlike some other APIs, Square wraps this in an amount_money object: {amount: 2500, currency: 'USD'}. Passing a decimal or the wrong object structure causes immediate 400 errors.
- Keep sandbox and production credentials clearly separated in your Bubble app. Consider using Bubble's App Settings to store environment-specific values (SDK URL, API base URL, Location ID) so switching environments is a settings change rather than editing HTML and workflow code directly.
- Set Privacy Rules on any 'Payment' data type you create to restrict Square payment IDs to the owning user — payment IDs can be used to look up transaction details and should not be visible to other users.
- For multi-location businesses, store Location IDs in a Bubble data type rather than hardcoding them. Use the List Locations API call to populate a Dropdown that lets the user (or admin) select the active location dynamically.
- Handle Square's webhook events (payment.created, payment.updated) via a Bubble Backend Workflow to keep payment status in sync asynchronously — especially important for in-person payments where status updates arrive seconds after the charge.
- Test the complete sandbox-to-production switch before going live: update all three components (SDK URL, Access Token, API base URLs) and run a real $1.00 test charge to confirm end-to-end functionality with production credentials.

## Use cases

### Restaurant Online Pre-Order System

A restaurant chain's Bubble app lets customers place online orders for pickup or delivery. Payments run through Square (same system the restaurant uses for in-person POS), giving the owner unified reporting across online and physical orders in the Square Dashboard.

Prompt example:

```
Build a Bubble checkout page that displays a Square card form in an HTML element, collects payment via the Web Payments SDK, passes the nonce to an API Connector workflow calling POST /v2/payments with the restaurant's Location ID, then creates an Order record in Bubble and sends a confirmation email.
```

### Retail Booking and Deposit System

A boutique retail business uses Bubble to manage appointment bookings (for personal styling sessions or alterations). Customers pay a deposit upfront via Square. The Bubble app stores the Square payment_id for refund processing if an appointment is cancelled.

Prompt example:

```
Create a Bubble workflow that charges a $50 deposit via Square when a customer confirms a booking — call POST /v2/payments with amount_money: {amount: 5000, currency: 'USD'}, idempotency_key using a random UUID, and save the returned payment.id to the Booking record for potential refund.
```

### Multi-Location Retail Platform

A franchise or multi-location retail brand uses a Bubble app where each store has its own Square Location ID. Customers select their nearest location, and payments are attributed to that location's Square account for location-level revenue reporting and settlement.

Prompt example:

```
Design a Bubble page that calls GET /v2/locations to list the user's active Square locations, displays them in a Dropdown, and stores the selected Location ID in a custom state. When the customer checks out, the POST /v2/payments call includes the selected location_id so the payment appears in that location's Square Dashboard.
```

## Troubleshooting

### 'Invalid application ID' error when the Square card form loads

Cause: The Application ID in the HTML element JavaScript does not match the SDK environment, OR you are using the production SDK URL with sandbox credentials (or vice versa).

Solution: Verify that you are using the correct combination: sandbox Application ID with the sandbox SDK URL (sandbox.web.squarecdn.com), or production Application ID with the production SDK URL (web.squarecdn.com). Open your browser console to see the full error message from Square's SDK — it will tell you which value it rejected. The Application ID should start with sq0idp-.

```
// Sandbox combination (both must match):
// src: https://sandbox.web.squarecdn.com/v1/square.js
// appId: 'sq0idp-YOUR_SANDBOX_APP_ID'

// Production combination (both must match):
// src: https://web.squarecdn.com/v1/square.js
// appId: 'sq0idp-YOUR_PRODUCTION_APP_ID'
```

### Square payment API returns 400 with 'DUPLICATE_IDEMPOTENCY_KEY'

Cause: The same idempotency_key was sent in two different payment calls with different parameters. Square considers this an error because the same key should represent the same payment attempt.

Solution: Generate a new unique idempotency_key for each payment attempt. Do not store and reuse idempotency keys. Use a combination of timestamp and random string to guarantee uniqueness. If you are retrying a failed payment for the same order, use the same idempotency_key only if you are retrying with identical parameters — otherwise generate a new key.

```
// Generate a unique idempotency_key in Bubble:
// Expression: "payment-" + Current datetime formatted as "YYYYMMDDHHmmss" + "-" + Random string 8 chars
// Example result: payment-20240115143022-xK9mPqLe
```

### The square_nonce state is not updating after the user clicks Pay Now in the HTML element

Cause: The bubble_fn_ bridge function name does not match the Custom State name, or there is a JavaScript error in the HTML element that prevents the tokenize() callback from completing.

Solution: Open your browser's developer console while on the checkout page. Click Pay Now and watch for JavaScript errors. Check that the function call in your HTML element matches: bubble_fn_ + exact Custom State name. If your state is 'square_nonce', the function is bubble_fn_square_nonce(result.token). Also verify the HTML element has the correct Custom State associated with it in Bubble's element properties.

### 'There was an issue setting up your call' when initializing the Create Payment API Connector call

Cause: The Initialize call requires real test values. The nonce value (source_id) used in initialization must be a valid format — using a placeholder string like 'test' causes Square to reject the request with a 400 error.

Solution: Square's sandbox provides a test nonce for initialization: use 'cnon:card-nonce-ok' as the source_id value for initialization. Set amount_money.amount to 100, currency to USD, location_id to your sandbox Location ID, and idempotency_key to any unique string. This should return a successful payment response that Bubble can use to detect the response fields.

```
// Initialize test values for Create Payment:
// source_id: cnon:card-nonce-ok
// amount_money.amount: 100
// amount_money.currency: USD
// location_id: LYOUR_SANDBOX_LOCATION_ID
// idempotency_key: init-test-12345
```

### Square payment API returns 401 Unauthorized

Cause: The Access Token in the API Connector is incorrect, has expired, or is from a different environment (sandbox token used with production URL, or vice versa).

Solution: Go to Plugins → API Connector → Square → shared headers → Authorization header. Verify the value starts with 'Bearer ' followed by your correct access token. Check the Square Developer Dashboard to confirm the token is still active. Ensure you are using the sandbox token with the sandbox URL (connect.squareupsandbox.com) and the production token with the production URL (connect.squareup.com).

## Frequently asked questions

### Is there a Square plugin for Bubble, or do I have to build this from scratch?

As of 2026, there is no officially maintained Square plugin in Bubble's Plugin Marketplace equivalent to the Stripe.js plugin. You need to build the integration manually using the API Connector (for server-side payment calls) and a Bubble HTML element (for the Web Payments SDK card form). This guide covers the complete setup — the process is more involved than Stripe's plugin-based setup but is achievable without writing complex code.

### Why does Square have separate sandbox and production environments instead of just test mode?

Unlike Stripe (which uses test/live as modes within the same account), Square treats sandbox as a completely separate environment with separate Application IDs, Access Tokens, SDK URLs, and Location IDs. When you move to production, you must update all three components: the SDK URL in your HTML element, the Access Token in your API Connector, and the API base URLs in your calls. Missing any one of them causes authentication errors that can be confusing to debug.

### What is the idempotency_key and can I skip it?

The idempotency_key is a unique string you generate for each payment attempt. Square uses it to detect and prevent duplicate charges — if the same key appears twice within a short window, Square returns the original response instead of creating a second charge. It is technically optional in Square's API but strongly recommended for any production app. If a network request fails and Bubble retries the workflow, the idempotency_key prevents the customer from being charged twice.

### Can I use Square for recurring subscriptions in Bubble?

Square's Subscriptions API is available but requires additional API Connector calls beyond basic payments. You would call POST /v2/subscriptions with a plan variation ID (created in the Square Dashboard). Unlike Stripe subscriptions, Square subscriptions require the customer to have a card on file (stored via Square's Cards API). The setup is more complex than Stripe's subscription flow and has less community documentation for Bubble specifically.

### How do I receive payment webhooks from Square in Bubble?

Create a Backend Workflow in Bubble (requires a paid plan) to receive Square webhook events. Go to Settings → API → enable Workflow API. Create a Backend Workflow named 'square_events', click 'Detect request data', then register the generated Bubble URL in the Square Developer Dashboard under Webhooks. Square can send payment.created, payment.updated, and order.updated events. Note that Square's webhook signature verification uses HMAC-SHA256 — Bubble cannot natively verify this signature, so use a query parameter secret in your webhook URL for basic validation.

---

Source: https://www.rapidevelopers.com/bubble-integrations/square
© RapidDev — https://www.rapidevelopers.com/bubble-integrations/square
