Төлем шлюзі: Ірі клиентті PayPal-мен қалай біріктірдік (және аман қалдық)
Сәлем, инженерлер мен жоғары жүктемелі жүйелердің (High-Load Systems) жанкүйерлері! Егер сіздер төлем жүйелерімен интеграция жасау — бұл жай ғана бірнеше API сұрауларын жіберіп, JSON жауаптарын деректер базасына сақтай салу деп ойласаңыздар, сіздер үшін жағымсыз жаңалығым бар. Нақты өндірістік ортада (Production), әсіресе миллиондаған доллар айналымы бар ірі жобаларда, бұл процесс нағыз архитектуралық сынаққа айналады.
Бүгін мен PayPal-дың ең ірі жаһандық клиенттерінің біріне (өкінішке орай, коммерциялық құпияны сақтау туралы қатаң NDA келісіміне байланысты оның атын атай алмаймын) арналған өте күрделі, жоғары жүктемелі төлем шлюзін қалай құрғанымызды егжей-тегжейлі баяндап беремін. Біз бұл жобада тек аман қалып қана қоймай, сонымен қатар секундына мыңдаған транзакцияларды өңдей алатын, қателіктерге төзімді (Fault-Tolerant) жүйе құрдық. Олай болса, кофелеріңізді дайындап қойыңыздар, біз терең техникалық талдауды бастаймыз!
Міндет: "Бізге жай ғана PayPal қосып беріңіздерші" немесе ескі монолиттің күйреуі
Клиент бізге өте қарапайым болып көрінетін сұраныспен келді: "Бізде қолданыстағы ескі монолитті жүйе бар, бірақ ол транзакциялардың орасан зор ағынына шыдамай, жиі құлап қалады. Бізге PayPal-мен жұмыс істейтін, ешқашан іркіліс бермейтін және өте жылдам жұмыс істейтін жеке кастомды төлем шлюзі қажет". Тәжірибесіз әзірлеушілер бұл жерде: "Ей, мұнда тұрған не бар? PayPal SDK-ны жүктеп аламыз, бір-екі Controller жазамыз, болды емес пе?" деп ойлауы мүмкін.
Алайда, біз клиенттің инфрақұрылымы мен талаптарын зерттей келе, мынадай қиындықтарға тап болдық:
- Асинхронды вебхуктардың (Webhooks) орасан зор ағыны: PayPal төлем статустарының өзгерісін вебхуктар арқылы хабарлайды. Егер сіздің жүйеңіз оларды уақытында өңдеп үлгермесе немесе HTTP 500 қатесін қайтарса, PayPal сұраныстарды қайтадан жібере бастайды, бұл өз кезегінде жүйені одан сайын құрдымға жіберетін "қар көшкіні" (Thundering Herd Problem) әсерін тудырады.
- Қатаң қауіпсіздік және сәйкестік талаптары: Төлем деректерін өңдеу PCI-DSS стандарттарына және жоғары қауіпсіздік деңгейіне сай болуы тиіс.
- Жоғары қолжетімділік (High Availability): Жүйенің тоқтап қалу уақыты (Downtime) SLA бойынша 99.99%-дан төмен болмауы керек. Әрбір секундтық кідіріс мыңдаған доллар шығынға әкеледі.
Архитектура: Google Cloud-та қамал тұрғызамыз
Біз бұл мәселені шешу үшін ескі монолиттің ішіне жаңа код жазудан бірден бас тарттық. Бұл жерде тек қана Cloud Native тәсілі көмектесетінін түсіндік. Біздің таңдауымыз Google Cloud Platform (GCP) экожүйесіне түсті. Төменде біз құрған жаңа микросервистік архитектураның негізгі компоненттері берілген:
1. Есептеу қуаттары: Google Cloud Run
Біз контейнерленген қосымшаларымызды орналастыру үшін Google Cloud Run қызметін таңдадық. Неліктен GKE (Google Kubernetes Engine) емес? Себебі Cloud Run бізге толық басқарылатын (Fully Managed), секунд ішінде нөлден мыңдаған контейнерге дейін автоматты түрде масштабталатын (Autoscaling) және тек нақты пайдаланылған ресурстар үшін ғана ақы төлейтін икемділікті берді. Әрбір микросервис Docker контейнеріне оралып, өте жеңіл әрі жылдам жұмыс істейтін болды.
2. Буферлеу және кезектер: Cloud Pub/Sub
PayPal-дан келетін вебхуктарды тікелей базаға жазу немесе бірден синхронды өңдеу — бұл архитектуралық суицид. Сондықтан біз кіріс трафикті қабылдап алатын және оны сенімді түрде кезекке қоятын Cloud Pub/Sub хабарламалар брокерін енгіздік. Вебхукты қабылдайтын микросервис тек қана сұраныстың дұрыстығын тексеріп (Signature Verification), оны бірден Pub/Sub тақырыбына (Topic) лақтырады да, PayPal-ға HTTP 200 OK жауабын қайтарады. Осылайша, біздің ішкі өңдеу жүйелеріміз қаншалықты жүктелген болса да, кіріс сұраныстарды қабылдау жылдамдығы секундына миллисекундтарды ғана құрады.
3. Деректерді сақтау: Cloud SQL (PostgreSQL) және Redis
Транзакциялық деректердің тұтастығы (ACID қасиеттері) үшін біз Cloud SQL (PostgreSQL) деректер базасын пайдаландық. Алайда, жоғары жүктеме кезінде базаға түсетін қысымды азайту үшін Cloud Memorystore for Redis кэштеу жүйесін алдына қойдық. Redis бізге транзакциялардың статус картасын, идемпотенттілік кілттерін (Idempotency Keys) және жылдам өзгеретін сессиялық деректерді сақтау үшін қажет болды.
Жүйенің өнімділігін арттыру мақсатында біз базамен байланысты оңтайландыру үшін PgBouncer қосылыстар пулын (Connection Pooling) орнаттық. Бұл базаға бір уақытта мыңдаған микросервис даналары қосылған кезде ресурстардың сарқылуын болдырмады.
Жүйе өнімділігінің салыстырмалы талдауы (TPS)
Ескі монолит пен жаңа Cloud-Native архитектурасының пиктік жүктеме кезіндегі өткізу қабілеті (Транзакциялар саны/секунд)
Терең техникалық мәселелер және олардың шешімдері
Енді ең қызықты бөлімге — бізді түн ұйқымыздан айырған нақты техникалық қиындықтарға және оларды қалай шешкенімізге тоқталайық.
Мәселе №1: Идемпотенттілік (Қос төлемнен қорғану)
Қаржылық жүйелердегі ең қорқынышты жағдай — бір транзакция үшін пайдаланушының шотынан ақшаны екі рет ұстап қалу. Желілік үзілістер, клиенттің браузерді қайта жүктеуі немесе PayPal тарапынан вебхуктың екі рет жіберілуі (бұл өте жиі кездеседі) осындай қателіктерге әкелуі мүмкін. Біз бұл мәселені шешу үшін қатаң идемпотенттілік механизмін енгіздік.
Біз отандық компаниялардың сайттарынан үнемі табатын топ-5 архитектуралық "балдақтар" (костыльдер)
Әрбір төлем сұранысы бірегей X-Idempotency-Key тақырыбымен (Header) бірге келуі тиіс. Төлемді өңдеу алдында біз бұл кілттің Redis-те бар-жоғын тексереміз. Төменде осы процестің оңтайландырылған кодтық мысалы берілген:
// Идемпотенттілікті тексеру және құлыптау (Locking)
async function acquireIdempotencyLock(redisClient, key, ttlMs) {
const lockKey = `idempotency:${key}`;
// SETNX командасы арқылы кілтті тек ол жоқ болса ғана жазамыз
const result = await redisClient.set(lockKey, 'PROCESSING', {
NX: true,
PX: ttlMs
});
return result === 'OK';
}Егер acquireIdempotencyLock функциясы false қайтарса, демек бұл транзакция қазіргі уақытта өңделіп жатыр немесе әлдеқашан аяқталған. Бұл жағдайда біз екінші рет төлем жасамай, алдыңғы сұраныстың нәтижесін қайтарамыз.
Мәселе №2: Ақылды Rate Limiter және Exponential Backoff
PayPal API-і шексіз емес. Егер сіз олардың серверлеріне секундына он мыңдаған сұраныс жібере бастасаңыз, олар сіздің сұраныстарыңызды бұғаттап, HTTP 429 Too Many Requests қатесін қайтарады. Біз мұны болдырмау үшін екі деңгейлі қорғаныс жасадық:
- Token Bucket алгоритмі негізіндегі Rate Limiter: Біз Redis-ке негізделген және әрбір клиент үшін рұқсат етілген сұраныстар жиілігін бақылайтын жүйе енгіздік.
- Экспоненциалды кідіріс (Exponential Backoff): Егер PayPal қандай да бір себептермен қате қайтарса, біз сұранысты бірден қайталамаймыз. Біз оны біртіндеп өсетін уақыт аралығымен қайталаймыз. Мысалы, бірінші сұраныс сәтсіз аяқталса, 1 секунд күтеміз, содан кейін 2 секунд, содан кейін 4 секунд, одан кейін 8 секунд және т.б. Бұл ретте желідегі синхрондылықты болдырмау үшін уақытқа кездейсоқ ауытқу (Jitter) қосамыз.
Қателіктер деңгейінің серпіні (Error Rate)
Сұраныстарды қайта өңдеу (Exponential Backoff және Rate Limiting) енгізілгеннен кейінгі сәтсіз транзакциялардың азаюы (пайызбен)
Мәселе №3: Вебхуктардың қолтаңбасын тексеру (Webhook Signature Verification)
Қауіпсіздік тұрғысынан біз бізге келіп түскен әрбір вебхуктың шынымен де PayPal-дан келгеніне сенімді болуымыз керек. Зиянкестер жалған вебхук жіберу арқылы тауарды төленді деп белгілеуге тырысуы мүмкін. PayPal әрбір вебхукпен бірге арнайы қолтаңба тақырыптарын жібереді:
PAYPAL-TRANSMISSION-IDPAYPAL-TRANSMISSION-TIMEPAYPAL-TRANSMISSION-SIGPAYPAL-CERT-URL
Біз бұл қолтаңбаларды тексеруді Cloud Run ішіндегі жеке шағын микросервиске жүктедік. Ол сертификаттарды кэштеп, криптографиялық тексерулерді жылдам орындайды, бұл негізгі өңдеуші сервистердің ресурстарын үнемдейді.
Реалистік сынақ: "Қара жұма" (Black Friday) дауылы
Жүйені толық іске қосар алдында біз оған нақты стресс-тест жасадық. Біз Locust құралын пайдалана отырып, пиктік жүктемені симуляцияладық. Сынақ барысында секундына 5,000 транзакцияға дейін трафик жіберілді.
Бастапқыда деректер базасындағы транзакциялық құлыптарға (Row Locks) байланысты кішігірім іркілістер байқалды. Біз деректер базасының оқшаулау деңгейін (Transaction Isolation Level) оңтайландырып, Read Committed режиміне ауыстырдық және индекстерді қайта құрдық. Осыдан кейін жүйе бірде-бір қатесіз, мінсіз жұмыс істеп кетті.
Нақты өндіріске енгізілгеннен кейін, бірінші "Қара жұма" кезінде біздің кастомды шлюзіміз миллиондаған транзакцияларды бірде-бір сәтсіздіксіз (Zero Failed Transactions) өңдеп шықты. Клиенттің техникалық директоры (CTO) бізге хабарласып, жүйенің тұрақтылығына таңғалғанын жеткізді.
Қорытынды
Бұл жоба бізге үлкен тәжірибе сыйлады. Біз күрделі финтех интеграцияларының жай ғана код жазу емес, ең алдымен дұрыс архитектура құру екенін тағы да дәлелдедік. Google Cloud құралдарын (Cloud Run, Pub/Sub, Cloud SQL) дұрыс үйлестіру және идемпотенттілік, кэштеу, ақылды қайталау сияқты инженерлік шешімдерді қолдану кез келген жоғары жүктемелі жобаның сәтті болуының кепілі болып табылады.
Егер сіздің де жобаңызда осындай күрделі инфрақұрылымдық немесе финтех мәселелері туындаса — біз әрқашан көмектесуге дайынбыз. Тәжірибелі инженерлер командасы кез келген қиындықты еңсеруге қауқарлы. Біздің блогымызға жазылыңыздар және жаңа техникалық кейстерді күтіңіздер. Сәттілік, әріптестер!

Рустам Шарафутдинов
Google Cloud архитектурасы саласындағы сарапшы және 15 жылдан астам тәжірибесі бар Senior Full-Stack әзірлеушісі. Ақауға төзімді архитектураларға, жоғары жүктемелі жобаларды оңтайландыруға және AI (Vertex AI) интеграциясына маманданған.