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

package org.example.client;

import org.example.exception.PaymentException;
import org.example.exception.PaymentGatewayUnavailableException;
import org.example.model.PaymentRequest;
import org.example.model.PaymentResponse;
import org.springframework.stereotype.Component;

@Component
public class PaymentGatewayClient {

    public PaymentResponse processPayment(PaymentRequest request) throws PaymentException {
        // Simulate communication with payment gateway
        if (Math.random() > 0.7) {
            return new PaymentResponse("SUCCESS", "Payment processed successfully.");
        } else {
            throw new PaymentGatewayUnavailableException("Payment gateway is temporarily unavailable.");
        }
    }
}

PaymentException and PaymentGatewayUnavailableException java class

package org.example.exception;

public abstract class PaymentException extends Exception {
    protected PaymentException(String message) {
        super(message);
    }
}
package org.example.exception;

public class PaymentGatewayUnavailableException extends PaymentException {
    public PaymentGatewayUnavailableException(String message) {
        super(message);
    }
}

PaymentRequest and PaymentResponse java Class

package org.example.model;

public class PaymentRequest {
    private String transactionId;
    private double amount;

    // Getters and Setters

    public PaymentRequest(String transactionId, double amount) {
        this.transactionId = transactionId;
        this.amount = amount;
    }

    public String getTransactionId() {
        return transactionId;
    }

    public double getAmount() {
        return amount;
    }
}
package org.example.model;

public class PaymentResponse {
    private String status;
    private String message;

    // Getters and Setters

    public PaymentResponse(String status, String message) {
        this.status = status;
        this.message = message;
    }

    public String getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }
}

Last updated

Was this helpful?