Merchant Billing
Merchant Billing is the recommended integration surface for new B2B checkout and billing integrations.
Use billing when you need:
- hosted checkout links for online orders
- balance top-ups such as AI credits or prepaid account balance
- managed renewal billing with customer-confirmed renewal invoices
- products and prices managed from a catalog
- project-scoped API keys, limits, team access, and webhooks
- usage records linked to your customer or subscription data
The old payment-intent flow remains available for existing integrations, but it should be treated as legacy and deprecated in future. New integrations should use /api/*.
Core objects
| Object | What it means |
|---|---|
| Project | Isolates catalog, checkout sessions, API keys, webhooks, limits, and team access. |
| Customer | Optional billing identity for subscriptions, invoices, usage, and portal access. Checkout can create one automatically from customer fields. |
| Product | What you sell, such as VPN Pro, AI token pack, or SaaS Pro. |
| Price | How you charge for a reusable product: one-time, balance top-up, or subscription. Optional for custom one-time payment requests. |
| Payment Link | Reusable public entry point for a fixed project and price. Opening it is read-only; checkout is created only after buyer confirmation. |
| Checkout session | One-time hosted checkout created from a Payment Link or from your backend. |
| Invoice | Internal billable record created behind checkout and subscriptions. Use it for status, reconciliation, and webhooks. |
| Subscription | Renewal schedule created after a subscription checkout is completed. Stendly creates renewal invoices; customers confirm renewal payments unless you build a balance-based flow. |
| Usage record | Metered activity for reporting or usage-based billing. |
| Webhook delivery | Delivery log for events sent to your backend. |
Billing modes
| Mode | Use it for | Required price fields |
|---|---|---|
one_time | Online order, license, setup fee, single purchase | unitAmountCents, currency |
balance_top_up | Credits, AI tokens, prepaid balance | unitAmountCents, currency, creditAmount |
subscription | Monthly, semiannual, yearly, or other recurring product access | unitAmountCents, currency, recurringInterval, recurringIntervalCount |
Recommended setup
- Create or select a project in the B2B dashboard.
- Copy the read-only Project ID from Project Settings.
- Create an API key with
billing:write,billing:read,payments:write,payments:read. - Configure a webhook endpoint and save the signing secret immediately.
- Create products and prices when you need reusable catalog, balance top-ups, or managed subscriptions.
- Start with a Payment Link when a fixed product/price is enough.
- Create checkout sessions from your backend when you need trusted customer IDs, dynamic amounts, or order-specific redirects.
- Fulfill access only after a verified webhook event confirms payment.
Create catalog and checkout
API_KEY="st_live_or_dev_key"
PROJECT_ID="project_uuid"
curl -X POST "https://api.stendly.com/api/products" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: product-vpn-pro" \
-d '{
"projectId": "'"$PROJECT_ID"'",
"name": "VPN Pro",
"description": "Monthly VPN access"
}'
PRODUCT_ID="product_uuid"
curl -X POST "https://api.stendly.com/api/prices" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: price-vpn-pro-semiannual" \
-d '{
"projectId": "'"$PROJECT_ID"'",
"productId": "'"$PRODUCT_ID"'",
"name": "VPN Pro semiannual",
"unitAmountCents": 4999,
"currency": "USD",
"billingMode": "subscription",
"recurringInterval": "month",
"recurringIntervalCount": 6
}'
For a one-off order where your own backend already owns the cart, you can skip catalog setup and create a checkout session with amountCents, mode: "one_time", and merchantReference.
PRICE_ID="price_uuid"
curl -X POST "https://api.stendly.com/api/checkout-sessions" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: checkout-order-1001" \
-d '{
"projectId": "'"$PROJECT_ID"'",
"priceId": "'"$PRICE_ID"'",
"mode": "subscription",
"externalCustomerId": "telegram_123456",
"customerEmail": "customer@example.com",
"merchantReference": "order_1001",
"successUrl": "https://example.com/success",
"cancelUrl": "https://example.com/cancel"
}'
Open the returned checkoutUrl on your website or inside your application.
Webhook handling
Your backend should verify X-Stendly-Signature and then update local order state by event type.
Common billing event families:
checkout.session.*invoice.*subscription.*customer.balance_*payment_intent.*
For production, do not collapse all event types into a single paid state. Treat checkout completion, invoice payment, subscription activation, and balance crediting according to your fulfillment rules.
Examples
The repo includes website-based billing examples:
examples/billing-nodeexamples/billing-pythonexamples/billing-dotnet
Each example implements one-time checkout, balance top-up, subscription checkout, catalog setup, usage recording, webhook verification, and local order status sync. The page shows what the customer receives and what the merchant should unlock after the signed webhook arrives.