Notes

Charging subscribers

You decide when to charge a subscriber, and how much: we keep no schedule. That makes three things yours to get right: the schedule, what to do when a charge fails, and how to get a new card when the old one stops working.

One subscriber per payer

Create a subscriber once for each payer, and keep its "id" with your customer record. Give it your customer ID as its "ref", which seq returns with it and with each of its charges.

When the payer's card expires, or they come back after cancelling, renew the subscriber rather than create a new one: it keeps its "id", so your records of it, and of its charges, stay as they are.

The schedule

Keep the schedule in your database: for each subscription, when it is next due, and what to charge. A job that runs, say, every hour charges what is due:

for each s in Due(now) do
    key := LoadOrCreateKey(s, s.period, s.attempt)  -- one key per period
    res := Charge(s.subid, s.items, s.orderid, key)
    if res is 200 then MarkPaid(s, s.period, res.id)
    else if res is not 4xx or is 408 then Failed(s, res)  -- see below
    else Alert(res)                                 -- a fault in the request
end for

The key is per subscription, period, and attempt, made before the attempt and stored, so that a crash or a timeout can't charge a period twice: a retry of the attempt with its key gets the first answer. Failed retries the attempt with its key when there was no answer, or a 408, 502, 503, or 504, and otherwise counts the attempt up, so that tomorrow's try is a new request with a new key, as a failed charge's answer is stored under its key too. See Retries and idempotency.

A charge waits for the acquirer, and can take seconds. Charge one subscriber at a time per card, and never two charges of the same subscriber at once: both can go through.

When a charge fails

Whatever the version, a charge can fail after the acquirer authorized it, as when an "autocapture" capture fails. So before you try a failed charge again with a new key, synchronize: if seq has a charge with the period's "orderid", the period is charged. A failed charge that did not happen leaves no transaction, so seq does not return it: record failures yourself.

In v1, a failed charge is a 500 with a message that starts with charge failed:, followed by where it failed and why, as 500 charge failed: nets error: not sufficient funds, with no transient or permanent. A decline, a fault at the acquirer, and a fault of ours look alike, so after synchronizing, try again once a day, at most five times in all, and then ask the payer to renew. A card that has expired, or a message that names a lost, stolen, or closed card, will not succeed: ask the payer to renew at once.

In v2, a charge that the acquirer declines is a 402 with a message that starts with transient or permanent:

  • transient: it may succeed later, as when the account lacks the money today. Try again tomorrow.
  • permanent: it will not succeed on this card. Don't try again, as the card schemes fine you for it, but ask the payer to renew.

A schedule for transient failures that keeps within the schemes' limits: try again once a day, at most five times in all, then treat it as permanent. In v2, any other error is not a decline: the request was wrong, or we failed.

In both, the limits themselves, and why they exist, are in the knowledge base's Declined charges. A 4xx won't succeed if you send it again unchanged, and no answer at all, or a 502, 503, or 504, is safe to retry with the same key.

Getting a new card

A card stops working when it expires, is replaced, or the payer closes the account. Plan for it:

  • Know when the card expires, from the "exp" in the subscriber's "method", as seq returns it. Ask the payer to renew in the month before.
  • Tell the payer at the first failure, so that they can fix it while you retry. After the last try, or a failure that will not go away, such as v2's permanent, send them a link to renew: create it with Renew subscriber, with the failed amount as an initial charge if they owe it, and send the "url" by email.
  • Pause the subscription until the subscriber is renewed, which seq tells you with a "renew" act.

Charging fairly

The card schemes, and the payer's bank in a dispute, expect the payer to have agreed to each charge, to be told before an unexpected one, and to be able to cancel. The knowledge base's Stored credentials has the rules. When a payer cancels, stop charging. In v2, delete the subscriber if you will never charge it again. In v1, which can't delete one, take it off your schedule and forget its "id".