Payments (totalum-sdk/payments)
import … from 'totalum-sdk/payments' — or the same namespace on totalum from 'totalum-sdk'. Install, configuration and errors: Totalum SDK.
Take payments and subscriptions with Stripe on the project owner's own Stripe account (Stripe Connect), without
handling card data: create a hosted Checkout, send the buyer to its url, and read the result back. The owner
connects Stripe once with onboardingLink(). The preview and the sandbox use Stripe test mode; the published site
uses live mode once the owner switched it on. Payment events reach the app's own POST /api/webhooks/stripe with no
setup, signed by Totalum (verify them with totalum.webhooks.verify).
const { url } = await totalum.payments.createCheckout({
mode: 'payment',
lineItems: [{ name: 'Pro plan', unitAmount: 1900, currency: 'eur', quantity: 1 }],
successUrl: 'https://acme.totalum.app/thanks?session_id={CHECKOUT_SESSION_ID}',
cancelUrl: 'https://acme.totalum.app/pricing',
});| Method or type | What it does |
|---|---|
totalumPayments | Creates the payments namespace (the same object as totalum.payments, scope payments): Stripe Connect charges on the project's own connected account. |
payments.getStatus | Whether the project's Stripe account can take payments right now: use it to show "Connect Stripe" to the owner or to hide a Buy button while payments are off. |
payments.onboardingLink | A single-use link to Stripe's hosted onboarding, where the project owner connects (or updates) the Stripe account the app's payments go to. |
payments.createCheckout | Creates a Stripe Checkout page for a one-off payment (payment), a subscription (subscription) or saving a card for later (setup), and returns its URL: redirect the buyer there. |
payments.getCheckout | Reads a Checkout Session back, to confirm a payment on the page Stripe returns the buyer to (successUrl with ?session_id={CHECKOUT_SESSION_ID}): when paymentStatus is 'paid', mark the order paid — no webhook needed. |
payments.customerPortalLink | A link to Stripe's hosted customer portal, where a subscriber updates their card, downloads invoices or cancels their subscription. |
payments.subscriptions.get | Reads a subscription: give access while status is 'active' or 'trialing'; currentPeriodEnd is the next renewal (or the end of access when cancelAtPeriodEnd is true). |
payments.subscriptions.update | Cancels a subscription at the end of the paid period (cancelAtPeriodEnd: true, the usual "Cancel" button: the member keeps access until then), or undoes that (false). |
payments.subscriptions.cancel | Cancels a subscription immediately: no more charges, status: 'canceled' at once, access ends now. |
payments.refunds.create | Refunds a payment, in full or in part. |
CheckoutInput | What a Checkout page sells and where it sends the buyer, for payments.createCheckout(). |
CheckoutOutput | The Checkout page created by payments.createCheckout(). |
CheckoutSession | A Checkout Session read back with payments.getCheckout(). |
CustomerPortalLinkInput | The input of payments.customerPortalLink(). |
CustomerPortalLinkOutput | The answer of payments.customerPortalLink(). |
OnboardingLinkInput | The input of payments.onboardingLink(). |
OnboardingLinkOutput | The answer of payments.onboardingLink(). |
PaymentsStatus | The state of the project's Stripe account, from payments.getStatus(). |
RefundInput | The input of payments.refunds.create(). |
RefundOutput | A refund, from payments.refunds.create(). |
Subscription | A Stripe subscription, from payments.subscriptions.*. |
SubscriptionUpdateInput | The change payments.subscriptions.update() makes. |
#totalumPayments
totalumPayments(options?: TotalumClientOptions): TotalumPaymentsCreates the payments namespace (the same object as totalum.payments, scope payments): Stripe Connect charges
on the project's own connected account. Card data never passes through here — send buyers to the hosted Checkout
url; card fields are refused with CARD_DATA_NOT_ACCEPTED. Stripe test mode on the preview and in the sandbox;
live mode only on the published site, once the owner onboarded and switched live on. Payment events
(payments.<Stripe type>) reach the app's own POST /api/webhooks/stripe with no setup — test mode's on the
preview, live mode's on the published site — signed by Totalum: verify them with totalum.webhooks.verify.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
options | TotalumClientOptions | no | The project key, API URL and environment; each falls back to its environment variable. |
options.key | string | no | Project key tlm_pk_…; default TOTALUM_PROJECT_KEY. Server-side only. |
options.apiUrl | string | no | The Totalum API's URL: default TOTALUM_SDK_API_URL, else https://sdk-api.totalum.app. |
options.env | 'dev' | 'live' | no | Sent as x-totalum-env on every call (the database it targets, and dev vs live usage); default TOTALUM_ENV, else live. |
Returns TotalumPayments — The payments namespace: the methods below.
Example
import { totalumPayments } from 'totalum-sdk/payments';
const payments = totalumPayments();
const status = await payments.getStatus();The namespace's type is exported as TotalumPayments (import type { TotalumPayments } from 'totalum-sdk/payments').
#payments.getStatus
payments.getStatus(): Promise<PaymentsStatus>Whether the project's Stripe account can take payments right now: use it to show "Connect Stripe" to the owner or to hide a Buy button while payments are off. Free.
Returns Promise<PaymentsStatus> — The state of the connected Stripe account in the current mode (test on the preview, live on the
published site).
| Field | Type | Always present | Description |
|---|---|---|---|
enabled | boolean | yes | Whether payments can be used in the current mode. |
mode | 'connect' | 'own_key' | yes | connect: payments run on the owner's Stripe account connected through Totalum. own_key: the app uses its own Stripe keys. |
accountId | string | null | yes | The connected Stripe account id (acct_…), or null before the first connection. |
livemode | boolean | yes | Whether this is the live (real money) account; false on the preview and in the sandbox. |
chargesEnabled | boolean | yes | Whether the account can take payments now. |
payoutsEnabled | boolean | yes | Whether Stripe can pay out to the owner's bank account. |
detailsSubmitted | boolean | yes | Whether the owner finished Stripe's onboarding form. |
requirements | { currentlyDue, pastDue, eventuallyDue } | yes | What Stripe still needs from the owner. |
requirements.currentlyDue | string[] | yes | Information Stripe needs now to keep the account working. |
requirements.pastDue | string[] | yes | Information that is overdue: charges or payouts may be paused until it is given. |
requirements.eventuallyDue | string[] | yes | Information Stripe will need later. |
liveEnabled | boolean | yes | Whether the owner switched live payments on for the published site. |
disconnectedAt | string | null | no | When the owner disconnected the Stripe account, or null. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
PAYMENTS_NOT_ENABLED | 403 | Payments are not configured for this environment. |
Example
const status = await totalum.payments.getStatus();
if (!status.chargesEnabled) return <p>Payments are not set up yet.</p>;Result:
{
"enabled": true,
"mode": "connect",
"accountId": "acct_1QZ3kPQm4n8Xa2Lb",
"livemode": true,
"chargesEnabled": true,
"payoutsEnabled": true,
"detailsSubmitted": true,
"requirements": { "currentlyDue": [], "pastDue": [], "eventuallyDue": [] },
"liveEnabled": true,
"disconnectedAt": null
}#payments.onboardingLink
payments.onboardingLink(input: OnboardingLinkInput): Promise<OnboardingLinkOutput>A single-use link to Stripe's hosted onboarding, where the project owner connects (or updates) the Stripe account the app's payments go to. Only for the owner: the coding agent's sandbox key is refused. The live account can only be connected once the project is published.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
input | OnboardingLinkInput | yes | Where Stripe sends the owner back, and the account's country. |
input.returnUrl | string | yes | Where Stripe sends the owner after onboarding. Allowed: a URL. |
input.refreshUrl | string | yes | Where Stripe sends the owner if the link expired or was already used; make a new link there. Allowed: a URL. |
input.type | 'account_onboarding' | 'account_update' | no | account_onboarding (default) to connect the account, account_update to change its details. |
input.country | string | no | The two-letter country of the owner's business (ES, US); required the first time the live account is created. |
Returns Promise<OnboardingLinkOutput> — The onboarding URL to redirect the owner to, and when it expires.
| Field | Type | Always present | Description |
|---|---|---|---|
url | string | yes | The Stripe onboarding page to redirect the owner to (single use). |
expiresAt | string | yes | When the link stops working. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
PAYMENTS_PUBLISH_FIRST | 409 | The project is not published yet: publish it, then connect Stripe. |
MISSING_INPUT | 400 | country is needed to create the live Stripe account. |
FORBIDDEN_SCOPE | 403 | Called with the agent's sandbox key: onboarding links are for the project owner. |
STRIPE_ERROR | 400 | Stripe refused the request; details.stripeCode says why. |
Example
const { url } = await totalum.payments.onboardingLink({
returnUrl: 'https://acme.totalum.app/admin/payments?connected=1',
refreshUrl: 'https://acme.totalum.app/admin/payments',
country: 'ES',
});Result:
{ "url": "https://connect.stripe.com/setup/e/acct_1QZ3kPQm4n8Xa2Lb/Xq3VbD9yPpLk", "expiresAt": "2026-10-11T18:05:00.000Z" }#payments.createCheckout
payments.createCheckout(input: CheckoutInput): Promise<CheckoutOutput>Creates a Stripe Checkout page for a one-off payment (payment), a subscription (subscription) or saving a card
for later (setup), and returns its URL: redirect the buyer there. Put your own ids (order, user) in metadata
to find them again on the return page or in the webhook. A subscription needs a recurring price: an inline item
with recurring: { interval: 'month' }, or a priceId; its metadata is also set on the subscription.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
input | CheckoutInput | yes | What is sold, the mode, and where Stripe sends the buyer afterwards. |
input.mode | 'payment' | 'subscription' | 'setup' | yes | payment: a one-off payment. subscription: a recurring payment (needs a recurring price). setup: save a card without charging. |
input.successUrl | string | yes | Where Stripe sends the buyer after paying. Add ?session_id={CHECKOUT_SESSION_ID} to read the session back with getCheckout(). Allowed: a URL. |
input.cancelUrl | string | yes | Where Stripe sends the buyer if they go back without paying. Allowed: a URL. |
input.lineItems | ({ priceId, quantity } | { name, unitAmount, currency, quantity, recurring? })[] | no | What is sold: each item is either a Stripe priceId with a quantity, or an inline item (name, unitAmount, currency, quantity, optional recurring). Required for payment and subscription. Allowed: at least 1 item. |
input.lineItems.priceId | string | no | A Price id (price_…) of the connected Stripe account, instead of an inline item. Allowed: at least 1 character. |
input.lineItems.quantity | number | yes | How many units. Allowed: > 0. |
input.lineItems.name | string | no | The product name shown on the Checkout page (inline item). Allowed: at least 1 character. |
input.lineItems.unitAmount | number | no | The price of one unit in the smallest currency unit: cents for EUR/USD (1900 = 19.00). Allowed: > 0. |
input.lineItems.currency | string | no | Three-letter ISO currency code, lower case (eur, usd). Allowed: exactly 3 characters. |
input.lineItems.recurring | { interval, intervalCount? } | no | Makes the inline item a subscription price: how often it renews. |
input.lineItems.recurring.interval | 'day' | 'month' | 'week' | 'year' | yes | The billing period: day, week, month or year. |
input.lineItems.recurring.intervalCount | number | no | How many periods between charges (3 with month = quarterly). Default 1. Allowed: > 0. |
input.currency | string | no | The currency of a setup session (three-letter code, lower case). Allowed: exactly 3 characters. |
input.customerEmail | string | no | Pre-fills the buyer's e-mail on the Checkout page. Allowed: an e-mail address. |
input.customerId | string | no | An existing Stripe customer (cus_…) to charge, so their saved cards appear. |
input.metadata | Record<string, string> | no | Your own key/value strings (order id, user id). Returned by getCheckout(), in the webhook events and, for a subscription, set on the subscription. |
input.allowPromotionCodes | boolean | no | true shows a promotion-code field. |
input.automaticTax | boolean | no | true lets Stripe Tax compute tax (the owner must have Stripe Tax set up). |
input.trialDays | number | no | Free trial days before the first charge of a subscription. Allowed: > 0. |
input.clientReferenceId | string | no | One more id of yours, returned by getCheckout(). |
Returns Promise<CheckoutOutput> — The Checkout Session id and the URL to redirect the buyer to.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The Checkout Session id (cs_test_… or cs_live_…). |
url | string | yes | The Stripe Checkout page: redirect the buyer here. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
CARD_DATA_NOT_ACCEPTED | 400 | The request contained card fields: card data is only ever typed on Stripe's page. |
PAYMENTS_LIVE_ONBOARDING_REQUIRED | 403 | Live mode, but the owner has not finished Stripe onboarding. |
PAYMENTS_ACCOUNT_DISCONNECTED | 403 | The owner disconnected the Stripe account: they must connect it again. |
PAYMENTS_NOT_ENABLED | 403 | Payments are not configured for this environment. |
STRIPE_ERROR | 400 | Stripe refused the session (an unknown priceId, a bad currency); details.stripeCode. |
STRIPE_RATE_LIMITED | 429 | Stripe's own rate limit; retry shortly. |
Examples
// src/app/api/checkout/route.ts
const { url } = await totalum.payments.createCheckout({
mode: 'payment',
lineItems: [{ name: 'Concert ticket', unitAmount: 2500, currency: 'eur', quantity: 2 }],
successUrl: 'https://acme.totalum.app/orders/ord_42?session_id={CHECKOUT_SESSION_ID}',
cancelUrl: 'https://acme.totalum.app/cart',
customerEmail: 'ana@example.com',
metadata: { orderId: 'ord_42' },
});
return Response.redirect(url, 303);Result:
{ "id": "cs_test_a1B2c3D4e5F6g7H8i9J0", "url": "https://checkout.stripe.com/c/pay/cs_test_a1B2c3D4e5F6g7H8i9J0#fid…" }// a monthly subscription with a 14-day trial
const { url } = await totalum.payments.createCheckout({
mode: 'subscription',
lineItems: [{ name: 'Pro', unitAmount: 1900, currency: 'eur', quantity: 1, recurring: { interval: 'month' } }],
trialDays: 14,
successUrl: 'https://acme.totalum.app/billing?session_id={CHECKOUT_SESSION_ID}',
cancelUrl: 'https://acme.totalum.app/pricing',
metadata: { userId: user.id },
});#payments.getCheckout
payments.getCheckout(sessionId: string): Promise<CheckoutSession>Reads a Checkout Session back, to confirm a payment on the page Stripe returns the buyer to (successUrl with
?session_id={CHECKOUT_SESSION_ID}): when paymentStatus is 'paid', mark the order paid — no webhook needed. A
subscription checkout also gives subscriptionId and customerId: store both on the member.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
sessionId | string | yes | The session_id Stripe added to successUrl (cs_test_… or cs_live_…). |
Returns Promise<CheckoutSession> — The session's status, amount, your metadata and the ids it created.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The Checkout Session id. |
status | 'expired' | 'open' | 'complete' | yes | open: not finished. complete: the buyer finished. expired: abandoned (sessions expire after 24 h). |
paymentStatus | 'paid' | 'unpaid' | 'no_payment_required' | yes | paid: the money was taken (mark the order paid). unpaid: not yet (some payment methods confirm later). no_payment_required: nothing to pay (a setup session or a 100 % discount). |
amountTotal | number | null | yes | The total charged, in the smallest currency unit (5000 = 50.00). |
currency | string | null | yes | The currency, lower case. |
metadata | Record<string, string> | yes | The metadata you passed to createCheckout(). |
paymentIntentId | string | null | yes | The payment (pi_…) of a payment session; pass it to refunds.create(). |
customerEmail | string | null | yes | The e-mail the buyer typed. |
clientReferenceId | string | null | yes | The clientReferenceId you passed. |
customerId | string | null | yes | The Stripe customer (cus_…) of a subscription or saved card; store it for customerPortalLink(). |
subscriptionId | string | null | yes | The subscription (sub_…) a subscription session created; store it to check access later. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
VALIDATION_ERROR | 400 | Not a Checkout Session id. |
STRIPE_ERROR | 404 | No such session on this project's Stripe account. |
Example
// src/app/orders/[id]/page.tsx
const session = await totalum.payments.getCheckout(searchParams.session_id);
if (session.paymentStatus === 'paid') await markOrderPaid(session.metadata.orderId);Result:
{
"id": "cs_test_a1B2c3D4e5F6g7H8i9J0",
"status": "complete",
"paymentStatus": "paid",
"amountTotal": 5000,
"currency": "eur",
"metadata": { "orderId": "ord_42" },
"paymentIntentId": "pi_3QZ4mNPQm4n8Xa2L0x1y2z3a",
"customerEmail": "ana@example.com",
"clientReferenceId": null,
"customerId": null,
"subscriptionId": null
}#payments.customerPortalLink
payments.customerPortalLink(input: CustomerPortalLinkInput): Promise<CustomerPortalLinkOutput>A link to Stripe's hosted customer portal, where a subscriber updates their card, downloads invoices or cancels their subscription. Show it as "Manage billing".
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
input | CustomerPortalLinkInput | yes | The Stripe customer and where to send them back. |
input.customerId | string | yes | The Stripe customer (cus_…), from getCheckout(). Allowed: at least 1 character. |
input.returnUrl | string | yes | Where the portal's back link goes. Allowed: a URL. |
Returns Promise<CustomerPortalLinkOutput> — The portal URL to redirect the customer to.
| Field | Type | Always present | Description |
|---|---|---|---|
url | string | yes | The portal page to redirect the customer to. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
STRIPE_ERROR | 404 | No such customer on this project's Stripe account. |
PAYMENTS_LIVE_ONBOARDING_REQUIRED | 403 | Live mode, but the owner has not finished Stripe onboarding. |
Example
const { url } = await totalum.payments.customerPortalLink({
customerId: member.stripeCustomerId,
returnUrl: 'https://acme.totalum.app/account',
});Result:
{ "url": "https://billing.stripe.com/p/session/test_YWNjdF8xUVozaw…" }#payments.subscriptions.get
payments.subscriptions.get(subscriptionId: string): Promise<Subscription>Reads a subscription: give access while status is 'active' or 'trialing'; currentPeriodEnd is the next
renewal (or the end of access when cancelAtPeriodEnd is true).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
subscriptionId | string | yes | The subscription id (sub_…) from getCheckout() or a payment webhook. |
Returns Promise<Subscription> — The subscription's status, renewal date and metadata.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The subscription id (sub_…). |
status | 'unpaid' | 'active' | 'paused' | 'incomplete' | 'incomplete_expired' | 'trialing' | 'past_due' | 'canceled' | yes | Give access while active or trialing. past_due/unpaid: a renewal failed. canceled: ended. incomplete/incomplete_expired: the first payment did not go through. paused: a trial ended without a payment method. |
customerId | string | yes | The Stripe customer (cus_…). |
cancelAtPeriodEnd | boolean | yes | true when it ends at currentPeriodEnd instead of renewing. |
currentPeriodEnd | string | null | yes | When the paid period ends: the next renewal, or the end of access when cancelAtPeriodEnd. |
canceledAt | string | null | yes | When it was canceled, or null. |
metadata | Record<string, string> | yes | The metadata given to the Checkout that created it. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
VALIDATION_ERROR | 400 | Not a subscription id. |
STRIPE_ERROR | 404 | No such subscription on this project's Stripe account. |
Example
const sub = await totalum.payments.subscriptions.get(member.subscriptionId);
const active = sub.status === 'active' || sub.status === 'trialing';Result:
{
"id": "sub_1QZ5aBPQm4n8Xa2LcD3eF4gH",
"status": "active",
"customerId": "cus_R2x9YpL0aBcDeF",
"cancelAtPeriodEnd": false,
"currentPeriodEnd": "2026-11-11T17:40:00.000Z",
"canceledAt": null,
"metadata": { "userId": "user_42" }
}#payments.subscriptions.update
payments.subscriptions.update(subscriptionId: string, input: SubscriptionUpdateInput): Promise<Subscription>Cancels a subscription at the end of the paid period (cancelAtPeriodEnd: true, the usual "Cancel" button: the
member keeps access until then), or undoes that (false).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
subscriptionId | string | yes | The subscription id (sub_…). |
input | SubscriptionUpdateInput | yes | Whether it ends at the end of the period. |
input.cancelAtPeriodEnd | boolean | yes | true: end the subscription when the paid period ends. false: keep renewing (undo a cancellation). |
Returns Promise<Subscription> — The updated subscription.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The subscription id (sub_…). |
status | 'unpaid' | 'active' | 'paused' | 'incomplete' | 'incomplete_expired' | 'trialing' | 'past_due' | 'canceled' | yes | Give access while active or trialing. past_due/unpaid: a renewal failed. canceled: ended. incomplete/incomplete_expired: the first payment did not go through. paused: a trial ended without a payment method. |
customerId | string | yes | The Stripe customer (cus_…). |
cancelAtPeriodEnd | boolean | yes | true when it ends at currentPeriodEnd instead of renewing. |
currentPeriodEnd | string | null | yes | When the paid period ends: the next renewal, or the end of access when cancelAtPeriodEnd. |
canceledAt | string | null | yes | When it was canceled, or null. |
metadata | Record<string, string> | yes | The metadata given to the Checkout that created it. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
VALIDATION_ERROR | 400 | Not a subscription id. |
STRIPE_ERROR | 404 | No such subscription on this project's Stripe account. |
Example
const sub = await totalum.payments.subscriptions.update(member.subscriptionId, { cancelAtPeriodEnd: true });Result:
{
"id": "sub_1QZ5aBPQm4n8Xa2LcD3eF4gH",
"status": "active",
"customerId": "cus_R2x9YpL0aBcDeF",
"cancelAtPeriodEnd": true,
"currentPeriodEnd": "2026-11-11T17:40:00.000Z",
"canceledAt": null,
"metadata": { "userId": "user_42" }
}#payments.subscriptions.cancel
payments.subscriptions.cancel(subscriptionId: string): Promise<Subscription>Cancels a subscription immediately: no more charges, status: 'canceled' at once, access ends now. To let the
member keep the period they paid for, use update(id, { cancelAtPeriodEnd: true }) instead.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
subscriptionId | string | yes | The subscription id (sub_…). |
Returns Promise<Subscription> — The canceled subscription.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The subscription id (sub_…). |
status | 'unpaid' | 'active' | 'paused' | 'incomplete' | 'incomplete_expired' | 'trialing' | 'past_due' | 'canceled' | yes | Give access while active or trialing. past_due/unpaid: a renewal failed. canceled: ended. incomplete/incomplete_expired: the first payment did not go through. paused: a trial ended without a payment method. |
customerId | string | yes | The Stripe customer (cus_…). |
cancelAtPeriodEnd | boolean | yes | true when it ends at currentPeriodEnd instead of renewing. |
currentPeriodEnd | string | null | yes | When the paid period ends: the next renewal, or the end of access when cancelAtPeriodEnd. |
canceledAt | string | null | yes | When it was canceled, or null. |
metadata | Record<string, string> | yes | The metadata given to the Checkout that created it. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
VALIDATION_ERROR | 400 | Not a subscription id. |
STRIPE_ERROR | 404 | No such subscription on this project's Stripe account. |
Example
const sub = await totalum.payments.subscriptions.cancel('sub_1QZ5aBPQm4n8Xa2LcD3eF4gH');Result:
{
"id": "sub_1QZ5aBPQm4n8Xa2LcD3eF4gH",
"status": "canceled",
"customerId": "cus_R2x9YpL0aBcDeF",
"cancelAtPeriodEnd": false,
"currentPeriodEnd": "2026-11-11T17:40:00.000Z",
"canceledAt": "2026-10-11T18:02:11.000Z",
"metadata": { "userId": "user_42" }
}#payments.refunds.create
payments.refunds.create(input: RefundInput): Promise<RefundOutput>Refunds a payment, in full or in part. The buyer gets the money back on their card in a few days.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
input | RefundInput | yes | The payment to refund and, for a partial refund, how much. |
input.paymentIntentId | string | yes | The payment to refund (pi_…), from getCheckout() or a payment webhook. Allowed: at least 1 character. |
input.amount | number | no | For a partial refund, the amount in the smallest currency unit. Default: everything not yet refunded. Allowed: > 0. |
input.reason | 'duplicate' | 'fraudulent' | 'requested_by_customer' | no | Why, as Stripe records it: duplicate, fraudulent or requested_by_customer. |
Returns Promise<RefundOutput> — The refund's id, status and amount.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The refund id (re_…). |
status | string | yes | Stripe's status: succeeded, pending, requires_action, failed or canceled. |
amount | number | yes | The refunded amount in the smallest currency unit. |
currency | string | yes | The currency, lower case. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
STRIPE_ERROR | 400 | Stripe refused it (already refunded, an amount above what is left); details.stripeCode. |
PAYMENTS_LIVE_ONBOARDING_REQUIRED | 403 | Live mode, but the owner has not finished Stripe onboarding. |
Example
const refund = await totalum.payments.refunds.create({
paymentIntentId: 'pi_3QZ4mNPQm4n8Xa2L0x1y2z3a',
amount: 2500,
reason: 'requested_by_customer',
});Result:
{ "id": "re_3QZ6bCPQm4n8Xa2L1k2l3m4n", "status": "succeeded", "amount": 2500, "currency": "eur" }#CheckoutInput
What a Checkout page sells and where it sends the buyer, for payments.createCheckout().
| Name | Type | Required | Description |
|---|---|---|---|
mode | 'payment' | 'subscription' | 'setup' | yes | payment: a one-off payment. subscription: a recurring payment (needs a recurring price). setup: save a card without charging. |
successUrl | string | yes | Where Stripe sends the buyer after paying. Add ?session_id={CHECKOUT_SESSION_ID} to read the session back with getCheckout(). Allowed: a URL. |
cancelUrl | string | yes | Where Stripe sends the buyer if they go back without paying. Allowed: a URL. |
lineItems | ({ priceId, quantity } | { name, unitAmount, currency, quantity, recurring? })[] | no | What is sold: each item is either a Stripe priceId with a quantity, or an inline item (name, unitAmount, currency, quantity, optional recurring). Required for payment and subscription. Allowed: at least 1 item. |
lineItems.priceId | string | no | A Price id (price_…) of the connected Stripe account, instead of an inline item. Allowed: at least 1 character. |
lineItems.quantity | number | yes | How many units. Allowed: > 0. |
lineItems.name | string | no | The product name shown on the Checkout page (inline item). Allowed: at least 1 character. |
lineItems.unitAmount | number | no | The price of one unit in the smallest currency unit: cents for EUR/USD (1900 = 19.00). Allowed: > 0. |
lineItems.currency | string | no | Three-letter ISO currency code, lower case (eur, usd). Allowed: exactly 3 characters. |
lineItems.recurring | { interval, intervalCount? } | no | Makes the inline item a subscription price: how often it renews. |
lineItems.recurring.interval | 'day' | 'month' | 'week' | 'year' | yes | The billing period: day, week, month or year. |
lineItems.recurring.intervalCount | number | no | How many periods between charges (3 with month = quarterly). Default 1. Allowed: > 0. |
currency | string | no | The currency of a setup session (three-letter code, lower case). Allowed: exactly 3 characters. |
customerEmail | string | no | Pre-fills the buyer's e-mail on the Checkout page. Allowed: an e-mail address. |
customerId | string | no | An existing Stripe customer (cus_…) to charge, so their saved cards appear. |
metadata | Record<string, string> | no | Your own key/value strings (order id, user id). Returned by getCheckout(), in the webhook events and, for a subscription, set on the subscription. |
allowPromotionCodes | boolean | no | true shows a promotion-code field. |
automaticTax | boolean | no | true lets Stripe Tax compute tax (the owner must have Stripe Tax set up). |
trialDays | number | no | Free trial days before the first charge of a subscription. Allowed: > 0. |
clientReferenceId | string | no | One more id of yours, returned by getCheckout(). |
#CheckoutOutput
The Checkout page created by payments.createCheckout().
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The Checkout Session id (cs_test_… or cs_live_…). |
url | string | yes | The Stripe Checkout page: redirect the buyer here. |
#CheckoutSession
A Checkout Session read back with payments.getCheckout().
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The Checkout Session id. |
status | 'expired' | 'open' | 'complete' | yes | open: not finished. complete: the buyer finished. expired: abandoned (sessions expire after 24 h). |
paymentStatus | 'paid' | 'unpaid' | 'no_payment_required' | yes | paid: the money was taken (mark the order paid). unpaid: not yet (some payment methods confirm later). no_payment_required: nothing to pay (a setup session or a 100 % discount). |
amountTotal | number | null | yes | The total charged, in the smallest currency unit (5000 = 50.00). |
currency | string | null | yes | The currency, lower case. |
metadata | Record<string, string> | yes | The metadata you passed to createCheckout(). |
paymentIntentId | string | null | yes | The payment (pi_…) of a payment session; pass it to refunds.create(). |
customerEmail | string | null | yes | The e-mail the buyer typed. |
clientReferenceId | string | null | yes | The clientReferenceId you passed. |
customerId | string | null | yes | The Stripe customer (cus_…) of a subscription or saved card; store it for customerPortalLink(). |
subscriptionId | string | null | yes | The subscription (sub_…) a subscription session created; store it to check access later. |
#CustomerPortalLinkInput
The input of payments.customerPortalLink().
| Name | Type | Required | Description |
|---|---|---|---|
customerId | string | yes | The Stripe customer (cus_…), from getCheckout(). Allowed: at least 1 character. |
returnUrl | string | yes | Where the portal's back link goes. Allowed: a URL. |
#CustomerPortalLinkOutput
The answer of payments.customerPortalLink().
| Field | Type | Always present | Description |
|---|---|---|---|
url | string | yes | The portal page to redirect the customer to. |
#OnboardingLinkInput
The input of payments.onboardingLink().
| Name | Type | Required | Description |
|---|---|---|---|
returnUrl | string | yes | Where Stripe sends the owner after onboarding. Allowed: a URL. |
refreshUrl | string | yes | Where Stripe sends the owner if the link expired or was already used; make a new link there. Allowed: a URL. |
type | 'account_onboarding' | 'account_update' | no | account_onboarding (default) to connect the account, account_update to change its details. |
country | string | no | The two-letter country of the owner's business (ES, US); required the first time the live account is created. |
#OnboardingLinkOutput
The answer of payments.onboardingLink().
| Field | Type | Always present | Description |
|---|---|---|---|
url | string | yes | The Stripe onboarding page to redirect the owner to (single use). |
expiresAt | string | yes | When the link stops working. |
#PaymentsStatus
The state of the project's Stripe account, from payments.getStatus().
| Field | Type | Always present | Description |
|---|---|---|---|
enabled | boolean | yes | Whether payments can be used in the current mode. |
mode | 'connect' | 'own_key' | yes | connect: payments run on the owner's Stripe account connected through Totalum. own_key: the app uses its own Stripe keys. |
accountId | string | null | yes | The connected Stripe account id (acct_…), or null before the first connection. |
livemode | boolean | yes | Whether this is the live (real money) account; false on the preview and in the sandbox. |
chargesEnabled | boolean | yes | Whether the account can take payments now. |
payoutsEnabled | boolean | yes | Whether Stripe can pay out to the owner's bank account. |
detailsSubmitted | boolean | yes | Whether the owner finished Stripe's onboarding form. |
requirements | { currentlyDue, pastDue, eventuallyDue } | yes | What Stripe still needs from the owner. |
requirements.currentlyDue | string[] | yes | Information Stripe needs now to keep the account working. |
requirements.pastDue | string[] | yes | Information that is overdue: charges or payouts may be paused until it is given. |
requirements.eventuallyDue | string[] | yes | Information Stripe will need later. |
liveEnabled | boolean | yes | Whether the owner switched live payments on for the published site. |
disconnectedAt | string | null | no | When the owner disconnected the Stripe account, or null. |
#RefundInput
The input of payments.refunds.create().
| Name | Type | Required | Description |
|---|---|---|---|
paymentIntentId | string | yes | The payment to refund (pi_…), from getCheckout() or a payment webhook. Allowed: at least 1 character. |
amount | number | no | For a partial refund, the amount in the smallest currency unit. Default: everything not yet refunded. Allowed: > 0. |
reason | 'duplicate' | 'fraudulent' | 'requested_by_customer' | no | Why, as Stripe records it: duplicate, fraudulent or requested_by_customer. |
#RefundOutput
A refund, from payments.refunds.create().
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The refund id (re_…). |
status | string | yes | Stripe's status: succeeded, pending, requires_action, failed or canceled. |
amount | number | yes | The refunded amount in the smallest currency unit. |
currency | string | yes | The currency, lower case. |
#Subscription
A Stripe subscription, from payments.subscriptions.*.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The subscription id (sub_…). |
status | 'unpaid' | 'active' | 'paused' | 'incomplete' | 'incomplete_expired' | 'trialing' | 'past_due' | 'canceled' | yes | Give access while active or trialing. past_due/unpaid: a renewal failed. canceled: ended. incomplete/incomplete_expired: the first payment did not go through. paused: a trial ended without a payment method. |
customerId | string | yes | The Stripe customer (cus_…). |
cancelAtPeriodEnd | boolean | yes | true when it ends at currentPeriodEnd instead of renewing. |
currentPeriodEnd | string | null | yes | When the paid period ends: the next renewal, or the end of access when cancelAtPeriodEnd. |
canceledAt | string | null | yes | When it was canceled, or null. |
metadata | Record<string, string> | yes | The metadata given to the Checkout that created it. |
#SubscriptionUpdateInput
The change payments.subscriptions.update() makes.
| Name | Type | Required | Description |
|---|---|---|---|
cancelAtPeriodEnd | boolean | yes | true: end the subscription when the paid period ends. false: keep renewing (undo a cancellation). |
