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-ы текст хэлбэрээс шууд
int64minor 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/statuslong-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