Chuyển đến nội dung chính

Lesson 5: Payment Processing - Authorization, Capture & Settlement

Deep-dive into payment processing flow. Two-phase commit for payment. Authorization hold and capture timing. Settlement process and clearing. Batch processing vs real-time settlement.

🏗️ Architecture — Lesson 5 Lesson 5: Payment Processing - Authorization, Capture & Settlement

FinTech & Payment Platform Architecture

Part 2: Core Payment Engine

xdev.asia

Lesson 5: Payment Processing - Authorization, Capture & Settlement

Introduction

After understanding the Payment Gateway overview, this article deep-dives into the payment processing process — from authorization to settlement. This is the most complicated part and requires absolute precision because it is directly related to money.


1. Authorization Flow

1.1 Card Authorization

┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
│ Customer │   │ Merchant │   │ Acquirer │   │  Card    │   │  Issuer  │
│          │   │          │   │  Bank    │   │ Network  │   │  Bank    │
└────┬─────┘   └────┬─────┘   └────┬─────┘   └────┬─────┘   └────┬─────┘
     │              │              │              │              │
     │  Card Info   │              │              │              │
     ├─────────────►│              │              │              │
     │              │  Auth Req    │              │              │
     │              ├─────────────►│              │              │
     │              │              │  Auth Req    │              │
     │              │              ├─────────────►│              │
     │              │              │              │  Auth Req    │
     │              │              │              ├─────────────►│
     │              │              │              │              │
     │              │              │              │  Check:      │
     │              │              │              │  - Balance   │
     │              │              │              │  - Fraud     │
     │              │              │              │  - Limits    │
     │              │              │              │              │
     │              │              │              │  Auth Resp   │
     │              │              │              │◄─────────────┤
     │              │              │  Auth Resp   │              │
     │              │              │◄─────────────┤              │
     │              │  Auth Resp   │              │              │
     │              │◄─────────────┤              │              │
     │  Result      │              │              │              │
     │◄─────────────┤              │              │              │

1.2 Authorization Response Codes

CodeMeaningAction
00ApprovedProceed with capture
05Do not honorTerminal - don't retry
14Invalid card numberTerminal
51Insufficient fundsTerminal
54Expired cardsTerminal
61Exceeds limitCan retry with lower amount
91Issuer unavailableRetry later

1.3 Auth Hold Management

Authorization creates a "hold" on customer funds:

Timeline:
Day 0: Auth $100 ──► Hold $100 on card
Day 1-7: Hold active, merchant can capture
Day 7+: Hold expires (auto-release by issuer)

Rules:
├── Auth hold duration varies by card network
│   ├── Visa: 7-30 days
│   ├── Mastercard: 7-30 days
│   └── NAPAS: Varies by issuer
├── Partial capture: Can capture <= auth amount
├── Multi-capture: Some networks support partial captures
└── Void: Cancel auth before capture (release hold)

2. Capture Process

2.1 Capture Strategies

1. Auto-capture (immediate):
   Auth ──► Immediate Capture
   Use case: Digital goods, instant delivery

2. Manual capture (delayed):
   Auth ──► Wait ──► Capture when ready
   Use case: Physical goods (capture on ship)

3. Partial capture:
   Auth $100 ──► Capture $80 (partial shipment)
   Remaining $20 ──► Void or capture later

4. Multi-capture:
   Auth $100 ──► Capture $50 ──► Capture $30 ──► Capture $20
   Use case: Marketplace with multiple sellers

2.2 Capture Implementation

public class CaptureService {

    @Transactional
    public CaptureResult capture(String paymentId, Money amount) {
        var payment = paymentRepository.findById(paymentId)
            .orElseThrow(() -> new PaymentNotFoundException(paymentId));

        // Validate
        if (payment.getStatus() != PaymentStatus.AUTHORIZED) {
            throw new InvalidStateException("Payment must be AUTHORIZED");
        }
        if (amount.isGreaterThan(payment.remainingCapturable())) {
            throw new AmountExceedsAuthException();
        }

        // Capture with PSP
        var pspResult = pspAdapter.capture(payment, amount);

        // Update payment
        payment.capture(amount, pspResult);
        paymentRepository.save(payment);

        // Publish event
        eventBus.publish(new PaymentCapturedEvent(payment, amount));

        return CaptureResult.success(payment);
    }
}

3. Settlement Process

3.1 Settlement Flow

Settlement = Transfer of actual funds from issuer to acquirer to merchant

Daily Settlement Cycle:
┌─────────────────────────────────────────────────────────┐
│                                                          │
│  23:00  Cut-off time                                     │
│    │                                                     │
│    ▼                                                     │
│  Batch all captured transactions for the day             │
│    │                                                     │
│    ▼                                                     │
│  Submit settlement file to acquirer/network              │
│    │                                                     │
│    ▼                                                     │
│  T+1: Clearing (acquirer receives funds from network)    │
│    │                                                     │
│    ▼                                                     │
│  T+1~T+3: Payout to merchant account                    │
│    │                                                     │
│    ▼                                                     │
│  Settlement complete                                     │
│                                                          │
└─────────────────────────────────────────────────────────┘

