Example

1. Handling Payment Processing Failures

In a microservices architecture, let’s assume we have an e-commerce platform with a Payment Service responsible for processing payments by communicating with a third-party payment gateway (e.g., Stripe, PayPal). Due to various reasons (e.g., network instability, gateway downtime, rate-limiting), payment processing may occasionally fail, but these failures are often transient.

The goal is to implement a retry mechanism with different retry policies, backoff strategies, and recovery handling, ensuring payment operations are retried when appropriate but without overloading the payment gateway.

  • Service: PaymentService calls a third-party payment API to process payments.

  • Retry Logic:

    • Retry on specific transient failures like TimeoutException and PaymentGatewayUnavailableException.

    • Use an exponential backoff strategy to avoid overloading the payment gateway.

    • Limit the number of retry attempts to prevent excessive retries.

  • Fallback Recovery: After exhausting retries, the transaction is marked as pending, and the user is notified to try again later.

PaymentService.java class

package org.example.service;

import org.example.client.PaymentGatewayClient;
import org.example.exception.PaymentException;
import org.example.exception.PaymentGatewayUnavailableException;
import org.example.model.PaymentRequest;
import org.example.model.PaymentResponse;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeoutException;

@Service
public class PaymentService {

    private final PaymentGatewayClient paymentGatewayClient;

    public PaymentService(PaymentGatewayClient paymentGatewayClient) {
        this.paymentGatewayClient = paymentGatewayClient;
    }

    // Retryable method for processing payment
    @Retryable(
            retryFor = { TimeoutException.class, PaymentGatewayUnavailableException.class },
            maxAttempts = 5,
            backoff = @Backoff(delay = 2000, multiplier = 2.0)
    )
    public PaymentResponse processPayment(PaymentRequest request) throws PaymentException {
        // Call the third-party payment gateway
        return paymentGatewayClient.processPayment(request);
    }

    // Recovery method if retries fail
    @Recover
    public PaymentResponse recover(PaymentGatewayUnavailableException e, PaymentRequest request) {
        System.out.println("Recovering after retries failed for request: " + request.getTransactionId());
        // Mark transaction as pending and notify user to retry
        return markTransactionAsPending(request);
    }

    private PaymentResponse markTransactionAsPending(PaymentRequest request) {
        // Logic to mark the transaction as pending due to payment failures
        return new PaymentResponse("PENDING", "Your payment is pending. Please try again later.");
    }
}

PaymentGatewayClient.java class

PaymentException and PaymentGatewayUnavailableException java class

PaymentRequest and PaymentResponse java Class

Last updated