Skip to main content

Stripe Payment Integration Guide

Availability

Stripe payment integration is not yet available. This guide describes preparation and code examples based on the integration proposal. Console configuration and function routes are subject to the actual implementation when released.

This guide covers Stripe-hosted Checkout: the server creates a payment session, the browser redirects to Stripe, and your application confirms the payment through Webhooks.

Elements-based custom checkout and Connect payment splitting are outside this guide's scope. Redirecting to hosted Checkout does not require stripePublishableKey.

Supported Clients​

This guide uses browser redirects and primarily targets Web clients:

Client TypeIntegration Method
Desktop WebRedirect from a desktop browser to Stripe-hosted Checkout
Mobile Web (H5)Redirect from a mobile browser to Stripe-hosted Checkout

Native apps can open the checkout page in the system browser, but returning to the app after payment requires separate integration. This guide does not cover native iOS/Android payment SDKs or use the WeChat Mini Program APIs wx.requestPayment and wx.requestVirtualPayment.

Prerequisites​

  • A CloudBase environment with permission to create integrations and deploy HTTP cloud functions.
  • A usable Stripe account. Account availability and payment eligibility depend on Stripe-supported countries and regions.
  • Order and product data, with prices maintained on the server.
  • Web pages for successful payment and cancellation redirects.

Step 1: Prepare Payment Credentials​

Sign in to Stripe Dashboard and select the test or live environment.

ConfigurationRequiredLocation and Purpose
stripeSecretKeyYes"Developers → API keys"; authenticates server-side Stripe API calls. Use sk_test_…, sk_live_…, or a restricted key rk_… with the required permissions
stripeWebhookSecretRequired for callbacksCreate a Webhook endpoint for this integration, then obtain that endpoint's whsec_… signing secret. See Register a Webhook
stripeApiVersionNoExplicitly sets the Stripe API version for server requests; must be compatible with the SDK and fields used by your code
Obtain the Webhook Secret After Creating the Endpoint

Unlike the credential preparation sequence for other integrations, first create the integration to obtain its callback URL, then create the corresponding Webhook endpoint in Stripe. Retrieve that endpoint's whsec_… secret and enter it in the integration configuration. Each endpoint has its own secret; do not reuse the Webhook secret of another endpoint or integration.

Server API keys and Webhook signing secrets serve different purposes and cannot replace each other. Store them only in the integration configuration or server environment variables, never in browser code.

Test and live keys, Webhook endpoints, and payment objects are managed separately and must not be mixed. Restricted keys need permissions for the API operations you actually use.

Step 2: Create the Integration and Configure Callbacks​

Create the Integration​

  1. Open CloudBase Console/Integration Center and select "Stripe Payment".
  2. Enter the integration name and stripeSecretKey, optionally set stripeApiVersion, and submit.
  3. Wait for the platform to automatically generate the HTTP cloud function and callback URL and inject the credentials into the function.
  4. View the generated function in the integration details and copy the complete callback URL for Webhook registration in the next step.

Initial creation does not require manually creating or associating a function or provisioning a callback domain. The generated function name follows <integration-name>-<random-string>; use the actual name shown in the details when calling it. For the general workflow, see Using the Integration Center.

Register a Webhook​

  1. Open Stripe Dashboard/Workbench/Webhooks and create an event destination.
  2. Enter the complete HTTPS callback URL provided by Integration Center. The proposed callback path is /stripe/webhook.
  3. Select the events to receive and save the endpoint.
  4. Retrieve the whsec_… signing secret from the newly created endpoint. Return to Integration Center, update stripeWebhookSecret, and save.
  5. Confirm the integration configuration, endpoint environment, and function association before sending a test event.
EventBusiness Handling
checkout.session.completedCheck the session's payment status and fulfill paid orders idempotently
checkout.session.async_payment_succeededRecheck status and fulfill after a delayed payment succeeds
checkout.session.async_payment_failedUpdate the order as failed; do not fulfill
checkout.session.expiredUpdate the associated unpaid order when the session expires
payment_intent.succeededHandle the successful payment with its order association; avoid duplicate fulfillment alongside session events
payment_intent.payment_failedRecord payment failure details
charge.refundedCheck refund amount and status, then update business refund records
charge.dispute.createdRecord the dispute and notify the responsible staff

Stripe delivers different event types to a single Webhook endpoint and dispatches them by event.type. The payload is JSON and does not require the AES decryption used for WeChat Pay callbacks.

Step 3: Create a Checkout Session​

The paths below follow the integration proposal's examples. Use the actual function routes when deploying:

RoutePurpose
POST /stripe/create_sessionCreates a Checkout Session and returns sessionId and checkout url
GET /stripe/retrieve_sessionRetrieves a payment session by Session ID
POST /stripe/create_refundProposed refund endpoint; confirm its implementation and key permissions before use
POST /stripe/webhookReceives payment, refund, and other events

Create the Session on the Server​

Authenticate the user and verify order ownership before looking up the order amount or Stripe Price ID. Do not create payment sessions using an unverified amount submitted by the browser.

The example below uses the stripe Node.js SDK and accepts an order already verified on the server. stripePriceId, quantity, and the user identifier come from the business order, not unvalidated request data.

const stripe = require('stripe')(process.env.stripeSecretKey, {
...(process.env.stripeApiVersion
? { apiVersion: process.env.stripeApiVersion }
: {}),
});