3.2 Settlement Architecture

┌─────────────────────────────────────────────────────────┐
│                 SETTLEMENT ENGINE                        │
│                                                          │
│  ┌──────────┐    ┌──────────┐    ┌──────────────────┐  │
│  │ Batch    │    │ Fee      │    │ Payout           │  │
│  │ Builder  │───►│ Engine   │───►│ Calculator       │  │
│  └──────────┘    └──────────┘    └────────┬─────────┘  │
│                                           │              │
│  ┌──────────┐    ┌──────────┐    ┌────────▼─────────┐  │
│  │ Report   │◄───│Reconcile │◄───│ Transfer         │  │
│  │ Generator│    │ Engine   │    │ Executor         │  │
│  └──────────┘    └──────────┘    └──────────────────┘  │
└─────────────────────────────────────────────────────────┘

3.3 Fee Calculation

Transaction Amount: 1,000,000 VND

Fee Breakdown:
├── Interchange fee (issuer):     1.5%  = 15,000 VND
├── Network fee (Visa/MC):        0.2%  =  2,000 VND
├── Acquirer fee:                 0.3%  =  3,000 VND
├── Gateway fee (our platform):   0.5%  =  5,000 VND
└── Total fees:                   2.5%  = 25,000 VND

Merchant receives: 975,000 VND
public record SettlementCalculation(
    Money grossAmount,
    Money interchangeFee,
    Money networkFee,
    Money acquirerFee,
    Money platformFee,
    Money netAmount
) {
    public static SettlementCalculation calculate(
            Payment payment, MerchantFeeConfig config) {
        Money gross = payment.getCapturedAmount();
        Money interchange = gross.multiply(config.interchangeRate());
        Money network = gross.multiply(config.networkRate());
        Money acquirer = gross.multiply(config.acquirerRate());
        Money platform = gross.multiply(config.platformRate());
        Money net = gross.subtract(interchange)
                        .subtract(network)
                        .subtract(acquirer)
                        .subtract(platform);
        return new SettlementCalculation(
            gross, interchange, network, acquirer, platform, net);
    }
}

4. Batch Processing

4.1 End-of-Day Batch

@Scheduled(cron = "0 0 23 * * *") // 23:00 daily
public void runDailySettlement() {
    var cutoffTime = LocalDateTime.now();
    var transactions = paymentRepository
        .findCapturedNotSettled(cutoffTime);

    // Group by merchant
    var byMerchant = transactions.stream()
        .collect(Collectors.groupingBy(Payment::getMerchantId));

    for (var entry : byMerchant.entrySet()) {
        var merchantId = entry.getKey();
        var payments = entry.getValue();

        // Calculate settlement
        var settlement = settlementService
            .createSettlement(merchantId, payments);

        // Generate settlement file
        settlementFileGenerator.generate(settlement);

        // Queue payout
        payoutService.queuePayout(settlement);
    }
}

4.2 Real-time Settlement

New trend with instant payments (VietQR, UPI):

Traditional:  Capture → T+1~T+3 Settlement
Real-time:    Capture → Instant Settlement (< 30 seconds)

Real-time settlement requires:
├── Real-time clearing network (NAPAS for Vietnam)
├── Pre-funded settlement accounts
├── Real-time balance management
└── Instant notification to merchant

5. Refund Processing

5.1 Refund Types

Full Refund:    Payment $100 → Refund $100
Partial Refund: Payment $100 → Refund $30 (keep $70)
Multiple Refund: Payment $100 → Refund $30 → Refund $20 → ... (total ≤ $100)

5.2 Refund Flow

@Transactional
public RefundResult processRefund(String paymentId, Money amount, String reason) {
    var payment = paymentRepository.findById(paymentId)
        .orElseThrow(() -> new PaymentNotFoundException(paymentId));

    // Validate
    if (!payment.isRefundable()) {
        throw new PaymentNotRefundableException();
    }
    if (amount.isGreaterThan(payment.refundableAmount())) {
        throw new RefundAmountExceedsException();
    }

    // Process refund with PSP
    var pspResult = pspAdapter.refund(payment, amount);

    // Create refund record
    var refund = Refund.create(payment, amount, reason, pspResult);
    refundRepository.save(refund);

    // Update payment
    payment.addRefund(refund);
    paymentRepository.save(payment);

    // Reverse ledger entries
    eventBus.publish(new RefundProcessedEvent(refund));

    return RefundResult.success(refund);
}

Summary

Payment processing is a complex process with many steps:

  • Authorization: Reserve funds, validate card
  • Capture: Collect authorized funds (immediate or delayed)
  • Settlement: Actual fund transfer via clearing network
  • Refund: Reverse the payment flow

Key principles: idempotency at every step, state machine clarity, audit trail for each change.

Next article: Multi-PSP Integration — design abstraction layer to integrate multiple PSPs (VNPay, MoMo, Stripe) with smart routing.