How subscription billing works: plans, recurring payments, and handling failed charges
For users, subscriptions are simple: pay once a month and use the service. Behind that simplicity is a stack of payment gateway integration, database logic, plan management, and failed payment handling. Here's how it works technically and what you need to watch out for to avoid losing revenue.
Recurring payments: what happens after the first charge
A recurring payment is an automatic charge using saved card credentials. The user pays once, the payment system stores a card token, and from then on your service initiates the charge every month.
Technically: after the first payment, the gateway (Stripe, Braintree, or a local provider) returns a token - an encrypted card identifier. You save it in your database alongside the user ID and next billing date. When the date arrives, your server sends a request to the gateway API: charge this amount to this token. The gateway attempts the transaction and returns the result.
Important: recurring billing only works if the user agreed during the first payment. The checkbox for saving the card for automatic billing is not a formality - it is a legal requirement. Without explicit consent, you cannot enable recurring charges.
In practice, the first payment often fails not because of technical issues but because the bank blocks an unfamiliar transaction. If the user has never paid your service before, the bank may request SMS confirmation or decline the charge outright. That is why the first payment must be maximally transparent: company name in the payment description, clear amount, receipt sent to email immediately after the charge.
Plans and switching between them
Plans are stored in the database as a set of parameters: name, price, period (monthly, annual), feature set. When a user selects a plan, you create a record in the subscriptions table: user ID, plan ID, start date, next payment date, status (active, cancelled, overdue).
Switching between plans is the most common headache. A user can upgrade mid-month, and there are two approaches: prorated billing (user pays the difference for remaining days) or deferred switch (current plan stays active until end of period, new plan starts next month). The first is fairer but more complex to implement.
If you do prorated billing, the logic is: calculate how many days remain until end of current period, calculate the cost of those days on the current plan, subtract from the price of the new plan for a full month, and charge the difference. The next payment date stays the same, only the amount changes.
Downgrades (switching to a cheaper plan) are better handled from the start of the next period. Refunding money proportionally is both technically harder and messier from an accounting perspective. The user continues on the current plan until the end of the paid month, then automatically switches to the new one.
Trial periods and delayed charging
A trial period looks simple: user signs up, uses the service free for 7-14 days, then gets charged automatically. In practice there are two approaches.
First: attach the card immediately but charge after N days. You tokenize the card at signup (charge and immediately refund a small amount to verify), save the token, and set the first real charge date two weeks out. Upside: less churn after trial because the card is already attached. Downside: some users will refuse to enter a card upfront.
Second: trial without a card, payment request on the last day. User registers with email, uses the service for two weeks, on the last day receives an email with a payment link. Upside: higher signup conversion. Downside: most users will not come back to pay.
We typically recommend the first approach if the product is expensive (a few hundred dollars per month) and aimed at business users. The second works for cheap services (under ten dollars) where audience volume matters more.
What to do when a payment fails
A recurring payment can fail for many reasons: insufficient funds, blocked card, expired card, bank declined the transaction. According to payment system statistics, 10-15% of recurring charges fail on the first attempt.
Standard approach: three retry attempts with a one-day interval. First failure - wait a day, try again. Second - another day. Third - suspend the subscription and send an email asking the user to update their card.
Important: do not block access immediately after the first failure. The issue may be temporary (salary deposit delay, technical glitch at the bank), and if you lock the account instantly, the user leaves annoyed. Give a few days to resolve the issue.
Logic in code: on each failure, increment a failed_attempts counter in the subscriptions table. If counter equals 3, change status to suspended and send an email. The user can go to settings, update their card (which is a new tokenization), and you immediately attempt to charge the outstanding amount. If it goes through - reset the counter, return status to active.
Some payment gateways can handle retries automatically - you set a rule in the dashboard and the system tries three times on its own. But control over subscription status still stays on your side.
How to calculate subscription revenue correctly
Subscription revenue is counted not at the moment of charge but evenly throughout the period. This is called revenue recognition. If a user paid twelve thousand for a year on January 1, you cannot record the entire sum as January revenue - in accounting terms it is one thousand per month over the course of a year.
Technically this means the database must store not only payment date but also subscription period. You charged on March 15, subscription period is March 15 to April 15. In reports you spread the amount proportionally across the days of each month: part goes to March, part to April.
It helps to maintain a revenue_log table: payment ID, period start date, period end date, amount. When building a monthly report, select all records whose period overlaps with that month and sum the proportional share.
If the user cancelled mid-month and you refunded the money - it subtracts from the revenue of the period the refund covered. Not from the month when you charged, but from the month for which the user did not receive the service.
Cancelling a subscription: how to avoid chargebacks
Cancellation must be simple and accessible. If a user cannot find the cancel subscription button in their account, they go to the bank and file a chargeback - a payment dispute. For you that is a fee from the payment system plus lost money.
Correct approach: cancel button in the account dashboard, one confirmation (are you sure?), and the subscription moves to cancelled status. Access remains until the end of the paid period - the user paid for a month, let them use it for a month. The recurring charge simply does not fire next time.
Do not route cancellation through support. Yes, it gives a chance to retain the customer through conversation, but it annoys more than it helps. Better to show a feedback form after cancellation: why are you leaving? with answer options. Some users will write back and you will learn what needs fixing.
Review chargebacks manually. If a user disputes a payment, the payment system sends a notification. You have 7-14 days to provide evidence: screenshot of the account dashboard with active subscription, service usage log, recurring billing consent. If you do not respond - the money is refunded automatically, plus a penalty.
Technical stack for subscriptions
If building subscriptions from scratch, you need a payment gateway with recurring support (Stripe, Braintree, or local equivalents), a database for storing subscriptions and tokens, and a cron job to check billing dates.
Cron runs once an hour, selects from the database all subscriptions where next_payment_date is now or earlier and status is active, sends charge requests. If the charge succeeds - update next_payment_date (add a month), if not - increment failed_attempts.
If subscription volume is high (thousands active), better to use a task queue (RabbitMQ, Redis Queue). Cron puts subscription IDs into the queue, workers pull from the queue and process payments in parallel. This is faster and more reliable.
Ready-made solutions like Stripe Billing or Paddle handle all the mechanics: you just create a plan in their interface, connect webhooks, and they handle charging, retries, and notifications. Convenient if working in Western markets. For other regions you may need to build your own wrapper or use local gateways directly.
If this is already on your plate, here is how we do it end to end: Web service and SaaS development.
Frequently asked
Can you accept subscriptions without recurring billing, just remind the user to pay manually?+
You can, but conversion to repeat payment will be several times lower. The user will forget, ignore the email, or simply not want to log into their bank every month. Recurring billing gives 3-4 times better retention, especially on cheaper plans.
How much does it cost to enable recurring payments?+
Depends on the gateway. Most charge a percentage per transaction (typically 2.5-3.5%), with no fixed setup fee. If volume is high, you can negotiate a lower rate. Technical integration takes a developer a few days.
What happens if the user runs out of money and all three retry attempts fail?+
The subscription is suspended, access to features is locked, and the user receives an email asking to update payment method. Some services give a grace period (3-5 more days of access after suspension) so the person has time to resolve the issue. If they do not pay - subscription is cancelled, data remains but functionality is unavailable.
Can you offer an annual subscription at a discount?+
Yes, it is standard practice. Create a separate plan with a 12-month period and a price lower than the sum of 12 monthly payments. Technically nothing changes, just next_payment_date shifts by a year instead of a month. Annual subscriptions reduce churn and provide predictable revenue, so they benefit both sides.




