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

Аюулгүй байдлын checklist

Live болохоос өмнө, дараа нь улирал бүр энэ жагсаалтаар интеграцаа шалгана. Хугацаа: 30 минут. Хэрэгтэй: таны backend-ийн код, тохиргоо ба консолын хандалт. Загварын тайлбар: Аюулгүй байдлын загвар.

Secret хадгалалт

  • API түлхүүрийн secret (gks_live_…, gks_test_…; хуучин анхдагч түлхүүрийн gma_cs_…) ба webhook secret (whsec_…) зөвхөн сервер дээр — орчны хувьсагч эсвэл secret manager-т. Мобайл апп, браузер, JavaScript, терминал, вендинг машин, POS, kiosk дотор хэзээ ч: тэд таны backend-ийг дуудна, таны backend Gerege-ийг.
  • Secret git репод, Docker image-д, log-д, алдааны мессежид, analytics-д ороогүй (.env нь .gitignore-д). Client id (gk_…, mapp_…) нууц биш — log-д бичиж болно.
  • Mini app-ын access ба refresh token (gma_at_…, gma_rt_…) зөвхөн backend-д; frontend-д өөрийн session cookie л.
  • Test ба live түлхүүр тусдаа хадгалагдсан; live secret-ийг хэрэгтэй хүн л харна.

Түлхүүр — хамгийн бага эрх, солих, хүчингүй болгох

  • Сервер бүрт тусдаа, нэртэй түлхүүр («checkout-api», «bi-readonly») — нэг нь алдагдвал зөвхөн түүнийг зогсооно. Апп дотор орчин бүрт идэвхтэй ≤ 20.
  • Хамгийн бага scope: preset-ээс эхэлнэ — checkout_server (payments:read, payments:write, refunds:write, events:read), read_only (BI, нягтлан), terminal_installer … Scope-ыг дараа нь өөрчлөхгүй (400 scopes_immutable) — өөр эрх бол шинэ түлхүүр. Дутуу бол 403 insufficient_scope + required_scope.
  • Солих (roll): POST /api/v1/miniapp/dev/apps/{id}/keys/{kid}/roll {"overlap_seconds": …} — ижил нэр, scope-той шинэ secret; хуучин нь давхцлын хугацаанд (0 – 7 хоног, анхдагч 24 цаг) ажилласаар. Тэр хугацаанд шинийг deploy хийнэ — тасалдалгүй. expires_at өгсөн түлхүүрийн хугацаа дуусахаас өмнө үүсгэсэн хүн ба админууд мэдэгдэл авна.
  • Live түлхүүр: үүсгэх, солих, IP өөрчлөх нь miniapp.keys.live ба сүүлийн 10 минутын нэвтрэлт шаардана (403 fresh_sign_in_required → дахин нэвтэрч давтана). Анхдагч түлхүүрийн live secret (POST …/credentials {"environment": "live"}) ч мөн адил; тэр солилт шууд, давхцалгүй — deploy-оо урьдчилан бэлдэнэ, эсвэл шинэ API-ын roll-ыг (давхцалтай) хэрэглэнэ. Live түлхүүрийн үүсгэлт, солилт, хүчингүй болголт байгууллагын админуудад мэдэгдэл болно.
  • Хүчингүй болгох: POST …/keys/{kid}/revoke {"reason": "…"} (шалтгаан заавал, ≤ 200) — шууд, дахин нэвтрэлт шаардахгүй (хамгаалах үйлдлийг хэзээ ч удаашруулахгүй). Хүчингүй, хугацаа дууссан, солигдсон түлхүүр бүгд адилхан 401 invalid_client.
  • IP allowlist (allowed_ips, ≤ 20 хаяг/CIDR): live server-ийнхээ гарах хаягийг тавина — бусад хаягаас 403 ip_not_allowed (ip — платформын харсан хаяг, итгэмжлэгдсэн proxy-ийн ард). Live түлхүүрийн allowlist-ийг өөрчлөх нь дахин нэвтрэлт шаардана.
  • Webhook secret: endpoint-ийн POST …/webhook-endpoints/{eid}/rotate-secret — шинэ whsec_…, хуучин нь 24 цаг хамт гарын үсэг зурна (хоёр v1). Тэр хугацаанд handler [шинэ, хуучин] хоёуланг зөвшөөрнө, дараа нь хуучныг хасна. Endpoint №1-д (POST …/webhook {"url"}) дуудлага бүр шинэ secret олгоно.

Webhook

  • Event бүрийн гарын үсгийг түүхий body-оор (JSON parse хийхээс өмнө), constant-time харьцуулалтаар, бүх v1-ийг шалгана; |now − t| ≤ 300 секунд (Гарын үсэг шалгах).
  • Гарын үсэггүй, буруу гарын үсэгтэй хүсэлтийг 400-аар татгалзаж, юу ч хийхгүй.
  • Gerege-Event-Id-аар давхардлыг арилгана; бараа олгохоос өмнө intent-ийг API-аас дахин уншина (Idempotent handler).
  • Endpoint нь https://, нийтийн хаяг; test ба live-д тусдаа endpoint (өөр secret); нийтийн «request bin»-д live event хэзээ ч.

