jeudi 16 décembre 2021

Java - Generic for Payment processing with Strategy pattern

I am trying to implement Strategy pattern approach for payment processing in my Spring webflux based application.

My application supports multiple payment method like, Card Payment, Cash Payment, ... Also, we have to support Square & Stripe for Card payment.

Model class,

// Model interface
public interface PaymentModel {

}

// Base model with attributes needed for all payment types
public class BaseModel implements PaymentModel {

    private Float amount;
    private Integer userId;
}

public class SquareCardModel extends BaseModel {

    private String merchantId;
    private String device;
    private String orderId;

}

public class StripeCardModel extends BaseModel {

    private String merchantId;
    private String orderId;

}

public class CashModel extends BaseModel {

    private String name;
    private String orderId;

}

Service Class,

@Service
public interface PaymentService<T extends PaymentModel> {

    Mono<ServerResponse> pay(T model);

    String method();
}

@Service
public class CashPaymentService implements PaymentService<CashModel> {

    private static final String PAYMENT_METHOD = "cash";

    @Override
    public Mono<ServerResponse> pay(CashModel model) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String method() {
        return PAYMENT_METHOD;
    }

}

@Service
public class SquarePaymentService implements PaymentService<SquareCardModel> {

    private static final String PAYMENT_METHOD = "cash";

    @Override
    public Mono<ServerResponse> pay(SquareCardModel model) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String method() {
        return PAYMENT_METHOD;
    }

}

@Service
public class StripePaymentService implements PaymentService<StripeCardModel> {

    private static final String PAYMENT_METHOD = "cash";

    @Override
    public Mono<ServerResponse> pay(SquareCardModel model) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String method() {
        return PAYMENT_METHOD;
    }

}

Factory Class,

@Service
public class PaymentFactory<T> {

    private final List<PaymentService<? extends PaymentModel>> paymentServices;

    @Autowired
    public PaymentFactory(List<PaymentService<? extends PaymentModel>> paymentServices) {
        this.paymentServices = paymentServices;
    }

    public PaymentService<? extends PaymentModel> retrievePaymentService(final String paymentMethod) {
        Optional<PaymentService<? extends PaymentModel>> paymentService = paymentServices.stream()
                .filter(service -> service.method().equals(paymentMethod)).findFirst();

        if (paymentService.isEmpty()) {
            throw new IllegalArgumentException("Unsupported Payment method ");
        }
        return paymentService.get();
    }

}

User choose the payment method and the call comes to the backend,

@Transactional
    public Mono<ServerResponse> payBilling(ServerRequest request) {
            return request.bodyToMono(PaymentDto.class).flatMap(paymentReq -> {
                if (paymentReq.getPaymentType().equals("CC")) { // For Card
                    return processCardPayment(usr, paymentReq);
                } else {
                    return badRequest().bodyValue("Not supported yet !");
                }
            });
    }

private Mono<? extends ServerResponse> processCardPayment(
            PaymentDto paymentReq) {
            PaymentService<PaymentModel> paymentService = (PaymentService<PaymentModel>) paymentFactory
                    .retrievePaymentService(paymentReq.getPaymentType());
            PaymentModel paymentModel = buildPaymentModel((String) paymentReq.getPaymentType(), paymentReq,
                    jsonMap);
            return paymentService.pay(paymentModel);
    }

    private PaymentModel buildPaymentModel(final String paymentMethod, final PaymentDto paymentReq,
        if (paymentMethod.equals("squarePayment")) {
            SquareCardModel model = new SquareCardModel();
            model.setAmount(paymentReq.getTotal());
            model.setMerchantId(paymentReq.getMerchantid());
            model.setOrderId(orderId);

            return model;
        }

        return null;

    }

Questions:

  1. Not sure if I have implemented generics properly with the strategy pattern.
  2. Also, I dont like type casting here. (PaymentService). is there any better approach?
  3. Why do I still need to use if for creating different model.

if (paymentMethod.equals("squarePayment")) {

PaymentService<PaymentModel> paymentService = (PaymentService<PaymentModel>) paymentFactory
                        .retrievePaymentService(paymentReq.getPaymentType());
                PaymentModel paymentModel = buildPaymentModel((String) paymentReq.getPaymentType(), paymentReq,
                        jsonMap);
                return paymentService.pay(paymentModel);

Aucun commentaire:

Enregistrer un commentaire