To‘lov moduli amalga oshirilishini boshlaganda eng avval duch kelgan muammo shunday edi: "To‘lov tasdiqlash API-ni qanday chaqirish kerak?" Aslida kalit nuqta shundan iboratki, "kelajakda qaysi to‘lov o'tkazuvchisi (PG) qo‘shilsa ham, xizmatning to‘lov oqimini beqaror qilmasdan bir xil to‘lov tajribasini taqdim eta olamizmi?"
Dastlabki MVP maqsadi T***Payments bilan integratsiya bo‘lsa-da, kelajakda to‘lov vositalari, hisob-kitob shartlari, chet el to‘lovlari qo‘llab-quvvatlanishi (Stripe kabi), mijoz talablari asosida bir vaqtning o‘zida bir nechta PG'ni qo‘llab-quvvatlash zarurati paydo bo‘lishi mumkin. Shuning uchun oddiy API integratsiyasidan tashqari quyidagi maqsadlar belgilandi.
-
Turli PG integratsiya kodlarini bitta joyga to‘plab parchalashni oldini oladi.
-
Ynew PG-ni oson qo‘shish mumkin bo‘lgan struktural kengayish imkoniyatini ta'minlaydi.
-
To‘lov ma'lumotlarini ichki DB-ga saqlab kuzatish va debugg qilish osonligini ta'minlaydi.
1. if-else shartlari bilan ishlashning cheklovlari va strategiya shabloni kiritilishi
Turli PG integratsiyasini to‘lov xizmatlari mantig‘ida bevosita amalga oshirish mumkin. Ammo, Stripe, K****Pay, chet el PG'lar qo‘shilganda to‘lov oqimi shunday shartlar bilan to‘lib ketadi.
if (pgProvider.equals("T***Payments")) {
// T*** 승인 로직
} else if (pgProvider.equals("Stripe")) {
// Stripe 승인 로직
} else if (pgProvider.equals("SomeOtherPg")) {
// 다른 PG 승인 로직}
Bu usul PG qo‘shilganda to‘lovning asosiy biznes oqimi va tashqi integratsiya tafsilotlari kuchli bog‘langan juda xavfli kamchilikka ega. To‘lov oqimi "mablag‘ni tekshirish → PG tasdiqlash → muvaffaqiyat / muvaffaqiyatsizlikni qayta ishlash" kabi umumiy oqimga e'tibor qaratishi kerak, har bir PG ning tasdiqlash sarlavhalari yoki javob parsalash usullari haqida bilishi shart emas. Buni ajratish uchun strategiya shabloni (Strategy Pattern) qo‘llanildi.
2. Umumiy interfeys (PgProxy) va amalga oshirishni loyihalash
Birinchidan, barcha PG'lar umumiy asosda amalga oshirishi lozim bo‘lganPgProxy interfeysini ta'rifladi. To‘lov oqimi nuqtai nazaridan PG'lar turidan qat'i nazar "to‘lovni tasdiqlang" degan bir xil xabarni berish kifoya.
public interface PgProxy {
String getPgProviderType();
WebClient getWebClient();
PgPaymentResponse approvePayment(String paymentKey, String orderId, Money amount);
PgPaymentResponse getPgPaymentResponse(String orderId);
}
To* payments amalga oshirilishi yuqoridagi interfeysni meros qilib olgan holda quyidagicha amalga oshiriladi.
@Configuration
@ConditionalOnProperty(value = "pg.t***.enabled", havingValue = "true")
public class T***PgProxy implements PgProxy {
@Value("${pg.t***.secret-key}")
private String secretKey;
@Override
public PgPaymentResponse approvePayment(String paymentKey, String orderId, Money amount) {
T***Payment response = confirmPayment(paymentKey, orderId, amount);
return new PgPaymentResponse(response);
}
@Override
public String getPgProviderType() {
return "T***Payments";
}
}
Bu yerda @ConditionalOnPropertyAnnotatsiyani foydalanib, sozlamalar qiymati(pg.t***.enabled=true)ga ko'ra muhitga mos PG amalga oshirish faqatgina Spring bean sifatida dinamik ro'yxatdan o'tkazilishi boshqarildi. Kelajakda qo'shiladigan StripePgProxy ham shu interfeysni amalga oshirib tayyorlanishi mumkin.
3. Muvofiq strategiyani topadigan yo'naltirish fabrikasi (PgProxyFactory)
Endi so'ralgan PG turiga mos keladigan amalga oshirishni topadigan PgProxyFactoryni tuzamiz.
@Component
public class PgProxyFactory {
private final Map<String, PgProxy> pgProxies;
public PgProxyFactory(List<PgProxy> pgProxies) {
this.pgProxies = pgProxies.stream()
.collect(Collectors.toMap(PgProxy::getPgProviderType, Function.identity()));
}
public PgProxy getPgProxy(String pgProvider) {
PgProxy pgProxy = pgProxies.get(pgProvider);
if (pgProxy == null) {
throw new NoSuchElementException("지원하지 않는 결제 대행사입니다: " + pgProvider); }
return pgProxy;
}
}
Spring konteyneri PgProxy turidagi barcha beanlarni ro'yxat sifatida kiritganda, fabrika buni Map shaklida o'zgartirib xotirada saqlaydi. Keyinchalik biznes mantiqida PG identifikatori (Enum va h.k.) o'tkazilganda darhol muvofiq amalga oshirishni qaytaradi. Klass nomi Factorykaka, lekin bu yerda ob'ektning 'yaratilishi'dan ko'ra real vaqt muhitiga mos 'strategiya obyekti'ni tanlab yo'naltirish orqali strategiya menejerining rolini bajaradi.
4. Asosiy biznes mantiqini soddalashtirish
Strategiya shablonini qo'llagan haqiqiy to'lov xizmati mantiqi tashqi aloqa tafsilotlaridan to'liq ozod bo'ladi.
@Service
@Transactional
@RequiredArgsConstructor
public class PaymentNeutFlow {
private final PaymentTask paymentTask;
private final PgProxyFactory pgProxyFactory;
private final PaymentLogic paymentLogic;
public String requestPayment(String paymentId, BigDecimal amount, String pgPaymentKey) {
paymentTask.updatePgPaymentKey(paymentId, pgPaymentKey);
try {
Payment payment = paymentLogic.findPayment(paymentId);
paymentTask.validateAmount(paymentId, amount);
// 외부 PG 통신: 팩토리에서 구현체를 가져와 승인 요청만 위임
PgPaymentResponse pgPaymentResponse = pgProxyFactory
.getPgProxy(payment.getPgProvider())
.approvePayment(pgPaymentKey, payment.getId(), payment.getAmount());
paymentTask.markSuccess(pgPaymentResponse.getOrderId());
return pgPaymentResponse.getOrderId();
} catch (Exception e) {
paymentTask.markFailed(paymentId);
throw e;
}
}
}
Endi PaymentNeutFlowma'lum bir PGning API URL yoki javob obyekt shaklini bilmaydi. Faqat to'lov kalitini saqlash, summa tekshirish, zavod orqali tasdiqlashni yuklash, muvaffaqiyat / muvaffaqiyatsizlik holatini ko'rib chiqishga qaratilgan biznes mas'uliyatiga e'tibor qaratadi.
5. Kengayish uchun ochiq tuzilma (OCP prinsipi erishish)
Bu tuzilmaning haqiqiy qiymati yangi PG (masalan: N***Pay) qo'shilganda namoyon bo'ladi. Kerakli ishlar faqat ikkita.
-
PgProvider Enumga N***Pay konstantasini qo'shish.
-
PgProxyni meros qilib oluvchi N***PgProxy klassini yangidan yaratish.
Mavjud to'lov oqimi(PaymentNeutFlow) yoki zavod(PgProxyFactory) kodlarFaqat bitta qatorni ham o'zgartirish shart emas.Spring yangi amalga oshiruvchini avtomatik tarzda aniqlab, quyish etdiyi uchun. O'zgarish doirasi juda minimallashtirilganligi sababli, mavjud funktsiyalarda xatolar (regressiya xatolari) paydo bo'lish ehtimoli ham ancha kamayadi.
6. Amalga oshirish samaradorligi va tugatish
To'lov modulida strategiya naqshini va adaptör tushunchasini birlashtirish natijasida olingan foyda quyidagi kabilar:
-
To'lov jarayonini soddalashtirish:Xizmat darajasi PG qismiga xos tafsilotlarni bilmasligi shart emas va to'lov biznes jarayoni o'zi faqatgina e'tibor qaratadi.
-
Yuqori mustahkamlik:Aloqa vaqtidan cheklash, sarlavha sozlash, javobni parselash kabi ma'lum PGga bog'liq kodlar har bir Proxy obyekti ichida xavfsiz tarzda izolyatsiya qilinadi.
-
Moslashuvchan operatsion sozlash: @ConditionalOnPropertyorqali mahalliy, ishlab chiqish, operatsiya kabi muhitlarga mos ravishda ma'lum PG amalga oshirishlarini erkin yoqish va o'chirish mumkin.
-
Sinov osonligi ta'minlandi:Tashqi aloqa qismidan mock qilish osonlashadi, to'lov jarayoni o'ziga bo'lgan birlik testlarini yozishni juda osonlashtiradi.
To'lov tizimi zaruriyat bilan muvaffaqiyatsizliklar va o'zgarishlar ko'p bo'lgan tashqi dunyo bilan bog'liqdir. Mustahkam to'lov moduli tashqi o'zgarishlarning tizim ichidagi asosiy mantiqqa o'tishiga to'siq bo'lishi kerak. Strategiya naqshlari o'sha mustahkam chegaralarni qurish uchun eng samarali va amaliy dizayn edi.
chnsik