When a renewal isn’t paid, your plan stays active for a grace period. Pay before it ends (wallet or card) to keep the plan; otherwise the account moves to its fallback plan, usually the free plan.
How to pay (wallet vs card)
Both methods use the same endpoints. Passpayment_method on quote, subscribe, and renew. Defaults to wallet when omitted.
Check what is enabled for the account:
payment_methods is the short list (wallet, card). payment_options adds label, description, available, and unavailable_reason — use description in the UI so merchants see that Card Payment pays the plan directly and is not a wallet deposit. Custom-negotiated plan prices are wallet-only (card checkout always charges the catalog price).
Pay with USD wallet balance
- Fund the wallet — top up or collect pay-ins so the business USD wallet covers
amount_due(see Pay-in / payout integration). - Optional: quote —
GET /v1/business/subscription/quote?plan_id={id}&payment_method=walletto seeamount_due, upgrade credit for unused time, and currency (always USD for standard plans). - Subscribe or change plan —
POST /v1/business/subscription/subscribewithpayment_method: "wallet"andauto_renew: true(default) if you want automatic wallet renewal each period.
- Success — response
data.actionisactivated(new plan or upgrade),renewed(same plan extended), orscheduled(downgrade at period end; nothing charged now).data.subscription.billing_methodiswallet. - Renew early or fix past due —
POST /v1/business/subscription/renewwith{ "payment_method": "wallet" }. The USD wallet is debited for the current plan price (or awarded price if set on the subscription).
422 with Insufficient USD wallet balance: … — top up or switch to card.
Automatic renewal (wallet): while auto_renew is true and billing is wallet, the scheduler debits USD each period. Turn it off with PUT /v1/business/subscription/auto-renew and renew manually before current_period_end.
Pay by Card Payment
Direct plan billing. The card is charged for the subscription (initial checkout and recurring renewals). Your USD wallet is unchanged — use wallet only if you intend to pay from an existing balance.- Optional: quote —
GET /v1/business/subscription/quote?plan_id={id}&payment_method=card. Upgrades charge the full catalog price (creditis always0for card). - Start checkout —
POST /v1/business/subscription/subscribe(orPOST …/renewfor the current plan) withpayment_method: "card".
- Response —
data.actionischeckout. Send the user todata.checkout_url(onapi.yativo.com, path/subscriptions/checkout/{token}). That page is the Yativo-hosted Card Payment checkout (Whop Elements); it does not redirect to Whop. - After payment — the user returns to
SUBSCRIPTION_RETURN_URL(default{WEB_URL}/settings/billing). PollGET /v1/business/subscriptionuntilbilling_methodiscardandplan.idmatches. Activation is usually within seconds once payment is confirmed. - Manage card — when
billing_methodiscard,subscription.manage_card_url(signed, ~30 minutes) opens a page to update the card or download receipts.
subscribe again with card reuses an still-valid open link for the same plan.
Renewals: Card Payment bills the saved card each period. If payment fails, status becomes past_due until grace ends; use renew with card (new checkout) or wallet to settle.
Card billing setup (operators / deploy)
Which Whopbiz_… account is charged?
Merchant card sales must not be created under the platform company. Subscription Card Payment must not be created under a merchant connected account. See
App\Support\WhopPlatformCompany.
Required environment (see .env.example and config/whop.php):
How it works in code:
WhopPlanSynccreates a hidden Whop product underWhopPlatformCompany::id()and one renewal variant per paid plan (plans.whop_variant_id).SubscriptionManager::startCardCheckout()calls WhopcreateCheckoutConfigurationwithaccount_id=WhopPlatformCompany::id()andplan_id= that variant.- Card Checkout charges use
CheckoutService::chargeAccountId()→ the order’s connected seller account. - Product id is stored in admin settings as
subscriptions.whop_product_id(created on first sync). Runphp artisan subscriptions:sync-plansafter plan price changes.
allow_wallet). Card Payment is on for every active paid self-serve plan whenever WHOP_API_KEY is set (WhopPlanSync::configured()); each plan gets a processor variant automatically on save.
Sandbox testing: use sandbox processor credentials and WHOP_ENV=sandbox; subscribe with payment_method: "card" and complete Card Payment checkout.
Related guides:
- Fee Schedule:
GET /v1/feesshows the fees your current plan gives you. Lines priced by your plan havesource: "plan".
Environments
All endpoints take
Authorization: Bearer <token>. They act on the business account, whoever on the team calls them. Team members need Plans → view (team.plans.view) for GET endpoints and Plans → upgrade (team.plans.upgrade) for everything else.
Current subscription
A new account with no plan is put on the free plan the first time this endpoint is called.
Plans you can choose
change_typesays what choosing the plan would do:new,renew,upgrade(takes effect now) ordowngrade(takes effect at the end of the paid period).is_custom: truemarks a plan built for your account with your negotiated pricing. Only you see it.contact_sales: trueplans haveprice: nulland can’t be bought here; ask your account manager.- A feature
limitof-1means unlimited.
Quote
Shows what a plan change costs before you commit.credit) and you pay amount_due. Card upgrades charge the full price and have credit: 0. A downgrade returns effective: "period_end" and amount_due: 0.
Subscribe, upgrade or downgrade
The response’s
data.action tells you what happened:
Card checkout (summary)
See Pay by Card Payment for the full flow. In short: opencheckout_url → card checkout → return to billing → poll until billing_method is card.
Renew now
Pays for the current plan immediately: early renewal while active (the new period starts when the current one ends), or settling apast_due / overdue plan.
action: "renewed" (wallet) or action: "checkout" with a checkout_url (card). Free plans renew by themselves and return an error.
Cancel and resume
current_period_end, then the account moves to the free plan. Nothing more is charged. subscription.cancel_at_period_end becomes true.
false means the plan isn’t renewed from the wallet automatically; renew it yourself with POST /renew before it ends. For card billing it is the same as cancel / resume.
Billing history
status is succeeded, failed, pending or refunded. Failed wallet renewals appear with a failure_reason. Invoice payments recorded by Yativo show their invoice_number.
type is one of started, subscribed, upgraded, renewed, assigned, change_scheduled, cancel_scheduled, resumed, past_due, recovered, overdue, downgraded, ended, fallback, invoice_paid, extended, auto_renew_on, auto_renew_off. by is You, Yativo or Automatic.
Emails
The account owner is emailed when a plan activates or renews, a few days before a renewal, when a renewal fails (with the grace period end date), when a plan becomes overdue or is downgraded, and when a cancellation or downgrade is scheduled.Service fee bills
Platform service fees (account maintenance, annual KYC/KYB, and similar) are billed separately from your plan subscription. Pending and partially paid bills appear only under/business/billing/*, never in transaction history.
Same permissions as subscription endpoints: Plans → view (team.plans.view) for GET, Plans → upgrade (team.plans.upgrade) for card payment.
Wallet collection runs automatically when configured; use card payment when you need to pay outstanding bills by card. See Fee Schedule for the rates that drive these bills.
Errors
Errors use the usual shape with HTTP422 and a message you can show the user:
Older endpoints
GET /business/plans/current, GET /business/plans/all, POST /business/plans/subscribe/{plan_id} and PUT /business/plans/upgrade/{plan_id} still work and follow the same rules (wallet by default; pass payment_method: "card" to get a checkout_url). Use the endpoints above for new work.
