Mintcash
Concepts

Subscriptions

Recurring billing — the state machine, the retry schedule, and what happens between renewals.

A Subscription represents a recurring agreement: charge a customer a fixed amount on a fixed interval. MintCash handles billing, retries on failure, and emails the customer at each state change.

The state machine

Rendering diagram…

Terminal states: failed, cancelled. Once either is reached, no more billing happens.

Allowed transitions

FromAllowed to
createdpending, active, failed
pendingactive, failed
activepast_due, cancelled
past_dueactive, cancelled
failed(terminal)
cancelled(terminal)

Full lifecycle

Rendering diagram…

Renewal logic

When a subscription reaches its billingIntervalDays since the last successful charge, MintCash:

  1. Charges the saved payment method using the stored card token.
  2. On success: subscription stays active, fires subscription.succeeded.
  3. On failure: subscription moves to past_due, fires subscription.failed, and schedules the next retry.

Retry schedule

A failed renewal isn't the end. MintCash retries on a backoff schedule:

AttemptWhenStatus
1Day 0 (renewal due date)First charge attempt
2+1 day after attempt 1Retry 1
3+3 days after attempt 2Retry 2
4+5 days after attempt 3Retry 3
5+7 days after attempt 4Retry 4
After all retries failSubscription cancelled

The customer receives an email after the first failure prompting them to update their payment method. If they do, the next renewal cycle starts fresh.

Why this schedule

The 1→3→5→7-day cadence is calibrated against industry decline-recovery patterns. Most recovered subscriptions come back in the first two attempts; the longer tail catches cards reissued mid-month.

Cancellation

Send DELETE /subscriptions/{id} to cancel immediately. Pending invoices are not charged. The subscription's status becomes cancelled — terminal, no further renewals or retries.

Cancellation does not refund prior payments. To refund a customer who cancels mid-cycle, issue a refund against the most recent payment with POST /payments/{id}/refund.

What fires when

TransitionWebhookCustomer email
created → activesubscription.succeeded"Subscription active"
active → past_duesubscription.failed"Payment failed — update method"
past_due → activesubscription.succeeded"Payment recovered"
* → cancelledsubscription.cancelled"Subscription ended"