Төлбөрийг шалгах

  • Дүн үргэлж таны серверээс. Intent-ийг таны backend өөрийн захиалгын дүнгээр үүсгэнэ; дүн, тайлбарыг браузер, апп, машинаас ирсэн утгаар хэзээ ч.
  • return_url?pi=…-д итгэхгүй. Redirect checkout-ын дараа хүн таны хуудсанд ?pi=pi_…-тэй буцна — үүнийг хэн ч гараар бичиж болно. Серверээсээ өөрийн түлхүүрээр GET /payment-intents/{id} хийж status (succeeded/requires_capture), amount, reference, livemode-ыг захиалгатайгаа тулгана.
  • Mini app-ын bridge-ийн requestPayment хариу, Gerege App-ын дэлгэц, URL — аль нь ч төлбөрийн нотолгоо биш; нотолгоо нь таны backend-ийн GET эсвэл гарын үсэг шалгасан webhook.
  • Нэг захиалга = нэг intent = нэг Idempotency-Key (захиалгын дугаар). Давталт ижил key-ээр — шинэ key давхар төлбөр үүсгэнэ (Давталт).

Терминал ба sticker

  • Терминал, sticker, NFC tag, QR secret барихгүй — зөвхөн терминалын URL. Машин таны backend-ээс сагсаа асууна.
  • NFC tag бүрийг бичсний дараа түгжинэ (Gerege App-ын «Sticker бичих» шалгаад түгждэг); sticker-ийг хуурамчаар дээгүүр нь наасан эсэхийг тогтмол шалгана (NFC-г турших).
  • Терминалын label, location-ийг тодорхой бичнэ — Gerege App-ын төлөх цонх Face ID-аас өмнө бүртгэлтэй нэр, байршлыг харуулдаг тул хүн буруу машиныг танина.
  • Хүн явчихсан claim-ийг POST /payment-intents/{id}/unclaim-аар суллана; баталгаажаагүй claim 90 секундэд өөрөө дуусна, нэг хүн нэг дор 3-аас олон баталгаажаагүй claim барихгүй.
  • Терминал бүртгэх, засах түлхүүр (terminals:write) нь машин дээр биш, таны сервер дээр; суулгагчид terminal_installer preset-тэй тусдаа түлхүүр.
  • Уншилт бүрт гарын үсэгтэй URL (NTAG 424 DNA, SUN) — «удахгүй».

Хүнд харагдах текст

  • description (≤ 80 тэмдэгт) хүний төлөх цонхонд гарна — захиалгын товч («Захиалга #8812», «Ус 0.5л»). URL, утасны дугаар, «нууц код», «баталгаажуулах код илгээ» мэт хэллэг хэзээ ч.
  • Тамхи, согтууруулах ундааны бүтээгдэхүүний нэр description-д бичихгүй (App Store-ийн дүрэм 1.4.3); насны шалгалт машин/таны талд, эсвэл min_age.
  • Буцаалтын reason таны API ба webhook-д л харагдана, хүний мэдэгдэлд биш — гэхдээ хувийн мэдээлэл бүү бич.

Хувийн мэдээлэл

  • Худалдагч хүний нэр, регистр, данс, утсыг хэзээ ч авахгүй. user_ref нь claim бүрт санамсаргүй — нэг хүнийг төлбөр хооронд холбох тогтвортой id биш (Хүний мэдээлэл).
  • Mini app хүний pairwise sub-ийг (апп бүрт өөр) ба зөвхөн зөвшөөрсөн scope-ийн мэдээллийг (profile.email, profile.phone) авна (Mini app-ын нээлт ба token).
  • Авсан мэдээллээ хуулийн дагуу хадгалж, нууцлалын бодлогодоо тодорхой бичнэ; шаардлагагүй болсныг устгана.

Secret илрүүлэх

Угтварууд нь scanner-т зориулагдсан: gks_live_ / gks_test_ (API түлхүүрийн secret — сүүлдээ 6 тэмдэгт CRC32 checksum-тай тул DB-гүйгээр жинхэнэ хэлбэрийг шүүнэ), gma_cs_ (анхдагч түлхүүрийн secret), whsec_ (webhook secret), gml_ (launch code), gma_at_ (access token), gma_rt_ (refresh token). Client id (gk_live_ / gk_test_ + 20 тэмдэгт, mapp_ / mapp_test_) нь нууц биш. Жишээ нь gitleaks-ийн дүрэм:

[[rules]]
id = "gerege-api-key-secret"
description = "Gerege App API key secret"
regex = '''gks_(live|test)_[A-Za-z0-9_-]{43}_[0-9A-Za-z]{6}'''

[[rules]]
id = "gerege-client-secret"
description = "Gerege App default key secret"
regex = '''gma_cs_[A-Za-z0-9_-]{43}'''

[[rules]]
id = "gerege-webhook-secret"
description = "Gerege App webhook secret"
regex = '''whsec_[A-Za-z0-9_-]{43}'''

[[rules]]
id = "gerege-token"
description = "Gerege App access / refresh token"
regex = '''gma_(at|rt)_[A-Za-z0-9_-]{43}'''

Secret алдагдвал

  1. Тэр дор нь зогсооно — API түлхүүр: POST …/keys/{kid}/revoke (шууд); анхдагч түлхүүр: POST …/credentials (хуучин шууд үхнэ); webhook secret: rotate-secret (хуучин 24 цаг хамт ажиллана — алдагдсан бол handler-аасаа хуучныг шууд хас).
  2. Шинэ secret-ийг deploy хийнэ.
  3. Консолын audit-аас (key_created, key_rolled, key_revoked, credentials_rotated, endpoint-ийн өөрчлөлт …) хэн юу хийснийг, түлхүүрийн last_used_at-ийг, төлбөр/буцаалтын жагсаалтаас танихгүй үйлдлийг шалгана.
  4. Тусламж-тай холбогдоно — secret-ийг өөрийг нь хэзээ ч бүү илгээ.