Агуулгыг алгасах

Gerege Wallet

Хагас · Давхарга 4 — Салбар продукт · Репо: wallet-gerege-mn · wallet.gerege.mn · api.wallet.gerege.mn

Иргэний цахим түрийвч — eID-ээр нэвтэрч, үлдэгдлээ хараад, IBAN-аар шилжүүлэг хийдэг бүтээгдэхүүн. Санхүүгийн цөмд Apache Fineract ажиллана.

Гар утасны апп (iOS SwiftUI, Android Compose) нь гол гадаргуу; вэб нь туслах консол.

Мөнгөний архитектур — хамгийн чухал дүрэм

Fineract бол мөнгөний цорын ганц эх сурвалж. Үлдэгдэл, гүйлгээ, дэвтэр бүгд тэнд. PostgreSQL нь ЗӨВХӨН иргэн ↔ Fineract-ийн ID-уудын зураглал, idempotency бичлэг, хэрэглэгчийн тохиргоог хадгална.

Давхарга Хариуцах зүйл
Wallet backend (Go) Танилт, эрх, урсгал, аудит
Apache Fineract 1.15 Данс, гүйлгээ, үлдэгдэл, давхар бичилт, дэвтэр
PostgreSQL Зөвхөн зураглал + тохиргоо — үлдэгдэл ХЭЗЭЭ Ч энд байхгүй

Гурван дүрэм үүнээс гарна:

  • Үлдэгдлийг кэшлэхгүй. /accounts/balance нь Fineract-ээс шууд уншина — хоёр систем зөрөх нөхцөл үндсээрээ үүсэхгүй.
  • Мөнгийг float болгохгүй. JSON-ы текст хэлбэрээс шууд int64 minor unit болгоно (₮ бол мөнгө = ₮×100). Дугуйлалтын алдаа гарах зам хаалттай.
  • Гүйлгээ бүр idempotent. Дор дэлгэрэнгүй.

Яагаад бэлэн core banking вэ?

Санхүүгийн дэвтэр нь зөв бичихэд хэцүү, алдаа нь үнэтэй домэйн: давхар бичилт, тэнцэл, хаалт, аудитын мөр. Fineract эдгээрийг олон жил production-д шалгагдсанаар шийдсэн. Бид дээр нь identity ба үйлчилгээний давхаргыг л нэмнэ.

Иргэн ↔ данс ↔ IBAN

Иргэн бүрд яг нэг Fineract харилцагч, нэг хадгаламжийн данс, нэг IBAN ногдоно. Холбоосын түлхүүр нь иргэний civil_id:

civil_id ──► externalId = PNOMN-<CIVIL_ID> ──► Fineract харилцагч + данс
                                              savings account ID
                                                      IBAN

Монголын IBAN — 20 тэмдэгт:

MN | kk | bbbb | aaaaaaaaaaaa
 2 |  2 |    4 |           12
 │    │     │      └─ дансны дугаар (Fineract savings ID, зүүнээс тэглэсэн)
 │    │     └──────── банк/байгууллагын код (4 орон)
 │    └────────────── mod-97 хяналтын орон
 └─────────────────── улсын код

Дансны 12 орон нь Fineract-ийн ID-аас гардаг тул нэмэлт дараалал (sequence) хэрэггүй, буцаах зураглал нь цэвэр арифметик.

Түлхүүр нь civil_id — дотоод user_id БИШ

Fineract-ийн externalId-г PostgreSQL-ийн user_id (мөр үүсэх бүрд шинэ UUID)-аас гаргаж байсныг зассан. Тэр үед DB дахин байгуулагдвал иргэн ↔ данс холбоос бүрмөсөн тасарч, дахин нэвтрэхэд шинэ данс, шинэ IBAN үүсч, хуучин үлдэгдэл өнчирдөг байв.

civil_id нь насан туршийн танигч (eID хэрэглэгч бүрд заавал байдаг) тул түүнээс гаргасан түлхүүр нь DB-ээс хамааралгүй. Данс нээхийн ӨМНӨ Fineract-аас байгаа харилцагч болон дансыг энэ түлхүүрээр хайж, олдвол дахин ашиглана — ингэснээр зураглал алдагдсан ч иргэн ижил IBAN-аа эргүүлж авна.

