E-Commerce Payment System - Strategy Pattern Implementation
This is an implementation of the Strategy design pattern for handling multiple payment methods in an e-commerce checkout system.
Problem Statement
Building a backend for an online store like Flipkart or Amazon where users can pay using different payment methods. Each payment method requires different details and processing logic, but the system must remain extensible without using if-else chains.
Class Diagram
+------------------+ +----------------------+
| PaymentRequest |---------->| PaymentFactory |
+------------------+ +----------------------+
| - type: Enum | | + getStrategy(req) |
| - amount: double | +----------+-----------+
| - card/upi data | |
+------------------+ | (Creates)
v
+------------------+ +----------------------+
| PaymentService | | PayStrategy |
| (Context) | | (Interface) |
+------------------+ +----------------------+
| - strategy |<>-------->| + pay(amount) |
| + makePayment() | +----------+-----------+
+------------------+ ^
| (Implements)
|
+---------------------+--------+--------+----------------------+
| | | |
+--------------------+ +-----------------+ +--------------------+
| CreditCardStrategy | | UPIStrategy | | PayPalStrategy |
+--------------------+ +-----------------+ +--------------------+
| - cardNumber | | - upiId | | - email, password |
+--------------------+ +-----------------+ +--------------------+
Implementation
package ecommercecheckout;
/**
* Main Driver Class for the E-Commerce Payment System.
* Implements Strategy Pattern (for algorithms) and Factory Pattern (for object creation).
*/
public class ECommerceCheckout {
/**
* Enum to define supported payment types.
* Ensures Type Safety and prevents invalid string inputs.
*/
enum PaymentType {
CREDIT_CARD,
UPI,
PAYPAL
}
/**
* Data Transfer Object (DTO) for Payment Requests.
* Acts as a unified form to collect all possible inputs from the user.
*/
static class PaymentRequest {
public PaymentType paymentType;
public double amount;
// Specific fields for different strategies
public String cardNumber;
public String cvv;
public String expiry;
public String upiId;
public String email;
public String password;
}
/**
* Strategy Interface.
* Defines the common contract that all payment methods must follow.
*/
public interface PayStrategy {
void pay(Double amount);
}
/**
* Concrete Strategy for Credit Card.
* Uses Constructor Injection to receive card details.
*/
static class CreditCardStrategy implements PayStrategy {
private final String cardNumber;
private final String cvv;
private final String expiryDate;
public CreditCardStrategy(String cardNumber, String cvv, String expiryDate) {
this.cardNumber = cardNumber;
this.cvv = cvv;
this.expiryDate = expiryDate;
}
@Override
public void pay(Double amount) {
System.out.println("Validation Card: " + this.cardNumber);
System.out.println("Paid " + amount + " successfully");
System.out.println("Card Details: Card Number => " + this.cardNumber +
" CVV => " + this.cvv + " ExpiryDate => " + expiryDate);
}
}
/**
* Concrete Strategy for UPI.
* Encapsulates logic specific to UPI payments.
*/
static class UPIStrategy implements PayStrategy {
private final String upiId;
public UPIStrategy(String upiId) {
this.upiId = upiId;
}
@Override
public void pay(Double amount) {
System.out.println("Validating UPI Id: " + this.upiId);
System.out.println("Paid " + amount + " successfully");
System.out.println("UPI Details: UPI Id => " + this.upiId);
}
}
/**
* Concrete Strategy for PayPal.
* Encapsulates logic specific to Email/Password authentication.
*/
static class PayPalStrategy implements PayStrategy {
private final String email;
private final String password;
public PayPalStrategy(String email, String password) {
this.email = email;
this.password = password;
}
@Override
public void pay(Double amount) {
System.out.println("Validating Email: " + this.email +
" and PassWord: " + this.password);
System.out.println("Paid " + amount + " successfully");
System.out.println("PayPal Detail: Email => " + this.email +
" Password => " + this.password);
}
}
/**
* Factory Class.
* Centralizes the object creation logic, keeping the Client code clean.
*/
static class PaymentFactory {
/**
* Creates and returns the appropriate Strategy object based on the Request Type.
* @param request The input DTO containing payment details.
* @return A valid PayStrategy instance.
*/
public static PayStrategy getStrategy(PaymentRequest request) {
switch (request.paymentType) {
case CREDIT_CARD -> {
return new CreditCardStrategy(request.cardNumber,
request.cvv, request.expiry);
}
case UPI -> {
return new UPIStrategy(request.upiId);
}
case PAYPAL -> {
return new PayPalStrategy(request.email, request.password);
}
default -> {
throw new IllegalArgumentException("Unsupported Payment Type: " +
request.paymentType);
}
}
}
}
/**
* Context Class (The Shopping Cart).
* Maintains a reference to the current Strategy and delegates execution.
*/
static class PaymentService {
private PayStrategy strategy;
/**
* Allows changing the payment strategy dynamically at runtime.
*/
public void setStrategy(PayStrategy strategy) {
this.strategy = strategy;
}
/**
* Executes the payment using the currently selected strategy.
*/
public void makePayment(Double amount) {
if(strategy == null) {
System.out.println("Error: No Payment method selected");
return;
}
System.out.println("\n----Getting Payment Mode-----");
strategy.pay(amount);
System.out.println("-------------------------------");
}
}
/**
* Main method to simulate the User/Client interaction.
*/
public static void main(String[] args) {
PaymentService razorPay = new PaymentService();
System.out.println("Simulation User: Shiv");
// Scenario 1: Credit Card Payment
try {
PaymentRequest request = new PaymentRequest();
request.paymentType = PaymentType.CREDIT_CARD;
request.cardNumber = "123456";
request.cvv = "111";
request.expiry = "11-2";
PayStrategy payStrategy = PaymentFactory.getStrategy(request);
razorPay.setStrategy(payStrategy);
razorPay.makePayment(100.00);
} catch (Exception e) {
System.out.println(e.getMessage());
}
// Scenario 2: UPI Payment
try {
PaymentRequest request = new PaymentRequest();
request.paymentType = PaymentType.UPI;
request.upiId = "1234567890";
PayStrategy payStrategy = PaymentFactory.getStrategy(request);
razorPay.setStrategy(payStrategy);
razorPay.makePayment(20.00);
} catch (Exception e) {
System.out.println(e.getMessage());
}
// Scenario 3: PayPal Payment
try {
PaymentRequest request = new PaymentRequest();
request.paymentType = PaymentType.PAYPAL;
request.email = "noob@gmail.com";
request.password = "12345678";
PayStrategy payStrategy = PaymentFactory.getStrategy(request);
razorPay.setStrategy(payStrategy);
razorPay.makePayment(50.00);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Key Features
- Strategy Pattern: Encapsulates different payment algorithms
- Factory Pattern: Centralizes object creation logic
- Type Safety: Uses Enum instead of raw strings
- Extensible: Easy to add new payment methods without modifying existing code
Top comments (0)