MockPaymentGateway.java
package com.rbctcsworld.ecommerce.payment;
import java.math.BigDecimal;
import java.util.UUID;
/**
* Stand-in for a real payment provider (Stripe/Adyen style test cards). Deterministic, so tests can
* trigger every outcome on purpose:
*
* 4000 0000 0000 0002 -> declined ("Card declined")
* 4000 0000 0000 9995 -> declined ("Insufficient funds")
* any other Luhn-valid -> approved (e.g. 4242 4242 4242 4242)
*
* Card format, Luhn and expiry are validated BEFORE the gateway is called (PaymentService).
* Default provider (app.payment.provider=mock, see PaymentGatewayConfig); StripePaymentGateway is the real one.
*/
public class MockPaymentGateway implements PaymentGateway {
public static final String DECLINED_CARD = "4000000000000002";
public static final String INSUFFICIENT_FUNDS_CARD = "4000000000009995";
@Override
public GatewayResult charge(Long orderId, String cardNumber, BigDecimal amount) {
return switch (cardNumber) {
case DECLINED_CARD -> GatewayResult.decline("Card declined", null);
case INSUFFICIENT_FUNDS_CARD -> GatewayResult.decline("Insufficient funds", null);
default -> GatewayResult.ok("mock_ch_" + UUID.randomUUID());
};
}
/** The token only remembers which kind of test card it was, so a saved declining card keeps declining. */
@Override
public String tokenize(String cardNumber) {
String kind = switch (cardNumber) {
case DECLINED_CARD -> "declined";
case INSUFFICIENT_FUNDS_CARD -> "funds";
default -> "ok";
};
return "mock_pm_" + kind + "_" + UUID.randomUUID().toString().substring(0, 8);
}
@Override
public GatewayResult chargeToken(Long orderId, String token, BigDecimal amount) {
if (token.startsWith("mock_pm_declined_")) return GatewayResult.decline("Card declined", null);
if (token.startsWith("mock_pm_funds_")) return GatewayResult.decline("Insufficient funds", null);
if (token.startsWith("mock_pm_ok_")) return GatewayResult.ok("mock_ch_" + UUID.randomUUID());
return GatewayResult.decline("Unknown saved card", null);
}
@Override
public GatewayResult refund(Long orderId, String chargeReference, BigDecimal amount, int refundNumber) {
return GatewayResult.ok("mock_re_" + UUID.randomUUID());
}
@Override
public String name() {
return "mock";
}
/** Luhn (mod 10) checksum used by every real card number. */
public static boolean luhnValid(String digits) {
int sum = 0;
boolean doubleIt = false;
for (int i = digits.length() - 1; i >= 0; i--) {
int d = digits.charAt(i) - '0';
if (doubleIt) {
d *= 2;
if (d > 9) d -= 9;
}
sum += d;
doubleIt = !doubleIt;
}
return sum % 10 == 0;
}
}