Skip to main content

Recurring Revenue. Automated.

Customers subscribe. We handle billing cycles, renewal recovery, and lifecycle management. You focus on your product.

How It Works

If a renewal charge fails, the subscription enters past_due. Waffo schedules no retries of its own: the payment channel retries the charge once, typically the next day. If that retry succeeds the subscription returns to active (subscription.recovered); if it fails, the subscription is canceled immediately (subscription.canceled) and the customer has to subscribe again. There is no separate grace-period timer. Customers receive a reminder email before each renewal (1 day ahead for weekly plans, 3 days for monthly and quarterly, 7 days for yearly; 1 day before a trial ends) and a receipt after each successful charge.

Subscription Plan Structure

In Waffo Pancake, each separately purchasable subscription option is an independent subscription product. The most common distinction is billing interval, such as:
  • monthly
  • yearly
If you offer both monthly and yearly billing, you would normally create two subscription products.

Subscription States


Billing Intervals


Trial Periods

Reduce signup friction. Let customers try before they commit — free, or for a small charge.

Configure Trials

When creating a subscription product, enable the trial toggle and set the number of days in the Dashboard.

Platform-Level Trial Protection

Waffo Pancake acts as the Merchant of Record and automatically prevents trial abuse: How the two layers interact:
  • If the merchant requests ≤ the platform maximum → the merchant’s requested value is used
  • If the merchant requests > the platform maximum → falls back to the platform maximum
  • If the merchant doesn’t specify → the full platform maximum is used
Trials don’t have to be free. You can charge a small, reduced amount during the trial period instead of waiving it entirely — the classic “$1 trial” pattern. It’s optional, can be set per currency, and always stays lower than the regular price. When the trial ends, billing switches automatically to the full price.
Paid trials filter out low-intent signups better than free trials, at the cost of some conversion friction. A small symbolic charge (e.g. $1) is usually enough to confirm payment method validity without deterring genuine buyers.

Set a trial price via API

See the trialAmount field reference for request format and rules.

Buyer Identity & Trial Protection

Trial eligibility is tracked by buyerIdentity — a stable identifier you provide via authenticated checkout. The platform uses this to detect repeat trial claims across sessions.
Without buyerIdentity (anonymous checkout), trial eligibility checks are skipped entirely. Any buyer can claim unlimited trials by changing their email address.
7-14 day trials work best. Too short = not enough time to evaluate. Too long = forgotten.

Renewal Recovery

When a renewal charge fails, the subscription transitions to past_due and you receive subscription.past_due. The recovery window is short and fixed by the payment channel: it retries the charge once, typically the next day.
  • Retry succeeds — the subscription returns to active and you receive subscription.recovered plus subscription.payment_succeeded.
  • Retry fails — the subscription is canceled immediately and you receive subscription.canceled. The customer must subscribe again; there is no further retry and no grace-period timer.
Keep access on during the past_due day and prompt the customer to update their payment method before the retry, since a second failure ends the subscription.
Treat past_due as “renewal requires recovery”, not simply “payment failed and service stopped”.

Managing Subscriptions

Cancel

Cancellation is always effective at the end of the current billing period. When a cancellation is requested, the subscription enters the canceling intermediate state. The customer retains access until the current billing period ends, at which point the status transitions to canceled.
Response includes currentPeriodEnd so you know when access expires. The returned status will be canceling (or canceled if the period has already ended).
There is no immediate cancellation option. Customers always retain access through the end of their paid period. The cancelingcanceled transition happens automatically when the current billing period expires.

Resume Subscription

If a subscription is still in canceling, the customer can resume it before the current period ends. After resuming:
  • the subscription returns to active
  • access continues without interruption
  • future renewals continue on the original billing cycle
Resuming applies to subscriptions that were canceled but have not reached the end of the current period yet. It is effectively an undo for cancellation, not a brand-new purchase.

Changing Plans

A plan change moves a subscriber to a different plan without making them cancel and buy again. It is modelled as one cancellation plus one new subscription — the old order closes, a new one opens. There is no in-place edit of a subscription’s plan. The customer always confirms on a hosted change confirmation page: old and new plans side by side, the prorated difference itemized, and the effective date stated.

