We compare Stripe, Polar and Lemon Squeezy for the web, and the App Store and Google Play for mobile later. The page covers every money lifecycle (purchase, refund, promotion, pay-as-you-go and grace period), the billing problems we will run into, and the tax and finance work that follows.
With a processor (Stripe direct), PAM is the legal seller and every tax, invoice and dispute is our job. With a merchant of record (Polar, Lemon Squeezy, Paddle, Stripe Managed Payments, the App Store, Google Play), the platform resells PAM to the customer. It carries that liability, and its higher fee pays for it.
Dark cells are jobs PAM owns. Light cells are jobs the platform owns.
The deepest API: Billing, meters, credit grants, Tax, Invoicing, Radar and Smart Retries. It acquired Metronome (usage billing) in early 2026.
Stripe's own merchant of record, sold through Link. API support landed on 22 April 2026 (managed_payments on Checkout Sessions).
An open-source, developer-first MoR. Usage billing is built in (events → meters → metered prices), and it has credit "benefits".
Stripe acquired it in 2024. The entity was renamed "Sold through Link, LLC", and merchants are migrating to Managed Payments.
Not on the shortlist, but it's the benchmark MoR for B2B SaaS. Strong invoicing, B2B tax IDs and enterprise contracts.
Required for digital credits bought and used inside a native app. Both stores handle VAT and GST worldwide.
A 50¢ fixed fee is 10% of a $5 top-up and 0.25% of a $200 one. PAM's smallest pack should be priced so the fixed fee stays under about 2%, which means $20 or more.
US domestic card, before tax and FX. Hover to read exact values.
Stripe direct includes Billing (0.7%) and Tax (0.5%). Polar Scale excludes its $400/mo plan fee. App stores take a flat percentage, so they don't fall as the ticket grows.
A German consumer buys a $100 credit pack, VAT (19%) included, paying with a non-US card. Hover any segment.
Stripe uses US-entity reference pricing; a Singapore Stripe account prices differently, at 3.4% + S$0.50 domestic. VAT is the same tax in every row. The difference is who files it: with Stripe direct, PAM registers for non-Union OSS from the first euro, because the €10k threshold only applies to sellers established in the EU.
PAM already meters cost from the gateway response (credits = reported_cost_usd × 1,000) and decides refunds and grants in the CMS. Payment providers only turn money into grants on that ledger and never decide balances themselves. That way, adding iOS later is a new adapter, not a rewrite.
interface PaymentProvider {
createCheckout(org, sku, opts)
createPortalSession(org)
verifyWebhook(req): RawEvent
normalize(raw): BillingEvent[]
refund(paymentId, amount?)
cancel(subId, atPeriodEnd)
}
Each event carries provider, provider_event_id (the idempotency key), occurred_at, the amount in minor units, the currency, and the tax split.
(provider, event_id).Each lifecycle is a state machine. Dark nodes are where the ledger changes; red nodes are failures we must handle. The tables underneath map each step to the real webhook per provider.
For example, 25,000 credits for $25. Credits are granted only from the webhook, never from the success redirect.
| Step | Stripe | Polar | Lemon Squeezy |
|---|---|---|---|
| Checkout done | checkout.session.completed | checkout.updated (succeeded) | — |
| Paid → grant | checkout.session.completed with payment_status=paid | order.paid | order_created (status paid) |
| Async method settles | checkout.session.async_payment_succeeded | order.paid | order_created |
| Async method fails | …async_payment_failed | — | — |
| Abandoned | checkout.session.expired (24h) | checkout expires | — |
requires_action, and the user can close the tab partway through.For example, Team at $5,000/month for 5.5M credits. Each renewal grants a fresh allowance with an expiry; purchased top-ups never expire.
| State | Stripe | Polar | Lemon Squeezy |
|---|---|---|---|
| Trial / start | customer.subscription.created (trialing) | subscription.created | subscription_created |
| Active | invoice.paid | subscription.active | subscription_payment_success |
| Renewal | invoice.paid (billing_reason=subscription_cycle) | order.paid (cycle) | subscription_payment_success |
| Past due | invoice.payment_failed | subscription.past_due | subscription_payment_failed |
| Recovered | invoice.paid | subscription.active | subscription_payment_recovered |
| Plan change | customer.subscription.updated | subscription.updated | subscription_updated |
| Cancel at end | …updated (cancel_at_period_end) | subscription.canceled | subscription_cancelled |
| Ended | customer.subscription.deleted | subscription.revoked | subscription_expired |
Every generation first holds its estimated cost, then settles to the reported cost and releases the rest. When the balance drops below the threshold, we charge the saved card.
Illustrative: threshold 10k credits, top-up 40k. From day 19 the saved card is declined at the next top-up, so the wallet drains and generation stops (on_empty: stop).
| Prepaid | Postpaid | |
|---|---|---|
| Bad-debt risk | None | Full month |
| Cash timing | Upfront | +30 days |
| Revenue | Deferred until burn | On invoice |
| Friction | Top-up step | None |
| Fits | Self-serve | Enterprise contract |
Postpaid only for orgs with a signed contract and a credit limit, invoiced through Stripe Invoicing or bank transfer.
Video and image jobs run for minutes. Reserve the estimate at queue time. Concurrent jobs then can't push the balance negative, and a failed job releases its hold, so failed work is never charged.
At most N top-ups a day, a monthly cap the owner sets, and an email on every charge. Use an off-session PaymentIntent with the saved mandate; SCA can still demand the customer, so fall back to an email link.
Polar meters bill usage on the next invoice (postpaid) and never block. For prepaid, sell top-up packs as one-off products and keep burn in PAM. Use Polar meters only for reporting.
Access steps down gradually. Purchased credits remain the customer's at every step, because they paid for them; only the plan allowance is paused.
| Platform | Grace | Then |
|---|---|---|
| Stripe | Smart Retries, configurable 1–4 week window | We choose: cancel, mark unpaid, or leave past_due |
| Polar | Its own retries → past_due | subscription.revoked |
| Apple | Billing grace period of 3, 16 or 28 days (opt-in) | Billing retry up to 60 days → EXPIRED |
| Google Play | Configurable grace period | Account hold → EXPIRED |
A cross-platform org can be "active" on web and "in hold" on iOS at the same time. Entitlement is the union of active sources.
Refunds are decided only in the CMS, never by customer owners. The credit clawback is computed from the ledger: unused credits are refunded pro rata, and consumed credits are not refundable.
| Step | Stripe | Polar | Apple / Google |
|---|---|---|---|
| Refund issued | charge.refunded, refund.updated | refund.created, order.refunded | REFUND · Voided Purchases API |
| Refund failed | refund.failed | — | — |
| Dispute opened | charge.dispute.created | handled by Polar | n/a (store refunds) |
| Evidence due | charge.dispute.updated | — | CONSUMPTION_REQUEST |
| Closed | charge.dispute.closed (won/lost) | — | REFUND_REVERSED |
| Early fraud warning | radar.early_fraud_warning.created | — | — |
dispute.opened, put the grant's unused credits on hold. On a lost dispute, debit them; if the balance is already spent, the org goes negative and is blocked.CONSUMPTION_REQUEST within 12 hours with consumption data; it changes the outcome.A money discount lowers what the gateway charges (a coupon). A credit grant adds balance with no money moving. Keep them separate, because accounting treats them differently.
This matches Stripe credit-grant ordering (priority, then expiry, then effective date). Burning free credits first protects paid value and makes refunds simple.
These are the problems every usage-billed AI product runs into, grouped by where they hurt. Filter by area.
Fees are the visible cost. Tax registrations, revenue recognition and reconciliation are where the hours go. With an MoR most of the tax work disappears; with a direct processor it doesn't.
Rates and thresholds come from secondary sources (PwC, EY, advisory blogs). VSF Tax must confirm them before launch.
| Check | When | Catches |
|---|---|---|
| Every provider payment has exactly one grant | Nightly | Missed or duplicate webhooks |
| Every provider refund has a CMS decision | Nightly | Refunds made in a provider dashboard |
| Payout total = sum of balance transactions | Per payout | Fees, reserves, dispute debits |
| Deferred revenue roll-forward balances | Month end | Ledger bugs, expiry jobs that didn't run |
Credits used inside the app are digital goods, so Apple guideline 3.1.1 and Google Play policy require in-app purchase. Guideline 3.1.3(b) (multiplatform services) lets a user spend credits bought on the web inside the app, as long as the same packs can also be bought through IAP.
appAccountToken (Apple) or obfuscatedAccountId (Google) set to the PAM org ID, so a purchase maps to the right org.No gateway. Grants are issued from the CMS against a signed order, then invoice and bank transfer. Build the normalized event table and the hold/settle ledger now, because everything later depends on them.
One adapter (Stripe or Polar), hosted checkout, $20+ packs, grants from webhooks, CMS refunds with clawback, an EU withdrawal checkbox, and a monthly liability export for Finance.
Subscriptions with monthly allowance grants, auto top-up with caps, dunning, a 21-day grace ladder, the customer portal, and promotion codes and grants.
StoreKit 2 and Play Billing consumables through the same adapter, server notifications, a US link-out, and cross-platform entitlement as the union of sources.
1. Entity. Does VSF have, or will it create, a Singapore or US company for PAM's global sales? This one answer settles Stripe or Polar.
2. Smallest pack. $20 keeps fees under 8% on an MoR. $10 would push them to 10%.
3. Expiry. Paid credits never expire and free credits expire in 90 days: confirm with Finance (breakage and escheat).
4. VN domestic market. Do we sell to Vietnamese SMEs in VND with e-invoices? That requires a VN gateway (VNPay or MoMo), a separate adapter.
5. Dispute policy. Auto-refund on early fraud warnings below what amount?
6. Video margin. Prepaid makes video safe from bad debt, but not from the ×4-vs-Higgsfield price gap. That is a separate pricing decision.
Many fee figures come from third-party write-ups, some of them by competitors. Check the provider's own pricing page before signing anything. Fetched 6 November 2026.