Шилжүүлгийн зөвшөөрөл

Шилжүүлгийг JWT session зөвшөөрнө. Апп нэвтрэлтийн үед авсан access token-оор /transfer/iban-д хандана.

Давхардлаас хамгаалах: хүсэлт бүр Idempotency-Key толгой авч явна. Ижил түлхүүртэй хоёр дахь хүсэлт шинэ гүйлгээ үүсгэхгүй, ЭХНИЙХИЙН үр дүнг буцаана — сүлжээ тасарч апп дахин илгээвэл мөнгө хоёр удаа явахгүй. Баталгааг DB-ийн UNIQUE (user_id, idempotency_key) эцэслэн барина.

Гарын үсгийн уялтыг хассан

Урьд нь шилжүүлэг бүр GWT канон форматаар дахин хэшлэгдэж, иргэний eID PIN2 гарын үсэгтэй тулгагддаг байсан (WYSIWYS — «харснаа гарын үсэг зурна»). Гурван портын (Go/Kotlin/Swift) байт-тэнцүү хэрэгжилт болон golden fixture-ийн CI шалгалт хамт байв.

Энэ бүхнийг бүтээгдэхүүний шийдвэрээр хассан. Үр дагавар: хүчинтэй access token эзэмшсэн тал мөнгө хөдөлгөж чадна — өмнө нь токен хулгайлагдсан ч PIN2-гүйгээр шилжүүлэг хийх боломжгүй байсан.

eID нэвтрэлт нь хэвээр — зөвхөн ШИЛЖҮҮЛГИЙН гарын үсэг хасагдсан.

API гадаргуу

Нэвтрэлт нь Gerege Platform-ийн суурь давхаргаас ирнэ; түрийвчний endpoint-ууд нь энэ репод.

Метод Зам Юу хийдэг
POST /api/v1/auth/initiate РД-аар eID push илгээнэ
GET /api/v1/auth/status/{sid} Төлөв + токен + IBAN (түрийвч энд нээгдэнэ)
GET /api/v1/accounts/balance Үлдэгдэл (Fineract-ээс шууд)
GET /api/v1/accounts/transactions Дансны хуулга
GET /api/v1/accounts/lookup Хүлээн авагчийн IBAN шалгах
POST /api/v1/transfer/iban Шилжүүлэг (Idempotency-Key шаардана)
GET/DELETE /api/v1/beneficiaries Хадгалсан хүлээн авагчид
POST/DELETE /api/v1/devices/register Push токены бүртгэл
POST /api/v1/pay/code/initiate Нэг удаагийн төлбөрийн QR токен

Хариултын хоёр хэлбэр

  • Flat JSON (дугтуйгүй) — гар утасны апп-ууд. Түрийвч болон гар утасны auth endpoint-ууд энэ хэлбэрийг ашиглана.
  • {status, message, data} дугтуй — вэб BFF.

Шинэ апп endpoint нэмэхдээ flat хэлбэрийг барина. Вэб BFF нь flat хариуг өөрийн клиентийн дугтуйнд хийж дамжуулна.

Апп-ын төлвийн үгсийн сан

Апп-ууд CONFIRMED / REFUSED / TIMEOUT гурвыг л терминал гэж үзнэ. Backend-ийн дотоод eID нэршлийг (COMPLETE/EXPIRED/…) гадагшаа гаргахдаа нэг л газарт буулгана.

Апп нь гэрээг тодорхойлно

iOS болон Android апп-ууд backend-ээс ӨМНӨ бүтээгдсэн бөгөөд дээрх хэлбэрүүдийг хүлээдэг. Апп ба backend зөрвөл backend-ийг засна.

Гар утасны апп-ууд

iOS Android
Технологи SwiftUI Kotlin + Compose
Нэвтрэлт
Үлдэгдэл / хуулга
Шилжүүлэг ⏳ дэлгэц хараахан алга
QR (EMVCo) төлбөр
Push бүртгэл

