Payment Synchronization
This document describes the processes by which we keep the transaction information from payment gateways synchronized with publica.la orders and payments - including status updates, gateway keys, and related payments.
Overview of Synchronization Processes
Our payment synchronization system consists of several automated processes that work together to maintain consistency between gateway data and our internal records:
Payment Creation and Updates
- PaymentSync - Creates missing
paymentsfor approved orders - CheckMercadoPagoRecurringPayments - Manages recurring payment status and subscription states. Cancel subscriptions if they are not paid.
- CheckMercadoPagoOneOffPayments - Processes pending one-time MercadoPago payments
- CheckPayUOneOffPayments - Handles pending PayU payment verification
Execution Schedule
| Process | Schedule | Purpose | Source of truth |
|---|---|---|---|
| PaymentSync | Hourly | Create missing payments. | Gateways |
| CheckMercadoPagoRecurringPayments | Every 5 minutes | Update subscription statuses | publica.la payments table |
| CheckMercadoPagoOneOffPayments | Hourly | Process pending one-time payments | publica.la payments table |
| CheckPayUOneOffPayments | Hourly | Verify pending PayU payments | publica.la payments table |
Detailed Process Descriptions
PaymentSync
Overview
The payment processing system is designed to handle various types of payments (one-off, recurring, external, total discount)
across multiple payment gateways (Stripe, MercadoPago, External) in a multi-tenant environment.
The system uses a strategy pattern to process different payment types, and implements job batching for efficient processing.
Core Components
PaymentSync Job
PaymentSync is the main orchestrator for payment processing, responsible for:
-
Strategy Selection: Chooses the appropriate strategy based on type (one-off, recurring, external, total discounts)
-
Order discovery: Searches for eligible orders for a specific strategy and redispatches in chunks
-
Order processing: Processes orders through the appropriate strategy to create missing payments
Payment Types
const TYPE_ONE_OFF = 'one-off';
const TYPE_RECURRING = 'recurring';
const TYPE_EXTERNAL = 'external';
const TYPE_ANY = 'any';
const TYPE_TOTAL_DISCOUNT_ONE_OFF = 'total-discount-one-off';
const TYPE_TOTAL_DISCOUNT_RECURRING = 'total-discount-recurring';
Strategy Pattern Implementation
Base Strategy: PaymentProcessingStrategy
An abstract class that defines the common structure for all payment strategies:
- Logging functionality
- Order processing limits (200 orders per job)
- Activity tracking
- Error handling
Key features:
- Processes orders in chunks of 200 to prevent timeouts
- Implements logging for monitoring and debugging
- Handles both successful processing and bypassed orders
- Includes safety delays (30ms) between gateway calls
Concrete Strategies
-
OneOffPaymentStrategy
- Handles single payment orders
- Supports Stripe and MercadoPago gateways
- Processes orders that are:
- Not pending
- Not checked recently
- Have valid payment gateway information
-
RecurringPaymentStrategy
- Manages subscription-based payments
- Checks approved orders every 2 days
- Special handling for MercadoPago pending orders:
- Checks recently updated pending orders on every run
- Focuses on orders updated within the last 4 days
-
ExternalPaymentStrategy
- Processes external payment systems
- Handles orders that:
- Use the 'external' gateway type
- Don't have payments
- Were created or updated in the last 4 days
- Have 'approved' status
-
TotalDiscountOneOffStrategy
- Manages one-time total discount payments
- Processes orders where:
- Tenant has
charge_total_discount_couponenabled - Plan types are 'prepaid', 'single', or 'retail'
- Don't have payments
- Tenant has
-
TotalDiscountRecurringStrategy
- Handles recurring total discount payments
- Processes orders:
- Without payments for the current month
- With valid recurring plans
Processing Flow details
-
Job Initialization examples
// Process all payment types
new PaymentSync('any');
// Process specific orders with strategy
new PaymentSync('oneOff', [$orderId1, $orderId2]);
// Process specific orders with any strategy
new PaymentSync('any', [$orderId1, $orderId2]);
// Process total discount orders from date
new PaymentSync('totalDiscountOneOff', null, '2024-01-01'); -
Strategy Selection
- Handles 'any' type by re-dispatching itself for each strategy sequentially
- Handles the rest of types by initiating the corresponding strategy
-
Order Processing
- Fetches orders based on strategy criteria
- If no orderIds where provided, it searches for the orderIds to be processed and redispatches itself in chunks including those orderIds
- If orderIds where provided (from a manual dispatch or from a previous job), it fetches those orders and processes them
- It always chunks orders (200 per batch) for efficient processing
- Implements safety delays between gateway calls
-
Logging and Monitoring
- Activity logs for tracking processing status
- Search them using
log_name = 'PaymentSync'
- Search them using
- Error logging for debugging
- Processing metrics (counts of processed/bypassed orders)
- Activity logs for tracking processing status
Testing
The system includes comprehensive tests covering:
- Strategy selection and execution
- Order processing for different payment types
- Gateway integration
- Error handling
- Activity logging
Test examples are available in PaymentSyncTest.php.
Maintenance Notes
Adding New Payment Types
- Create new strategy class extending
PaymentProcessingStrategy - Implement required methods:
getOrders()processOrder()getPaymentType()
- Add new type constant to
PaymentSync - Update strategy factory method
Monitoring
- Check activity logs for processing status
- Search them using
log_name = 'PaymentSync'
- Search them using
- Monitor processing times for large batches
- Watch for bypassed orders patterns
- Review error logs for gateway issues
Performance Optimization
- Index critical query fields
- Monitor chunk size effectiveness
- Review gateway call delays
- Check database load during processing
CheckMercadoPagoRecurringPayments
This job ensures that all payments for approved orders are present and handles situations such as manual subscription cancellations by users on the MercadoPago website or through the credit card company, as well as payment failures that MercadoPago keeps trying to collect indefinitely.
The process only covers approved UserPlans, verifying the associated payments and pausing the plan if the number of approved payments is less than the minimum expected. It constantly reviews recently updated pending orders (from the last 4 days) to approve them when they have enough payments or cancel them if they do not. This query, ordered by mp_checked_at, takes 100 orders at a time, placing pending orders at the end of the list once checked.
It prioritizes older orders since we cannot order by status without risking that pending orders remain at the end of the list and are not processed if new approved orders accumulate. Most of the checks are performed against our own database, with no interaction with external providers, except when pausing unpaid subscriptions.
CheckMercadoPagoOneOffPayments
This job will process only the Mercado Pago Orders that are in pending status and while the plan_type in user_plans has the value single or prepaid. It will be approved if the total amount of transactions with a success status in the payments table is equal to the amount in users_plans, then it is Paid.
CheckPayUOneOffPayments
This job processes pending UserPlans created in the last week. It checks if any approved payments exist, and if so, it approves the UserPlan. This job was created to address issues where both the callback_url to approve not work. As a result, some Payu UserPlans had approved payments but remained in a 'pending' status.
This job checks Payu records with a pending status and approves them if the payment was successful after calling the Payu API. It also updates the gateway_key and gateway_meta_post fields.