v2 Billing

Create checkout (server-side start-checkout)

EP-023-T8 (implemented). Session OR anonymous+email (paywall without login; anonymous → claimable account created_via=billing). The server calls monetize start-checkout with our x-api-key, userMeta.p2m_account_id, success/error/shop URLs on our domain. Returns checkout_url to open in a tab; the provider userId is saved as monetize_customer_id. EP-065-T3: checkout creation is serialized per account (a short lock). A repeat of the SAME plan while one is in flight replays the same checkout_url; a DIFFERENT plan gets 409 checkout_in_progress. Without the lock two concurrent requests both bypassed the provider's active-purchase protection, which is a double charge. EP-062-T4: whether the provider's active-purchase protection is bypassed (ignoreActivePurchase) is decided SERVER-SIDE by comparing the requested price with the account's stored subscription — tier ranks, then billing interval (month < year < lifetime). Only a proven upgrade bypasses it; the same plan, a lower tier, a shorter interval or an unknown stored interval get 409 active_subscription with a message directing the buyer to the billing portal (starting a second checkout would charge them twice). EP-066-T1: the bypass requires an AUTHENTICATED caller (session or device-signed intent_token). An anonymous email checkout never receives it — the account was resolved from a string in the request body, so nothing proves the caller owns that address — and it therefore never takes the per-account lock either. Consequently checkout_in_progress can only be caused by the account's own signed-in requests. EP-066-T7: a subscription that is CANCELLED and merely running out its paid period is a re-subscribe, not a repeat purchase: the checkout is allowed (without the bypass) instead of answering 409 same_plan. trialing is not included — a trial converts at the provider.

post/api/v2/billing/checkout

Request

  • Base URL: https://pdf2md.dev
  • URL: https://pdf2md.dev/api/v2/billing/checkout
  • Auth: one of:
    • API key in cookie p2m_session
    • none

Request body

price_idstring
tier_code'anonymous' | 'free' | 'lifetime' | 'builder' | 'pro' | 'business' | 'enterprise'

EP-069: lifetime is the one-time unlock, ranked BETWEEN free and builder — it raises the interactive limits and carries 600 pages/month, but grants no API keys, hosted MCP, webhooks or batch, so a subscription is genuinely more and its holder may still buy one.

interval'month' | 'year'
emailstring email

for anonymous checkout (if there is no session)

channel'extension' | 'web'

Checkout source for runtime purchase flags (EP-023-T23). Treat absence as web for site/pricing or by the calling client.

trial_daysinteger nullable
upgradeboolean

IGNORED since EP-062-T4 (the server decides from the stored subscription whether ignoreActivePurchase applies). Still accepted so already-published extension builds keep working.

intent_tokenstring

EP-028: device-signed checkout-intent token (from POST /billing/checkout-intent). When valid it names the account to charge (the extension's account), overriding the web session. One-time consume; invalid/expired falls back to session/email.

Response

Checkout created

checkout_urlstring uri required

open in a new tab (hosted by Stripe/Paddle)

Changes

No recorded changes to this operation across all 1 revision of this API.