Skip to content

Checkout Flow

The checkout endpoint creates a payment session with the configured provider and returns a URL or token for the user to complete payment.

Flow Overview

1. User selects plan    →  POST /api/checkout
2. Server validates     →  Checks plan, coupon, existing subscription
3. Provider session     →  Creates checkout via integration source
4. Response             →  Returns checkoutUrl / clientToken
5. User pays            →  Redirected to provider checkout page
6. Provider webhook     →  POST /api/payments/webhooks/:providerKind
7. Subscription active  →  Status updated, invoice created

API

Initiate Checkout

POST /api/checkout

Body:

json
{
  "planId": "plan-id",
  "billingCycle": "monthly",
  "couponCode": "SAVE20",
  "successUrl": "https://your-app.com/billing/success",
  "cancelUrl": "https://your-app.com/billing"
}
FieldTypeRequiredDescription
planIdstringYesID of the plan to subscribe to
billingCycle"monthly" | "yearly"YesBilling interval
couponCodestringNoDiscount coupon code
successUrlstringNoRedirect URL after successful payment
cancelUrlstringNoRedirect URL if user cancels

Response:

json
{
  "subscriptionId": "sub-123",
  "checkoutUrl": "https://checkout.paddle.com/...",
  "clientToken": null
}

Error Responses

CodeStatusDescription
PLAN_NOT_FOUND404Plan does not exist
PLAN_NOT_ACTIVE400Plan is deactivated
ACTIVE_SUBSCRIPTION_EXISTS409User/workspace already has an active subscription
PAYMENTS_NOT_CONFIGURED500No payment provider configured
MISSING_EXTERNAL_PRICE_ID400Plan lacks provider-specific price IDs
COUPON_*400Various coupon validation errors

Webhooks

Each provider sends webhooks to confirm payment status:

POST /api/payments/webhooks/:providerKind

Where :providerKind is paddle, paypal, or sumit.

Idempotency

All webhook events are tracked by their external event ID. Duplicate events are safely ignored and return { status: "already_processed" }.

Paddle Events

EventAction
subscription.activatedActivate subscription, set billing period
subscription.canceledCancel subscription
subscription.past_dueMark as past due
transaction.completedCreate invoice
transaction.payment_failedMark as past due

PayPal Events

EventAction
BILLING.SUBSCRIPTION.ACTIVATEDActivate subscription
BILLING.SUBSCRIPTION.CANCELLEDCancel subscription
BILLING.SUBSCRIPTION.EXPIREDMark as expired
PAYMENT.SALE.COMPLETEDCreate invoice
BILLING.SUBSCRIPTION.PAYMENT.FAILEDMark as past due

Sumit Events

EventAction
payment_success / RecurringPaymentChargedActivate subscription + create invoice
payment_failed / RecurringPaymentFailedMark as past due
recurring_canceled / RecurringPaymentCanceledCancel subscription

SDK Usage

typescript
// Start checkout
const { checkoutUrl } = await sdk.payments.checkout({
  planId: 'plan-pro',
  billingCycle: 'yearly',
  couponCode: 'WELCOME',
});

// Redirect user
window.location.href = checkoutUrl;

// After redirect back, check subscription status
const subscription = await sdk.payments.getMySubscription();

Cancel Subscription

typescript
await sdk.payments.cancelSubscription('subscription-id');

This cancels the subscription both locally and at the payment provider. For Paddle, cancellation takes effect at the end of the current billing period.

Build SaaS Products Without Limits.