Featured image of post Continuous Monthly Subscription - Periodic Deduction - Auto-Renewal Business Process Summary

Continuous Monthly Subscription - Periodic Deduction - Auto-Renewal Business Process Summary

Continuous monthly subscription is a common feature in many apps. This article summarizes the development process.

Overview

The terms “Continuous Monthly Subscription”, “Periodic Deduction”, and “Auto-Renewal” refer to the same core functionality:

  • Periodic Deduction - Official Alipay terminology
  • Continuous Monthly Subscription - Common implementation pattern (also supports daily/quarterly)
  • Auto-Renewal - User-facing behavior of automatic payment

Deduction Scenarios

Two implementation models:

  1. Sign-then-Deduct

    • Redirect to agreement signing page
    • User signs agreement
    • Alipay async callback + immediate deduction
  2. Pay-then-Sign

    • Redirect to payment page (user can disable auto-renewal)
    • User completes payment
    • Receive two async notifications: payment & agreement

Recommendation: Prefer Sign-then-Deduct to prevent users from disabling renewal during payment.

Platform Requirements

  • Alipay: Requires enabling “Periodic Deduction” via Alipay Open Platform
  • WeChat Pay: More complex application process requiring additional Template ID

Agreement Signing Flow

Signature Generation (Go Implementation)

We use modified gopay library (PR #295 merged 2023-01-01):

Key implementation notes:

  1. Generate contract_code as merchant agreement ID
  2. Create signature parameters for mobile app deep linking:
// Example signature parameters
bm := gopay.BodyMap{}
bm.Set("personal_product_code", "GENERAL_WITHHOLDING")
bm.Set("sign_scene", "INDUSTRY|OTHER")
bm.Set("external_agreement_no", contractCode)
// ... other required fields
  1. Construct deep link URL:
alipays://platformapi/startapp?appId=60000157&sign_params={URL-Encoded_Params}

Database Design

Core subscription table structure (simplified):

CREATE TABLE subscriptions (
    id INT PRIMARY KEY,
    order_no VARCHAR(64) UNIQUE,      -- Merchant order ID
    trade_no VARCHAR(64),             -- Payment platform ID
    user_id INT,                      -- User reference
    contract_code VARCHAR(64) UNIQUE, -- Custom agreement ID
    contract_id VARCHAR(64),          -- Platform agreement ID
    expire_time DATETIME              -- Next deduction date
);

Order Processing Flow

  1. Create order with generated contract_code
  2. Generate signing parameters and return deep link to client
  3. Handle async notification to store contract_id
  4. Initial payment via alipay.trade.pay (synchronous API)
  5. Client polling for payment status
  6. Scheduled task for recurring deductions:
// Cron job example (executed daily)
func CheckSubscriptions() {
    // Query subscriptions expiring in 5 days
    subscriptions := GetExpiringSubscriptions()
    
    for _, sub := range subscriptions {
        // Initiate payment via Alipay API
        resp, err := alipay.TradePay(sub)
        // Handle payment result
    }
}

Platform-Specific Notes

Platform Deduction Lead Time Example Timeline
WeChat 2 days Sign 15th → Deduct 13th → Result 15th
Alipay 5 days Sign 15th → Deduct 10th → Result 15th

Key Implementation Points

  1. Use separate async handlers for payment & agreement notifications
  2. Implement idempotency checks for all payment operations
  3. Maintain detailed audit logs for deduction attempts
  4. Provide clear cancellation UI/API endpoints

Complete implementation example available at: github.com/yourrepo/subscription-service