Direction decides the default timing

On an immediate change the unused value of the current plan is credited against the new plan’s charge. That credit is recalculated at submission time from the days actually remaining — the figure shown when the link was issued is an estimate, not a promise.
The last 24 hours are asymmetric. An immediate change needs at least 24 hours before the next charge; a next-period change needs only 1 hour before the period ends.So inside that final day an upgrade fails with 400 (Immediate plan change requires at least 24 hours before the next charge) unless you explicitly pass changeTiming: "next_period" — it does not silently fall back. Downgrades already default to next period, so they still work.

Who can start one

Two routes, and they follow different rules:
selfServicePlanChange is a product-group rule and counts as off when unset. While it is off, portal plan changes are rejected with 403 — merchant-issued changes are unaffected. Turn it on before telling customers they can switch plans themselves.
Starting a plan change from the Dashboard UI is not available yet — the entry point on the subscriptions page is marked coming soon. The API works today.

Customer self-service

Self-service happens in the Customer Portal, and it needs no integration work from you. The customer signs in, opens the subscription, picks another plan in the same group, and confirms. To enable it: put the plans in one product group and turn on selfServicePlanChange.
The portal is not the only self-service surface. You can offer the same thing in your own UI with a customer session token — customer.createPlanChangeSession() in the SDKs — which runs under the customer’s own credential and is gated by exactly the same three conditions: their own subscription, same product group, selfServicePlanChange on. Issuing the link with your API Key instead is a different path: none of the three apply, and you can move a subscription to any plan — see below.

Merchant-initiated changes via API

1

Issue the change link

Call checkout/create-session with originOrderId set to the subscription you want to move. That puts the endpoint into plan-change mode and returns a checkoutUrl for the confirmation page.
2

Deliver it yourself

Email, in-app message, however you already reach the customer. The platform does not send this link.
3

Customer confirms

They review the comparison and confirm. Your system finds out by webhook.
Optional parameters Guards checked when issuing the link — these fail before the customer ever sees the page:
An unconfirmed change does not block a new one. If the customer opened a link and never paid, just issue another.

What your system should expect

Three webhook events cover the lifecycle: An immediate change reaches its switch moment right after authorization, so it goes straight to plan_changed with no scheduled event first.
Two traps for access-control logic:
  1. subscription.activated and subscription.canceled are not sent for the two orders involved — deliberately, so a “revoke access on cancellation” handler does not fire on a plan change.
  2. Between confirmation and the switch moment the original subscription sits in canceling while still entitled — its paid period is still running. Treat canceling as active access, exactly as you would for a customer’s own end-of-period cancellation.
While a scheduled change is pending, the new subscription has no billing period yet (currentPeriod is null) — the first period is confirmed by the payment channel at the switch moment.
If it fails, the new order closes and the original subscription carries on as before. A scheduled change that had already moved the original to canceling puts it back to active. You can then issue a new change.
Cross-currency changes are not supported, and a subscription cannot change plan during a trial.

Metrics

MRR (Monthly Recurring Revenue)

Key Metrics


Webhooks

Subscribe to subscription lifecycle events via webhooks. Configure webhook endpoints in Settings —> Webhooks.
Specific webhook event names are not listed here as they may change. Refer to the webhook configuration in your Dashboard for the current list of available events.
Webhook payloads use standard Waffo Pancake conventions:
  • IDs are UUID v4 format
  • Amounts as display format strings
  • Timestamps in ISO 8601 UTC
  • Billing frequency uses the billingPeriod field (e.g., monthly, yearly)

Customer Portal

Let customers manage their own subscriptions:
  • View details
  • Update payment method
  • Change plans
  • Cancel
  • Resume subscription
  • Download invoices

Customer Portal

Self-service subscription management.

Best Practices

A missed renewal charge does not need to mean instant cancellation. Give the customer time to recover the subscription.
Trial ending. Upcoming charge. No surprises.