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.
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: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.
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.
nulldoes not mean “no more charges.” Waffo returnsnullwhile 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. Treatnullas 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
currentPeriodagainst the subscription’s final period count, or checkendDateTime, before telling a customer they will be charged again.
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_SUCCESSandORDER_CLOSEare the only final statuses. Anything else can still change.- A period reaching
ORDER_CLOSEdoes not change the subscription’s own status. The subscription staysACTIVEand 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 iscreate, 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.