Skip to main content

SDK billing Billing

The billing namespace is the recommended SDK surface for new merchant billing integrations. It maps to /api/*.

Legacy SDK namespaces

client.intents, terminals, and the old merchant stats APIs remain available for existing integrations, but they are legacy and deprecated in future. Prefer client.billing for new checkout and billing work.

Choosing a mode

  • Use one_time for a single hosted payment request. You can pass a catalog priceId or a custom amountCents.
  • Use balance_top_up when the checkout buys prepaid credits or account balance.
  • Use subscription when Stendly should manage renewal invoices. Customers confirm renewal payments unless you build a balance-based flow.

For the simplest integration, create a reusable Payment Link and put it on your site. A public Payment Link can include client_reference_id:

https://app.stendly.com/p/pl_example?client_reference_id=user_8472

That reference is returned in checkout and webhook data for reconciliation, but it is not proof of user identity because the buyer can edit the URL. Use backend-created checkout sessions when your server must attach trusted externalCustomerId, merchantReference, dynamic amounts, or order-specific redirects.

const setup = await client.billing.setupPaymentLink({
projectId: process.env.STENDLY_PROJECT_ID!,
productName: 'VPN Pro',
billingMode: 'subscription',
unitAmountCents: 1000,
currency: 'USD',
recurringInterval: 'month',
requireClientReferenceId: true,
collectCustomerEmail: true,
});

console.log(setup.paymentLink.url);

Node.js

import {StendlyClient} from '@stendly/sdk';

const client = new StendlyClient({
apiKey: process.env.STENDLY_API_KEY!,
environment: 'devnet'
});

const product = await client.billing.createProduct({
projectId: process.env.STENDLY_PROJECT_ID!,
name: 'VPN Pro',
description: 'Monthly VPN access',
}, 'product-vpn-pro');

const price = await client.billing.createPrice({
projectId: process.env.STENDLY_PROJECT_ID!,
productId: product.id,
name: 'VPN Pro semiannual',
unitAmountCents: 4999,
currency: 'USD',
billingMode: 'subscription',
recurringInterval: 'month',
recurringIntervalCount: 6,
}, 'price-vpn-pro-semiannual');

const checkout = await client.billing.createCheckoutSession({
projectId: process.env.STENDLY_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',
}, 'checkout-order-1001');

console.log(checkout.checkoutUrl);

Python

from stendly import Client
from stendly.models import (
CreateBillingProductRequest,
CreateBillingPriceRequest,
CreateCheckoutSessionRequest,
)
import os

client = Client(api_key=os.environ["STENDLY_API_KEY"], environment="devnet")
project_id = os.environ["STENDLY_PROJECT_ID"]

product = client.billing.create_product(
CreateBillingProductRequest(
project_id=project_id,
name="AI token pack",
description="Prepaid AI credits",
),
idempotency_key="product-ai-token-pack",
)

price = client.billing.create_price(
CreateBillingPriceRequest(
project_id=project_id,
product_id=product.id,
name="10,000 AI credits",
unit_amount_cents=1000,
currency="USD",
billing_mode="balance_top_up",
credit_amount=10000,
),
idempotency_key="price-ai-credits-10000",
)

checkout = client.billing.create_checkout_session(
CreateCheckoutSessionRequest(
project_id=project_id,
price_id=price.id,
mode="balance_top_up",
external_customer_id="user_123",
merchant_reference="topup_1001",
success_url="https://example.com/success",
cancel_url="https://example.com/cancel",
),
idempotency_key="checkout-topup-1001",
)

print(checkout.checkout_url)

.NET

using Stendly;
using Stendly.Models;

var client = new StendlyClient(new HttpClient(), Environment.GetEnvironmentVariable("STENDLY_API_KEY")!, "devnet");
var projectId = Guid.Parse(Environment.GetEnvironmentVariable("STENDLY_PROJECT_ID")!);

var product = await client.Billing.CreateProductAsync(
new CreateBillingProductRequest(
ProjectId: projectId,
Name: "Online order",
Description: "Single purchase"
),
idempotencyKey: "product-online-order"
);

var price = await client.Billing.CreatePriceAsync(
new CreateBillingPriceRequest(
ProjectId: projectId,
ProductId: product.Id,
UnitAmountCents: 4999,
Name: "Standard order",
Currency: "USD",
BillingMode: "one_time"
),
idempotencyKey: "price-online-order"
);

var checkout = await client.Billing.CreateCheckoutSessionAsync(
new CreateCheckoutSessionRequest(
ProjectId: projectId,
PriceId: price.Id,
Mode: "one_time",
MerchantReference: "order_1001",
SuccessUrl: "https://example.com/success",
CancelUrl: "https://example.com/cancel"
),
idempotencyKey: "checkout-order-1001"
);

Console.WriteLine(checkout.CheckoutUrl);

billing methods

AreaMethods
Overviewoverview
ProjectslistProjects, retrieveProject, createProject, updateProject, deleteProject
CustomerslistCustomers, retrieveCustomer, createCustomer
ProductslistProducts, retrieveProduct, createProduct, updateProduct
PriceslistPrices, retrievePrice, createPrice, updatePrice
Payment LinkslistPaymentLinks, retrievePaymentLink, createPaymentLink, setupPaymentLink, updatePaymentLink, deletePaymentLink
Checkout sessionslistCheckoutSessions, retrieveCheckoutSession, createCheckoutSession
InvoiceslistInvoices, retrieveInvoice
SubscriptionslistSubscriptions, retrieveSubscription, updateSubscription, cancelSubscription, previewSubscriptionChange
UsagelistUsage, recordUsage
Portal sessionscreatePortalSession
Webhook deliverieslistWebhookDeliveries, retrieveWebhookDelivery, resendWebhookDelivery

Website examples

Use these examples when you need a complete website checkout flow:

  • examples/billing-node
  • examples/billing-python
  • examples/billing-dotnet

Each example covers:

  • one_time, balance_top_up, and subscription checkout modes
  • catalog setup and reuse
  • visible website state showing what the customer receives after payment
  • usage reporting
  • webhook receiver and signature verification
  • local order status sync after checkout redirects