Fintech

Building Reliable Recurring Payment Systems

Technical guide to building recurring payment systems that handle retries, card updates, mandate management, and edge cases.

The Recurring Payment Challenge

Collecting a one-time payment is conceptually simple: customer pays, you deliver. Recurring payments introduce a fundamentally different dynamic. You are charging someone who is not actively present in your checkout flow. Cards expire. Bank accounts close. Customers forget they subscribed. Your system must handle all of this while maintaining both technical reliability and customer trust.

Payment Methods for Recurring Charges

Card-on-File

Store a tokenized card reference and charge it periodically. This is the most common approach for SaaS and subscription services.

Advantages: Works globally, customers are familiar with the flow, immediate confirmation.

Challenges: Cards expire (typically every 3-4 years), card details change (lost/stolen replacement), and issuers may decline recurring charges they consider suspicious.

Card updater services: Visa Account Updater (VAU) and Mastercard Automatic Billing Updater (ABU) automatically update stored card credentials when a customer receives a new card. Most PSPs support this. Enable it.

SEPA Direct Debit

Pull funds directly from the customer's bank account using a signed mandate. Dominant in Europe for subscription billing.

Advantages: No card expiration problem, lower processing fees than cards, high success rates for established mandates.

Challenges: Longer settlement time (D+5 for first collection on SEPA Core DD), chargeback window of 8 weeks (no-questions-asked) or 13 months (unauthorized), and mandate management overhead.

SEPA Direct Debit Mandate Flow

1. Customer provides IBAN and signs mandate (digital or paper)
2. You store mandate details and generate a Unique Mandate Reference (UMR)
3. You submit the mandate to your bank/PSP
4. For each collection: submit a pre-notification and the direct debit instruction
5. Bank processes the debit on the agreed date
6. Funds settle in your account (D+5 for first, D+2 for recurring on Core scheme)

Pre-notification requirement: You must inform the customer before each debit, typically 14 days in advance (reducible to 1 day with customer agreement). Your system needs to send these notifications on schedule.

The Charge Scheduler

Your recurring charge system needs a reliable scheduler. Do not rely on cron jobs that fire at midnight and attempt to charge everyone at once.

Distributed Scheduling

1. Store the next charge date on each subscription
2. A scheduler job runs every hour and picks up subscriptions due within the next hour
3. Each subscription gets its own queued charge job
4. Jobs execute independently with their own retry logic
5. After successful/failed charge, calculate and store the next charge date

Why hourly batches instead of one daily run? Distributing charges across the day reduces peak load on your PSP's API and your own infrastructure. It also means a temporary PSP outage does not affect all your charges.

Idempotency Protection

Your charge scheduler must be idempotent. If the scheduler runs twice for the same period (due to a deployment, a crash, or a bug), it must not create duplicate charges:

public function chargeSubscription(Subscription $subscription): void
{
    // Check if this period has already been charged
    $existingCharge = Charge::where('subscription_id', $subscription->id)
        ->where('period_start', $subscription->current_period_start)
        ->where('period_end', $subscription->current_period_end)
        ->first();

    if ($existingCharge) {
        return; // Already charged for this period
    }

    // Create charge record first, then attempt payment
    $charge = Charge::create([
        'subscription_id' => $subscription->id,
        'period_start' => $subscription->current_period_start,
        'period_end' => $subscription->current_period_end,
        'amount' => $subscription->plan->price,
        'status' => 'pending',
    ]);

    $this->attemptPayment($charge);
}

Retry Logic (Dunning)

Payment failures in recurring billing are normal. Cards get replaced, bank balances fluctuate, issuers have temporary outages. A good retry strategy recovers 60-80% of initial failures.

Smart Retry Timing

Not all retry schedules are equal. Research and industry practice suggest:

  • Retry on different days of the week. A card declined on Monday (post-weekend spending) may succeed on Friday (post-payday).
  • Retry at different times. Some issuers have maintenance windows. Vary your retry timing.
  • Back off exponentially. 1 day, 3 days, 7 days, 14 days is a reasonable schedule.

Decline Code Analysis

Different decline codes require different responses:

Retryable:

  • Insufficient funds (retry after a few days)
  • Temporary processing error (retry after a few hours)
  • Issuer unavailable (retry after an hour)

Not retryable:

  • Card expired (request card update from customer)
  • Lost/stolen card (request new payment method)
  • Do not honor (contact customer)

Map your PSP's decline codes to these categories and route accordingly.

Grace Periods and Access Control

When a recurring charge fails, you face a product decision: does the customer lose access immediately?

Common approach:

  1. Days 1-3 after failure: Full access, retry silently
  2. Days 4-7: Full access, notify customer of payment issue
  3. Days 8-14: Restricted access (read-only, limited features), urgent notifications
  4. Day 15+: Suspend account, send final notice
  5. Day 30: Cancel subscription

The exact timing depends on your business. Higher-value subscriptions warrant longer grace periods and more personal outreach.

SCA for Recurring Payments

Under PSD2, the first payment must be authenticated with SCA. Subsequent recurring charges of the same amount to the same payee are exempt from SCA.

Variable amounts complicate this. If your subscription price changes (usage-based billing, plan upgrades), you may need to re-authenticate. The practical threshold varies by issuer.

For SEPA Direct Debit, the signed mandate serves as the authentication. No additional SCA is required for individual collections.

Monitoring and Alerting

Track these metrics for your recurring payment system:

  • Success rate by payment method (target: above 95% for established subscriptions)
  • Recovery rate (percentage of failed charges eventually recovered through retries)
  • Involuntary churn (customers lost due to payment failures, not voluntary cancellation)
  • Time to recovery (average days between initial failure and successful retry)
  • Decline code distribution (shifting patterns may indicate systemic issues)

Set alerts for:

  • Success rate dropping below threshold
  • Unusual spike in a specific decline code
  • PSP API response times exceeding normal range
  • Queue depth growing (charges not being processed)

Recurring payments are a continuous relationship between your system and your customers' banks. Build for resilience: distribute your charges, retry intelligently, handle every decline code, and monitor relentlessly. The difference between 95% and 98% collection rate is significant revenue.

Let's talk about your fintech needs

Whether you're modernizing your infrastructure, navigating compliance, or building new software - we can help.

Book a 30-min Call