Skip to content

Updating visuals

If you see any images containing outdated UI, please bear with us.

We are updating all content as quickly as possible to mirror our new UI.

Payment Intents and subscriptions ​

This page covers two flows: one-time payments using Payment Intents (with a payment form in your app) and subscriptions (recurring charges) created from your backend.

Handling payments in a real app ​

  • Never store card numbers — Card data stays in Stripe. You use Stripe’s UI (Checkout or the payment element) to collect it; your backend only tells Stripe what to charge.
  • Use a Stripe customer per user — In most apps, each signed-in user has a Stripe Customer. Store the customer ID (cus_...) in your database so you can charge them again or create subscriptions.
  • Use the backend for the charge — Create or update Stripe objects from the API Endpoints tab of the Backend Workflows subtab in Data & API (or backend workflows), and return only what your interface needs (e.g. client_secret, url, or a success flag).

One-time payments (Payment Intents) ​

Use this when you charge once (e.g. a single purchase) and you want the payment form inside your app rather than on a Stripe-hosted page.

Flow ​

  1. Create or reuse a customer — When a user pays for the first time, use Create Customer and save the returned Customer ID (cus_...) in your database (e.g. on the user record).
  2. Create a Payment Intent — In an API (e.g. POST /payments/intent), use Create Payment Intent with:
    • Amount (in cents) and Currency
    • Customer (your saved cus_..., if you have it)
    • Optional: Receipt Email, Description, Metadata (e.g. order ID)
  3. Return the client_secret (and optionally the Payment Intent id) to the front end.
  4. Collect payment in the interface — Mount Stripe’s payment form in your page with Stripe.js, using that client_secret. This step is custom code. On submit, Stripe confirms the payment.
  5. Handle success and refunds — To refund, use Create Refund with the Payment Intent (or Charge).

You can also confirm a payment intent from the backend with Confirm Payment Intent. A declined card is a normal outcome that surfaces as a thrown error with no result, so wrap the action in a Try/Catch and read the reason with Retrieve Payment Intent (last_payment_error). For manual-capture flows, capture the held funds later with Capture Payment Intent.

What to store in your database ​

  • Customer ID — cus_... for the signed-in user.
  • Payment Intent ID — pi_... for support and refunds.
  • Your own reference — e.g. order ID in Metadata.

Subscriptions ​

Use subscriptions when you charge repeatedly (e.g. monthly access to a plan).

Flow ​

  1. Create products and prices in Stripe — Use Create Product for the plan and Create Price with Recurring (e.g. {"interval":"month"}).
  2. Create or reuse a customer — If the user doesn’t have a Stripe customer yet, use Create Customer and store cus_....
  3. Create the subscription — In an API (e.g. POST /subscriptions), use Create Subscription with:
    • Customer = your stored cus_...
    • Items = at least one item with a Price (price_...)
  4. Keep your app in sync — Use Retrieve Subscription or List Subscriptions to check status (active, canceled, etc.). Use Update Subscription to change plans and Cancel Subscription (or cancel at period end) to stop access.

What to store in your database ​

  • Customer ID — cus_...
  • Subscription ID — sub_... for that user.
  • Price ID — price_... so you can show their plan and handle upgrades/downgrades.

You can also offer subscriptions via Stripe Checkout (mode subscription); see Stripe Checkout.

Good to know ​

  • Create needs a recurring price — Create Subscription rejects one-time prices. The customer also needs a default payment method, or you defer payment with payment_behavior: "default_incomplete" or a trial_period_days.
  • Cancel is immediate — Cancel Subscription ends the subscription right away. To stop it at the end of the paid period, use Update Subscription with cancel_at_period_end: "true"; "false" un-schedules a pending cancellation.
  • Two different pauses — Update Subscription with pause_collection pauses billing while the status stays active; clear it with the Resume choice on that action (it sends an empty pause_collection). The separate Resume Subscription action only targets a subscription whose status is paused (a trial that ended with no payment method) and rejects anything else, including canceled ones. Resuming issues an invoice, and the subscription only returns to active once that invoice is paid.
  • Migrate is one-way — Migrate Subscription upgrades to the flexible billing mode and cannot be reversed.
  • The billing period lives on the items — current_period_start and current_period_end are on each item (items.data[]) in what the API returns, not at the top level. Webhook payloads still carry the legacy top-level fields, so read from items.data[0] when you re-read a subscription through an action.
  • List Subscriptions Hides canceled ones by default — omitting status returns only non-canceled subscriptions; pass all or ended to include canceled ones.

Next: Webhooks or Workflow examples