SDK billing Billing
The billing namespace is the recommended SDK surface for new merchant billing integrations. It maps to /api/*.
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_timefor a single hosted payment request. You can pass a catalogpriceIdor a customamountCents. - Use
balance_top_upwhen the checkout buys prepaid credits or account balance. - Use
subscriptionwhen Stendly should manage renewal invoices. Customers confirm renewal payments unless you build a balance-based flow.
Start with Payment Links
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
| Area | Methods |
|---|---|
| Overview | overview |
| Projects | listProjects, retrieveProject, createProject, updateProject, deleteProject |
| Customers | listCustomers, retrieveCustomer, createCustomer |
| Products | listProducts, retrieveProduct, createProduct, updateProduct |
| Prices | listPrices, retrievePrice, createPrice, updatePrice |
| Payment Links | listPaymentLinks, retrievePaymentLink, createPaymentLink, setupPaymentLink, updatePaymentLink, deletePaymentLink |
| Checkout sessions | listCheckoutSessions, retrieveCheckoutSession, createCheckoutSession |
| Invoices | listInvoices, retrieveInvoice |
| Subscriptions | listSubscriptions, retrieveSubscription, updateSubscription, cancelSubscription, previewSubscriptionChange |
| Usage | listUsage, recordUsage |
| Portal sessions | createPortalSession |
| Webhook deliveries | listWebhookDeliveries, retrieveWebhookDelivery, resendWebhookDelivery |
Website examples
Use these examples when you need a complete website checkout flow:
examples/billing-nodeexamples/billing-pythonexamples/billing-dotnet
Each example covers:
one_time,balance_top_up, andsubscriptioncheckout 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