async function createCheckoutForOrder(order) {
if (!order.id || !order.userId || !order.stripePriceId || !order.paymentAttemptId) {
throw new Error('Missing order information');
}
if (!Number.isInteger(order.quantity) || order.quantity < 1) {
throw new Error('Invalid quantity');
}

const metadata = {
orderId: String(order.id),
userId: String(order.userId),
};

const session = await stripe.checkout.sessions.create(
{
mode: 'payment',
line_items: [{ price: order.stripePriceId, quantity: order.quantity }],
success_url: 'https://example.com/result?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://example.com/cart',
client_reference_id: String(order.id),
metadata,
payment_intent_data: { metadata },
},
{ idempotencyKey: `checkout:${order.id}:${order.paymentAttemptId}` },
);

return { sessionId: session.id, url: session.url };
}

Use the same idempotency key and parameters for the same payment attempt. For a new attempt, use a new paymentAttemptId persisted by your application, and save the association between the Session ID and order.

Top-level metadata belongs to the Checkout Session and is not automatically copied to the PaymentIntent. Set payment_intent_data.metadata separately if payment events need the same order information.

Configure Amounts and Redirects​

  • For predefined prices, pass line_items[].price, such as price_….
  • For dynamic prices, set currency, unit_amount, and product_data in line_items[].price_data; calculate the amount on the server.
  • unit_amount uses the currency's minor unit: pass 2000 for 20 USD and 500 for 500 JPY. Do not multiply all currencies by 100. See Stripe currency documentation for special cases.
  • Hosted Checkout uses success_url and cancel_url. Use server-controlled or allowlisted URLs, not arbitrary browser-supplied redirects.
  • Stripe replaces {CHECKOUT_SESSION_ID} with the actual Session ID, allowing the result page to query its status through the server.

Step 4: Redirect the Browser to Checkout​

The browser submits a business order ID, and the server creates a session and returns its checkout URL. Authenticate the endpoint. This example assumes a same-origin API forwards requests to the integration function using an existing login session.

async function startCheckout(orderId) {
const response = await fetch('/stripe/create_session', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ orderId }),
});

const result = await response.json();
if (!response.ok || !result.url) {
throw new Error(result.message || 'Unable to create checkout session');
}

window.location.assign(result.url);
}

For cross-origin requests, adapt the domain, route, authentication, and cross-origin rules to your deployment. Never send stripeSecretKey or stripeWebhookSecret to the browser.

Payment Results

success_url is for display only and is not proof of payment for fulfillment. The server verifies the final state, and Webhooks drive order updates and fulfillment.

Step 5: Handle Payment Results​

Verify Callback Sources​

The integration proposal delegates signature verification to the gateway, with the business function processing forwarded events. The function should accept only trusted gateway requests; do not expose an unauthenticated public endpoint that accepts arbitrary event JSON.

For self-hosted callbacks or local testing, verify signatures with the Stripe SDK. Register the Webhook route before express.json() to preserve the raw request body.

const express = require('express');
const stripe = require('stripe')(process.env.stripeSecretKey);
const app = express();

app.post('/stripe/webhook', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = stripe.webhooks.constructEvent(
req.body,
req.headers['stripe-signature'],
process.env.stripeWebhookSecret,
);
res.json({ received: true, eventId: event.id });
} catch (error) {
res.status(400).send('Invalid webhook signature');
}
});

app.use(express.json());

This example demonstrates signature verification only, not order handling. In production, reliably persist the event or enqueue it durably before returning 2xx. Return non-2xx on persistence failure so Stripe can retry.

Update Orders and Fulfill​

  1. Persist and deduplicate by event.id to avoid processing the same notification twice.
  2. Look up the order using its Session ID or business order number, and verify the user, currency, amount, and payment object association.
  3. For checkout.session.completed, check payment_status: fulfill for paid, wait for further results for unpaid, and handle no_payment_required according to business rules.
  4. Confirm delayed payments through checkout.session.async_payment_succeeded; do not fulfill failed payments.
  5. Record fulfillment state with a database transaction or atomic update. Use the business order or Session ID for fulfillment idempotency to prevent different events from triggering duplicate delivery.

A 2xx response does not mean payment settlement or fulfillment is complete. After persisting the event, perform slow fulfillment and notification tasks asynchronously and record failures for retry.

Verify the Integration​

Complete a payment in the Stripe test environment and verify:

  • The create-session endpoint returns a Session ID and Checkout URL.
  • After payment, the Webhook endpoint's delivery record shows 2xx and the function receives the corresponding event.
  • The business order updates based on server verification, with exactly one fulfillment record.
  • Resending the same event does not trigger duplicate fulfillment.
  • Cancelled or unpaid sessions are not incorrectly marked paid.

Do not use live keys to create real charges when validating a test integration.

Common Issues​

Webhook Signature Verification Fails​

Check that the endpoint's whsec_… matches the integration configuration and that the raw body has not been modified. An event forwarded after gateway verification cannot be reverified against the original signature using rewritten JSON.

Payment Completes but the Order Is Not Updated​

Check event delivery records and response codes in the Stripe endpoint details. Confirm event subscriptions, endpoint environment, and the integration's function association. Delayed payments also require the asynchronous success event.

Test Callbacks Locally​

Use Stripe CLI to forward test events to the local callback route:

stripe listen --forward-to localhost:3000/stripe/webhook

Use the temporary Webhook signing secret printed by the command. It differs from the Dashboard endpoint secret and must not be entered into the live integration.

Switch Between Test and Live Environments​

Prepare separate server keys and Webhook signing secrets for each environment. When switching the integration configuration, also check the environment of Price IDs and other payment objects.

Related Documentation​