Skip to main content
This page is about timing: which day a period is charged, and how that day moves. Retry policy and payment-method behaviour are in Subscription retries & payment methods; amounts are in Subscription amounts & per-period scheduling.

When is a renewal charge actually initiated?

Waffo stores a next-charge timestamp on the subscription and initiates that period’s charge on a periodic sweep once the timestamp has passed. Two consequences:
  • The date is deterministic; the time of day is not. Plan reconciliation and customer messaging at day granularity, not to the minute.
  • All stored subscription timestamps — the next-charge time, period start and end, the subscription end time — are UTC, and the API returns them in ISO 8601. If your billing calendar is local, convert on your side; there is no per-merchant billing timezone setting.
There is no way to pin a subscription to “charge at 09:00 in the customer’s timezone”. The nearest control you have is startTime on /api/v1/subscription/create, which sets the anchor the whole schedule is derived from — see Subscription creation & validation.

How is each subsequent billing date derived?

From the subscription’s start time, by adding whole cycles:
Not from the date the previous charge succeeded, and not by stepping from the previous period’s date. Every period is computed from the same anchor, so a period that was charged late does not shift the ones after it, and rounding never accumulates from one period to the next. The cycle used is the cycle in force for that period number: if you configured a separate trial cycle, the trial periods are counted from the start in the trial cycle, and the regular periods are counted from the end of the trial in the regular cycle. The switch happens at the period number where the trial ends.

A monthly subscription started on the 31st. What happens in February?

It clamps to the last valid day of a short month, and springs back to the 31st afterwards. Because every period is computed as start + n months, adding one month to 31 January gives 28 February (29 in a leap year) — there is no 31 February — but adding two months gives 31 March, and adding three gives 30 April. The clamp applies only to the month being landed on; it does not become the new anchor. This is the single most common surprise in month-end subscriptions: the subscription does spring back to the 31st. A customer who started on the 31st is charged on the last day of every month — the 28th, 29th, 30th or 31st, whichever that month has. Do not build a reminder that assumes a fixed day-of-month for such a subscription. If billing on a stable day-of-month matters to your product, start the subscription on a day that exists in every month (1–28), or set startTime explicitly rather than letting “now” decide the anchor.

A period only succeeded after three days of retries. Does the next period move?

By default, no. The next period is computed from the subscription’s start as described above, so a period recovered by retries does not push the rest of the schedule back — the customer does not gain days. There is an account-level setting that rebases the schedule onto the actual payment time instead: where it is enabled, a successful renewal recomputes the next charge date from the moment that charge succeeded, and the subscription’s end date is recomputed with it. Two things to know about it:
  • It only ever moves the schedule forward. If the recomputed date is earlier than the stored one, the stored one stands.
  • It is configured on your acquiring agreement by Waffo, not in the Portal. If you need “the customer always gets a full cycle from the day they actually paid”, ask your Waffo contact which behaviour is set on your account before you build against either one.
Because the two behaviours produce different dates, do not derive next-charge dates yourself. Read nextPaymentDateTime back from /api/v1/subscription/inquiry after each period.

Which date field should I read for a “next charge” reminder?

So: read nextPaymentDateTime for reminders, but handle two cases before you promise a customer a date.
  • null does not mean “no more charges.” Waffo returns null while the subscription’s renewal is being held — for example because the current period is still being attempted, or a plan change is pending on it. Once the hold clears, a date reappears. Treat null as not known yet, not as finished.
  • On the final period the value is stale rather than future. It is not advanced there, so it still shows the last period’s own date. Cross-check currentPeriod against the subscription’s final period count, or check endDateTime, before telling a customer they will be charged again.
For “is this subscription finished”, read the status — the terminal statuses on Subscription creation & validation are the answer, not a date field.

What do the per-period charge statuses mean?

Every billing period has its own order, with its own status. The Portal renders these as Initial, To Pay, Retrying, Paid and Closed: Two rules that matter more than the labels:
  • PAY_SUCCESS and ORDER_CLOSE are the only final statuses. Anything else can still change.
  • A period reaching ORDER_CLOSE does not change the subscription’s own status. The subscription stays ACTIVE and the next period is attempted on schedule. Entitlement decisions are yours to make from these per-period outcomes; see Subscription retries & payment methods.

Why was the very first charge not retried at all?

Because the first charge of an immediately-starting subscription is a customer-present payment, not a background renewal. When it fails, that period’s order is closed straight away: no retry schedule, no backup-method attempt, and no payment-failed email to the customer. The reasoning is that the customer is right there — the correct recovery is to let them try again, or to try a different payment method, not to retry a card they are watching fail. The second row is the exception people trip over. A deferred-start subscription is authorised up front and charged later without the customer present, so its first charge behaves like every later one. Recovery for the immediate case is to create a new subscription once the customer picks another method. Do not create a separate one-off order for the same period and expect the subscription to notice it.

Can I let the customer pay a failed period themselves instead of waiting?

There is no merchant-facing endpoint that collects a specific failed period. The subscription API surface is create, inquiry, cancel, manage, change, change/inquiry and update — none of them re-drives a period’s charge, and there is no “pay now” or “repay” call. What you can actually do: Adding a working payment method is the highest-value action, because a bound backup is tried immediately on the next failure rather than waiting for the retry interval.

Can I change the retry policy myself in the Portal?

No. The retry strategy — per-cycle interval and maximum attempts — lives on your acquiring agreement and is set by Waffo. There is no Portal screen and no API that writes it, so it cannot be changed self-serve, and it is not a per-request parameter either. The same is true of the other schedule-shaping settings on the agreement: the rebase-after-payment behaviour above, and whether pending retries are closed when a subscription is cancelled. All three are account-level. When one of them is changed, ask explicitly whether it applies to subscriptions that already exist or only to new ones — the answer depends on which setting it is, and assuming the wrong one is how reconciliation breaks.

How do I test all of this without waiting for a real cycle?

Use the sandbox renewal simulator: activate a subscription, fetch its management URL with /api/v1/subscription/manage, open it, and drive one period at a time with the simulate-next-payment controls. Each click is one period, so you can chain “period 2 succeeds, period 3 fails”. Details and the reserved test amounts are in Sandbox simulator and Sandbox testing. What the sandbox does not let you do is fast-forward the real clock, so the month-end behaviour above is best verified by reading nextPaymentDateTime after each simulated period rather than by waiting — a subscription started on the 31st should show the 28th/29th for February and the 31st again for March.