SDK-гүй хэлүүд¶
Node, Go-оос өөр хэлээр (PHP, Python, Java, C#, Ruby …) холбогдох нь энгийн: API нь ердийн HTTPS + JSON, нэвтрэлт нь HTTP Basic, webhook-ийн гарын үсэг нь нэг HMAC. Энэ хуудсаар та өөрийн хэлэндээ жижиг client ба webhook шалгагч бичнэ — 30–60 минут.
- Хэрэгтэй: апп-ын API түлхүүр —
client_id(gk_test_…/gk_live_…, эсвэл «Анхдагч түлхүүр»-ийнmapp_test_…/mapp_…) ба secret (gks_…/gma_cs_…), webhook endpoint-ийн secret (whsec_…), HTTP client, HMAC-SHA256 бүхий crypto сан.
1. HTTP API товчоор¶
| Зүйл | Дүрэм |
|---|---|
| Суурь URL | https://geregeapp.mn/api/v1/miniapp |
| Нэвтрэлт | Authorization: Basic base64(client_id:secret) — ганц зам. Түлхүүр бүр scope-той: route-ийн scope дутуу бол 403 insufficient_scope + required_scope («Анхдагч түлхүүр» тухайн орчны бүх scope-той). Test ба live түлхүүр тус тусдаа — түлхүүрийн орчин л харагдана (livemode) |
| Mini app-ын хүний token | Authorization: Bearer gma_at_… (app intent үүсгэх, /me) — нээлт ба token |
| Body | Content-Type: application/json, UTF-8. Мэдэхгүй талбар илгээвэл 400 invalid_request — зөвхөн баримтжуулсан талбарыг илгээ |
| Мөнгө | Бүхэл төгрөг (3500 = 3 500₮), currency нь MNT |
| Цаг | RFC 3339, UTC (2026-10-22T09:00:00Z) |
Idempotency-Key |
Intent үүсгэх, буцаалтад заавал: 1–128 хэвлэгдэх ASCII, зайгүй. Сүлжээний алдаа, 429, 5xx-д ижил key-ээр дахин |
| Алдаа | Модулийн {"error": "<монгол текст>", "code": "<код>", …} эсвэл OAuth-ын {"error": "<код>", "error_description": "…"} — code-оор салаал (Алдааны код) |
| Хуудаслалт, хязгаар | API-ийн нийтлэг дүрэм, Хязгаар |
curl -s -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
-H "Content-Type: application/json" -H "Idempotency-Key: order_8812" \
-d '{"amount": 3500, "description": "Ус 0.5л", "reference": "order_8812", "presentment": "qr"}' \
https://geregeapp.mn/api/v1/miniapp/payment-intents
Client бичихдээ:
- Алдааны хариуг уншихдаа:
codeталбар байвал тэр нь код,errorнь хүнд харуулж болох мессеж;codeбайхгүй чerrorнь[a-z_]+хэлбэртэй бол (OAuth)errorнь код; аль нь ч биш бол ({"error": "not found"},429,500) кодыг HTTP статусаар (http_404г.м.) нэрлэ — SDK-ууд яг ингэдэг. X-Request-Idхариуны толгойг log-д хадгал — Тусламж-д хэрэгтэй.- Timeout 20–30 секунд; дахин оролдохдоо exponential backoff + ижил
Idempotency-Key. - Client secret зөвхөн сервер дээр — браузер, мобайл апп, терминалд хэзээ ч (Аюулгүй байдал).
2. Webhook шалгах¶
Алгоритм, толгойнууд, нарийн зүйлс ба Node, Go, Python, PHP-ийн бэлэн функц — Гарын үсэг шалгах.
Доор Java ба C# — ижил логик (|now − t| ≤ 300, аль нэг v1, constant-time, жижиг үсэгтэй hex):
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HexFormat;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public final class GeregeWebhook {
public static boolean verify(byte[] rawBody, String header, List<String> secrets, long now) throws Exception {
long t = -1;
List<byte[]> sigs = new ArrayList<>();
for (String part : (header == null ? "" : header).split(",")) {
String p = part.trim();
int i = p.indexOf('=');
String key = i < 0 ? p : p.substring(0, i);
String value = i < 0 ? "" : p.substring(i + 1);
if (key.equals("t") && value.matches("[0-9]{1,18}")) t = Long.parseLong(value);
else if (key.equals("v1")) sigs.add(value.getBytes(StandardCharsets.US_ASCII));
}
if (t < 0 || sigs.isEmpty() || Math.abs(now - t) > 300) return false;
boolean ok = false;
for (String secret : secrets) {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
mac.update((t + ".").getBytes(StandardCharsets.UTF_8));
byte[] want = HexFormat.of().formatHex(mac.doFinal(rawBody)).getBytes(StandardCharsets.US_ASCII);
for (byte[] sig : sigs) ok = MessageDigest.isEqual(sig, want) | ok;
}
return ok;
}
}
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public static class GeregeWebhook
{
public static bool Verify(byte[] rawBody, string? header, IEnumerable<string> secrets, long now, long tolerance = 300)
{
long t = -1;
var sigs = new List<byte[]>();
foreach (var part in (header ?? "").Split(','))
{
var p = part.Trim();
var i = p.IndexOf('=');
var key = i < 0 ? p : p[..i];
var value = i < 0 ? "" : p[(i + 1)..];
if (key == "t" && value.Length is > 0 and <= 18 && value.All(char.IsAsciiDigit)) t = long.Parse(value);
else if (key == "v1") sigs.Add(Encoding.ASCII.GetBytes(value));
}
if (t < 0 || sigs.Count == 0 || Math.Abs(now - t) > tolerance) return false;
var ok = false;
foreach (var secret in secrets)
{
using var mac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var message = Encoding.UTF8.GetBytes($"{t}.").Concat(rawBody).ToArray();
var want = Encoding.ASCII.GetBytes(Convert.ToHexString(mac.ComputeHash(message)).ToLowerInvariant());
foreach (var sig in sigs) ok = CryptographicOperations.FixedTimeEquals(sig, want) | ok;
}
return ok;
}
}
Body-г framework-ийн parse хийхээс өмнө byte-аар ав (Spring: @RequestBody byte[]; ASP.NET Core:
Request.EnableBuffering() + Request.Body-г MemoryStream-д хуулах).
3. Өөрийн кодоо шалгах¶
- Платформын өөрийн кодоос үүсгэсэн тест векторууд (
vectors.json: гарын үсэг зурах ба шалгах 20 тохиолдол) SDK-уудын хамт түгээгддэг. SDK нийтлэгдэх хүртэл файлыг Тусламж-аас ав; түүнийг өөрийн unit test-д ачаалжwebhook.verification[]бүртvalid-тай тэнцэх эсэхийг шалга (жишээ). - Векторгүйгээр: Гарын үсэг шалгах-ийн «Ажилласан жишээ»-ний secret,
t, body-оорaab41441…c9821гарч байгаа эсэх. - Бодит хүргэлтээр: консолын «Тест илгээх» (
webhook.test) — tunnel-ээр локал машин руу (Локал webhook).