Апп-ууд eID-ийн домэйн руу шууд хандахгүй — бүх харилцаа api.wallet.gerege.mn-ээр дамжина. Нэвтрэхэд иргэн eID апп руу ирсэн push-аас PIN-ээ оруулна.

Аюулгүй байдал

  • Row-Level Security. API нь superuser БИШ дүрээр DB-д холбогдоно (production-д boot guard шалгана) тул RLS бодлогууд үнэхээр хэрэгжинэ. Per-user хүснэгт бүр өөрийн бодлоготой. Данс нээх, шилжүүлгийн бичлэг үүсгэх зэрэг серверийн итгэмжлэгдсэн бичилт нь тусад нь service дүрээр гүйцэтгэгдэнэ — иргэнд эдгээр хүснэгтэд бичих эрх олгогдоогүй.
  • DB TLS. Хувийн CA-аар PostgreSQL-д TLS тавьсан тул sslmode=verify-full нь бодит утгатай.
  • Нууцын байршил. Бүх нууц /etc/gerege-wallet/*.env-д. Апп-ын .env нь deploy бүрд ДАХИН үүсдэг тул түүнийг гараар засвал дараагийн deploy устгана.
  • Rate limit. /auth/* ~5 хүсэлт/мин (4 KiB body cap), /auth/status long-poll тусдаа сул хязгаарт, мөнгө хөдөлгөх endpoint ~30/мин.
  • Idempotency. Шилжүүлэг бүр Idempotency-Key шаардана — сүлжээ тасарч апп дахин илгээвэл мөнгө хоёр удаа явахгүй. Гарын үсгийн уялт байхгүй тул энэ нь давхардлаас хамгаалах ГОЛ механизм.
  • Fineract тусгаарлалт. Зөвхөн 127.0.0.1:8090 дээр сонсоно — гаднаас хандах зам байхгүй.

Байршуулалт

Docker БИШ — native systemd:

gerege-wallet.slice
├── gerege-wallet-fineract.service   # Fineract 1.15 (JAR, 127.0.0.1:8090)
├── gerege-wallet-api.service        # Go API (127.0.0.1:8080)
└── gerege-wallet-web.service        # Next.js BFF (127.0.0.1:3000)

PostgreSQL, Redis, nginx нь хостын үйлчилгээ. nginx нь wallet.gerege.mn ба api.wallet.gerege.mn-г Let's Encrypt TLS-ээр гаргана.

CD: main руу нэгтгэсний дараа CI ногоон болмогц Deploy workflow ажиллана. Бүх build нь runner дээр хийгдэж, серверт зөвхөн үр дүн хүрнэ — сервер дээр Go/Node toolchain хэрэггүй, ажиллагааны цонх богино. Deploy нь /health төдийгүй хамгаалагдсан зам зөв хариулж байгааг шалгаж байж амжилттай гэж бүртгэнэ.

Одоогийн төлөв

Чадвар Төлөв
eID нэвтрэлт (RP итгэмжлэлтэй) Ажиллаж байна
Түрийвч автоматаар нээгдэх + IBAN олгох Ажиллаж байна
Үлдэгдэл / хуулга Ажиллаж байна
Шинэ түрийвчид угтвар бонус Ажиллаж байна
IBAN шилжүүлэг (JWT-ээр зөвшөөрөгдөх) Хэрэгжсэн, туршилт дутуу
Android шилжүүлэг / QR Төлөвлөгөөнд
App Store / TestFlight Бэлтгэж байна

IBAN-ы банкны код түр утгатай

Одоогийн банк/байгууллагын код нь Монголбанкнаас бодит код авах хүртэлх түр утга. Иргэнд олгосон IBAN өөрчлөгдөх ёсгүй тул бодит хэрэглэгч орсны дараа энэ утгыг солихгүй.

Дэлгэрэнгүй баримт

ARCHITECTURE, DEVELOPMENT, API_CONTRACT, SECURITY баримтууд нь wallet-gerege-mn репогийн backend/docs/ хавтаст (EN/MN хосоор). Гар утасны build-ийн заавар ios/README.md, байршуулалтынх docs/DEPLOYMENT.md.

Холбоотой платформууд: eID Mongolia · G-Sign · Gerege Platform · Gerege Verify