Skip to Content
BillingStripe

Stripe Billing

PickLayer’s first-party payment processor is Stripe. End-user subscriptions, payment-method management, overage credits, and webhook-driven renewals all run through Stripe. For the cross-cutting usage and quota model that backs all funding sources, see the billing overview.

For iOS in-app purchases (where Stripe is not permitted), use the Apple App Store integration instead.

Billing API

PickLayer provides end-user billing endpoints for subscription management. All billing routes require an end-user JWT.

List available plans

GET /v1/billing/plans

Returns all billing plans available for the application:

{ "code": 200, "type": "success", "data": { "plans": [ { "id": "plan-uuid", "name": "Pro Plan", "description": "Unlimited access", "price_cents": 2999, "currency": "usd", "interval": "month", "monthly_cost_cap": 100, "fundingModes": [ { "id": "fm-uuid", "type": "USER", "stripePrice": "price_xxx" } ] } ] } }

Subscribe to a plan

POST /v1/billing/subscribe

{ "planId": "plan-uuid", "fundingModeId": "fm-uuid", "paymentMethodToken": "tok_visa" }
  • paymentMethodToken or paymentMethodId attaches the card and starts the subscription immediately
  • On success, creates a Stripe subscription and activates the user’s plan assignment
  • Returns subscription details and payment intent info if SCA is required

Check billing readiness

GET /v1/billing/readiness

Returns the user’s current billing status for UI display:

{ "code": 200, "type": "success", "data": { "funded": true, "percentRemaining": 85.5, "billingModel": "USER", "reason": null, "plan": { "id": "...", "name": "Pro Plan" }, "next_plan": null, "pending_effective_at": null, "cancel_at": null, "renewal_at": "2026-02-20T00:00:00Z", "subscription": { "status": "active", "pending_payment": false }, "cap_windows": [ { "type": "monthly", "used_cents": 1450, "cap_cents": 10000, "percent_used": 14.5 } ] } }

Possible reason values when funded: false:

  • no_subscription – User has no active subscription
  • subscription_canceled – Subscription was canceled
  • period_inactive – Subscription period expired (may auto-sync from Stripe)
  • funding_exhausted – Usage cap reached
  • payment_failed – Subscription has failed payment

Get billing status (detailed)

GET /v1/billing/status

Builder/system endpoint with additional wallet and Stripe details. End-users should use /billing/readiness.

Change plan (upgrade/downgrade/cancel)

POST /v1/billing/plan-change

{ "action": "upgrade", "targetPlanId": "plan-uuid", "targetFundingModeId": "fm-uuid", "paymentMethodToken": "tok_visa", "prorationBehavior": "create_prorations" }

Actions:

  • upgrade – Switch to a higher-tier plan (immediate)
  • downgrade – Switch to a lower-tier plan (at period end)
  • cancel – Cancel subscription (at period end)
  • reactivate – Undo pending cancellation

Update payment method

POST /v1/billing/payment-method

{ "paymentMethodToken": "tok_visa" }

Replaces the default payment method on the subscription. If the subscription is in past_due or unpaid status (due to a failed renewal payment), automatically attempts to pay the most recent failed invoice with the new payment method. This allows users to fix payment issues in a single call.

Retry failed payment

POST /v1/billing/retry-payment

{}

Retries payment for a failed subscription renewal using the default payment method. Use when subscription shows past_due status.

Response:

{ "code": 200, "type": "success", "data": { "invoiceId": "in_xxx", "status": "paid", "paymentIntent": { "id": "pi_xxx", "status": "succeeded" } } }

Returns 400 if no failed invoice exists.

Sync subscription from Stripe

POST /v1/billing/sync-subscription

{}

Forces a refresh of the local subscription period from Stripe. Use when the local subscription appears expired but Stripe may have renewed it (e.g., missed webhooks).

Response:

{ "code": 200, "type": "success", "data": { "synced": true, "subscriptionId": "sub_xxx" } }

Note: /billing/readiness performs this sync automatically when it detects an expired period.

Overage Credits

Overage credits let users pre-load funds to cover usage beyond their subscription plan’s included allowance. Credits are purchased via Stripe and carry a 10% platform fee at load time.

Load credits

POST /v1/billing/overage-credits/load

{ "amount_cents": 1000, "use_saved_payment_method": true, "force_checkout": false, "success_url": "https://yourapp.com/billing/success", "cancel_url": "https://yourapp.com/billing/cancel" }
  • amount_cents: Amount to load, in cents. Range: $5–$500 ($500–$50,000 cents).
  • use_saved_payment_method: Charge the user’s saved card immediately if true. If false or no card is saved, a Stripe Checkout session URL is returned.
  • force_checkout: Always redirect to Checkout even if a saved card is present.
  • success_url / cancel_url: Redirect destinations when using Checkout.

A 10% platform fee is applied at load time (e.g., loading $10.00 adds $9.09 of spendable credit).

Get credit balance

GET /v1/billing/overage-credits/balance

{ "overage_credits_pusd": 9.09, "subscription_credits_pusd": 0, "total_available_pusd": 9.09, "overages_enabled": true, "plan_overage_mode": "credits" }
  • overage_credits_pusd: Pre-loaded overage credit balance in platform USD.
  • subscription_credits_pusd: Credits rolled over from the subscription plan.
  • total_available_pusd: Combined spendable balance.
  • overages_enabled: Whether the user has opted into overage spending.
  • plan_overage_mode: How the billing plan handles overages (credits, disabled, etc.).

Toggle overage opt-in

POST /v1/billing/overage-credits/settings

{ "overages_enabled": true }

Enables or disables overage spending for the user. When disabled, requests that exceed the subscription allowance return 429 QUOTA regardless of available credit balance.

Last updated on