BeginnerPayments

Stripe Checkout Session

Create a Stripe Checkout session and return the payment URL to your frontend.

Click to expand
Flow StartAPI POSTCodecodeJs1HTTP RequestStripe APICodecodeJs2Simple OutputCheckout URL

Stripe Checkout Session

Create a Stripe Checkout session and return the payment URL to your frontend.

Overview

This workflow exposes an API endpoint that receives product details (name, price, quantity), validates all inputs, creates a Stripe Checkout session via the Stripe API, and returns the checkout URL. Your frontend redirects the customer to that URL to complete payment.

This is the most common Stripe integration pattern — no need to build a custom payment form. The workflow includes input validation, redirect URL allowlisting, Stripe error handling, and secret management.

Prerequisites

  • A Stripe account (test mode is fine for development)
  • Your Stripe Secret Key (from the Stripe Dashboard → Developers → API Keys)
  • A success and cancel URL on your domain

What You'll Build

A single API endpoint that:

  • Receives product name, price (in cents), and quantity
  • Validates all inputs (type, range, length)
  • Validates redirect URLs against an allowed domain list
  • Creates a Stripe Checkout session with those line items
  • Handles Stripe API errors gracefully
  • Returns the checkout URL for the customer to pay

Endpoint: POST /api/v1/YOUR_ID/create-checkout

Request body:

{
  "product_name": "Pro Plan",
  "price": 2999,
  "quantity": 1,
  "success_url": "https://yourdomain.com/success",
  "cancel_url": "https://yourdomain.com/cancel"
}

Response:

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_...",
  "session_id": "cs_test_..."
}

Vault

Store your Stripe key in Settings → Vault:

Key Value
stripe_secret_key Your Stripe secret key (sk_test_... or sk_live_...)

Never hardcode the Stripe secret key in the workflow. Use {{secrets.stripe_secret_key}} to reference it securely.

Workflow Nodes

1. Flow Start - API Endpoint

Setting Value
Trigger Type API
Method POST
Custom Path create-checkout
CORS Origins Your domain(s) (e.g. https://yourdomain.com)
Rate Limit 10/min
Timeout 30s

CORS restricts which domains can call this endpoint from a browser. Without it, any website could create checkout sessions on your Stripe account. The rate limit of 10/min prevents abuse — lower it further if your traffic is low.

2. Code - Validate & Build Stripe Payload

Validates all inputs and constructs the request body for the Stripe Checkout Sessions API. Stripe expects application/x-www-form-urlencoded format.

Output variable: codeJs1

var productName = variables.product_name || "";
var price = parseInt(variables.price);
var quantity = parseInt(variables.quantity);
var successUrl = variables.success_url || "";
var cancelUrl = variables.cancel_url || "";

// Validate product name
if (!productName || productName.length < 1 || productName.length > 200) {
  throw new Error("Invalid product name");
}

// Validate price (minimum $0.50 = 50 cents, max $100,000)
if (!price || price < 50 || price > 10000000) {
  throw new Error("Invalid price: must be between 50 and 10000000 cents");
}

// Validate quantity
if (!quantity || quantity < 1 || quantity > 99) {
  throw new Error("Invalid quantity: must be between 1 and 99");
}

// Validate redirect URLs against allowed domains
var allowedDomains = ["https://yourdomain.com"];
var successAllowed = false;
var cancelAllowed = false;

for (var i = 0; i < allowedDomains.length; i++) {
  if (successUrl.indexOf(allowedDomains[i]) === 0) successAllowed = true;
  if (cancelUrl.indexOf(allowedDomains[i]) === 0) cancelAllowed = true;
}

if (!successAllowed || !cancelAllowed) {
  throw new Error("Invalid redirect URL: must match allowed domain");
}

var params = [
  "payment_method_types[0]=card",
  "line_items[0][price_data][currency]=usd",
  "line_items[0][price_data][product_data][name]=" + encodeURIComponent(productName),
  "line_items[0][price_data][unit_amount]=" + price,
  "line_items[0][quantity]=" + quantity,
  "mode=payment",
  "success_url=" + encodeURIComponent(successUrl),
  "cancel_url=" + encodeURIComponent(cancelUrl)
];

({
  stripeBody: params.join("&")
});

Input validation rules

Field Type Constraints
product_name string 1–200 characters
price integer 50–10,000,000 (cents). Stripe minimum is $0.50
quantity integer 1–99
success_url string Must start with an allowed domain
cancel_url string Must start with an allowed domain

The redirect URL validation is critical. Without it, an attacker could set success_url to a phishing site that mimics your payment confirmation page. The allowedDomains array should contain only your domain(s).

Understanding the Stripe payload

Field Description
payment_method_types[0] Payment method — card for credit/debit cards
line_items[0][price_data][currency] Currency code (e.g., usd, eur)
line_items[0][price_data][product_data][name] Product name shown on checkout
line_items[0][price_data][unit_amount] Price in cents (2999 = $29.99)
line_items[0][quantity] Number of items
mode payment for one-time, subscription for recurring
success_url Where to redirect after successful payment
cancel_url Where to redirect if customer cancels

Stripe prices are always in the smallest currency unit. For USD that's cents, for EUR it's cents, for JPY it's yen (no decimals).

3. HTTP Request - Stripe API

Creates the Checkout session via the Stripe API.

Setting Value
Method POST
URL https://api.stripe.com/v1/checkout/sessions

Headers:

Key Value
Authorization Bearer {{secrets.stripe_secret_key}}
Content-Type application/x-www-form-urlencoded

Body: {{codeJs1.stripeBody}}

Output variable: httpRequest

The Stripe secret key is stored as a secret and referenced with {{secrets.stripe_secret_key}}. Never hardcode API keys in workflow nodes — if the workflow JSON is exported or shared, the key would be exposed.

4. Code - Extract Checkout URL

Extracts the checkout URL and session ID from the Stripe response, with error handling for failed API calls.

Output variable: codeJs2

var resp = variables.httpRequest;

if (resp.status !== 200) {
  throw new Error("Stripe error " + resp.status + ": " + (resp.data.error ? resp.data.error.message : resp.statusText));
}

var session = resp.data;

({
  checkout_url: session.url,
  session_id: session.id
});

Without this check, a Stripe error (invalid key, bad parameters, rate limit) would cause the next node to crash with an unhelpful error. This surfaces the actual Stripe error message.

5. Simple Output - Return URL

Returns the checkout URL to the caller.

Setting Value
Status 200
Type JSON
Output {{codeJs2}}

Frontend Integration

Once you have the endpoint, redirect the customer from your frontend:

async function checkout() {
  const res = await fetch('https://your-domain.com/api/v1/YOUR_ID/create-checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      product_name: 'Pro Plan',
      price: 2999,
      quantity: 1,
      success_url: window.location.origin + '/success',
      cancel_url: window.location.origin + '/cancel'
    })
  });

  if (!res.ok) {
    const error = await res.text();
    console.error('Checkout error:', error);
    return;
  }

  const data = await res.json();
  window.location.href = data.checkout_url;
}

Always handle errors on the frontend. If validation fails or Stripe returns an error, show a user-friendly message instead of silently failing.

Test vs Live Mode

Mode Secret Key Prefix Cards
Test sk_test_... Use 4242 4242 4242 4242 with any future date and CVC
Live sk_live_... Real cards are charged

Always develop and test with test mode keys. Switch to live keys only when deploying to production.

Testing

Using curl

curl -X POST https://your-domain.com/api/v1/YOUR_ID/create-checkout \
  -H "Content-Type: application/json" \
  -d '{
    "product_name": "Pro Plan",
    "price": 2999,
    "quantity": 1,
    "success_url": "https://yourdomain.com/success",
    "cancel_url": "https://yourdomain.com/cancel"
  }'

Expected:

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_...",
  "session_id": "cs_test_..."
}

Open the checkout_url in your browser — you should see the Stripe Checkout page with your product.

Testing validation

Test Input Expected
Missing product name "product_name": "" Error: Invalid product name
Price too low "price": 10 Error: Invalid price
Price too high "price": 99999999 Error: Invalid price
Quantity zero "quantity": 0 Error: Invalid quantity
Bad redirect URL "success_url": "https://evil.com/phish" Error: Invalid redirect URL
Valid request All fields correct 200 with checkout URL

Using Ubex Manual Mode

  1. Open the workflow in the Ubex editor
  2. Click Manual Run
  3. Enter the test input:
{
  "product_name": "Pro Plan",
  "price": 2999,
  "quantity": 1,
  "success_url": "https://yourdomain.com/success",
  "cancel_url": "https://yourdomain.com/cancel"
}
  1. Step through each node and verify you get a valid checkout URL back.

Test card numbers

Card Number
Visa (success) 4242 4242 4242 4242
Visa (decline) 4000 0000 0000 0002
3D Secure 4000 0025 0000 3155
Insufficient funds 4000 0000 0000 9995

Use any future expiry date and any 3-digit CVC.

What to verify

Check Expected
checkout_url Starts with https://checkout.stripe.com/
session_id Starts with cs_test_ (test mode)
Checkout page Shows your product name and price
Success redirect After payment, redirects to your success URL
Invalid price rejected Error thrown for out-of-range values
Bad URL rejected Error thrown for non-allowed domains
Stripe error surfaced Bad API key returns readable error message

Security Checklist

Control Status
POST only endpoint
Stripe secret key stored as secret
CORS restricted to your domain(s)
Rate limiting (10/min)
Input validation (price, quantity, name)
Redirect URL allowlist
Stripe API error handling
Stripe Checkout hosted page (PCI compliant)
Price in smallest currency unit (prevents rounding errors)
Test vs live mode separation
No real secrets in tutorial JSON