توثيق الواجهة البرمجية ودليل المطوّر
v1 · إصدار حيّالبدء السريع
يتم البدء باستقبال المدفوعات المشفّرة على موقعك في ثلاث خطوات: أنشئ مفتاح واجهة برمجية، وسجّل عناوين محافظ التحصيل، ثم افتح فاتورتك الأولى. ولأن النظام غير احتجازي، تنتقل التحصيلات مباشرة إلى محفظة تسيطر عليها أنت.
POST /v1/merchants/{merchant_id}/api-keys— يُصدر زوج مفاتيح الواجهة الذي توقّع به طلباتك.POST /v1/addresses— يسجّل العنوان الثابت لتحصيل الرموز مثل USDT (عنوان واحد لكل شبكة وأصل).POST /v1/sweep-addresses— يسجّل المحفظة الرئيسية التي تُجمَّع فيها تحصيلات العملات الأصلية مثل TRX وBNB وGRAM.POST /v1/webhooks— يسجّل عنوان URL الذي نُشعِر عليه نظامك فور اكتمال الدفع.
توقيع الطلبات (HMAC)
تُوقَّع جميع طلبات HTTP بمعيار HMAC-SHA256. ويُحتسب التوقيع على جسم الطلب الخام (raw bytes)، فيبقى صالحاً حتى لو تغيّر ترتيب مفاتيح JSON.
METHOD \n PATH \n TIMESTAMP \n NONCE \n SHA256(raw_body)- X-Api-Key
- معرّف مفتاح الواجهة (pk_…)
- X-Timestamp
- طابعة زمنية Unix بالثواني (مع تسامح لساعة الخادم)
- X-Nonce
- سلسلة عشوائية فريدة (128 محرفاً كحد أقصى، تُستخدم مرة واحدة)
- X-Signature
- hex(hmac_sha256(secret, canonical))
يحمي الطلبَ مستويان: يجب أن تقع الطابعة الزمنية ضمن نطاق التسامح، وألا تُستخدم قيمة nonce إلا مرة واحدة. أما إعادة إرسال الطلب نفسه فتُرفض تلقائياً (HTTP 401).
import hashlib, hmac, os, time, json, urllib.request KEY_ID = os.environ["COINZEN_KEY_ID"]SECRET = os.environ["COINZEN_SECRET"]BASE = "https://coinzen.cerceyn.com" def request(method: str, path: str, payload: dict | None = None) -> bytes: body = json.dumps(payload).encode() if payload is not None else b"" ts = str(int(time.time())) nonce = os.urandom(16).hex() canonical = "\n".join( [method.upper(), path, ts, nonce, hashlib.sha256(body).hexdigest()] ) signature = hmac.new(SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest() req = urllib.request.Request( BASE + path, data=body or None, method=method.upper(), headers={ "X-Api-Key": KEY_ID, "X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": signature, "Content-Type": "application/json", }, ) with urllib.request.urlopen(req) as resp: return resp.read()دورة حياة الفاتورة
يبدأ إنشاء الفاتورة آلة حالات. فإن لم تُحدَّد الشبكة والأصل، تبدأ الفاتورة بحالة selecting. وحين يختار العميل الشبكة والعملة في صفحة الدفع، تنتقل إلى pending ويُخصَّص لها عنوان دفع.
selecting→pending,expired,cancelledpending→confirming,partially_paid,expired,cancelledconfirming→paid,overpaid,failedpaid⇄overpaid— إذا أرسل العميل تحويلاً إضافياً على فاتورة مدفوعة، يُحدَّث المبلغ تلقائياً.
الحالات paid وoverpaid وexpired وcancelled وfailed نهائية. وعند حدوث إعادة تنظيم على السلسلة أو دفعة متأخرة، يحدّث النظام الحالة تلقائياً ويُبلغ نظامك عبر إشعار Webhook.
amount_exact بقيمة true، على العميل تحويل المبلغ الصافي المحدَّد تماماً، كي تتم المطابقة فوراً ودون لبس.إشعارات Webhook
يُرسَل كل تغيّر في حالة الدفع فوراً إلى نقطة Webhook المسجّلة لديك. ويُوقَّع كل إشعار بمعيار HMAC-SHA256، فتتحقّق بثقة من سلامة البيانات ومن أنها صادرة عن Coinzen.
- X-Coinzen-Signature
- t=<unix>,v1=<hex hmac_sha256(secret, "<t>.<raw_body>")>
- X-Coinzen-Delivery
- معرّف التسليم الفريد (UUID)
- X-Coinzen-Event
- اسم الحدث المُطلَق (مثل invoice.paid)
import hashlib, hmac, time def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool: """X-Coinzen-Signature: t=<unix>,v1=<hex>""" parts = dict(p.split("=", 1) for p in header.split(",")) t, v1 = parts["t"], parts["v1"] if abs(time.time() - int(t)) > tolerance: return False expected = hmac.new( secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, v1)الأحداث المدعومة
invoice.pendinginvoice.confirminginvoice.partially_paidinvoice.paidinvoice.overpaidinvoice.expiredinvoice.cancelledpayment.reverteddeposit.unmatchedcredit.lowcredit.exhausted
آلية إعادة المحاولة
تجري المحاولة الأولى فور اكتمال الدفع. فإن لم يستجب خادمك بـ 200 OK، تُعاد المحاولة تلقائياً على سبع مراحل بفواصل 30 · 120 · 600 · 3600 · 21600 · 86400 ثانية. وإن أخفقت المحاولات جميعها، ينتقل السجل إلى حالة dead_letter ويمكن إعادته إلى الطابور بنقرة واحدة من لوحة التحكم.
العمولة والرصيد
لا تقتطع Coinzen عمولتها من دفعة العميل إطلاقاً. فمدفوعات USDT وغيرها تصل إلى عنوانك مباشرة، ومفاتيح تلك المحفظة بحوزتك وحدك. أما رسوم الخدمة فتُخصم من رصيد العمولة الذي تشحنه مسبقاً، فيبقى تحصيلك كاملاً.
- السعر المعتاد 1%. وتحصل الحسابات الجديدة على 0.1% خلال أول 45 يوماً. ويمكنك الاطّلاع على سعرك الحالي في حقل
fee_bpsضمن استجابةGET /v1/credit(بنقاط الأساس: 10 = 0.1%، 100 = 1%). - لا تُحتسب العمولة إلا على الفواتير المكتملة بنجاح (
paidوoverpaid). - لا تُؤخذ أي عمولة على الفواتير الملغاة أو المنتهية أو الناقصة.
- عند نفاد رصيدك، تستمر معالجة الفواتير المفتوحة كالمعتاد؛ ولا يتلقّى تنبيه الرصيد (HTTP
402) سوى طلبات إنشاء الفواتير الجديدة.
نموذج الخصم على خطوتين
حين تُدفع الفاتورة، يُفتح قيد العمولة فوراً بحالة pending كي لا تتباطأ متابعة السلسلة، ويُعالَج دون الاعتماد على أي خدمة خارجية. ثم يجري تحويل سعر الصرف في الخلفية ويُخصم المبلغ الصافي من رصيدك.
وإن سُحبت الدفعة بسبب إعادة تنظيم على السلسلة، يُلغى خصم العمولة فوراً؛ وإن كان قد خُصم، يُعاد إلى رصيدك بقيد reversal.
شحن الرصيد
يمنحك استدعاء POST /v1/credit/deposit-addresses عنوان شحن USDT دائماً خاصاً بك. ويُضاف ما ترسله إلى هذا العنوان إلى رصيدك فور تأكيده على السلسلة — دون موافقة يدوية ودون انتظار.
التعبئة التلقائية
عند تفعيل هذه الخاصية، لا تتوقّف مبيعات متجرك حتى لو بلغ رصيدك صفراً. إذ تُحوَّل حصيلة فاتورتك الصغيرة التالية إلى رصيدك، فتستمر الخدمة دون انقطاع.
- لا تُؤخذ عمولة إضافية على الفاتورة المحوَّلة إلى الرصيد.
- يسري حد أعلى حمايةً لك (25 دولاراً افتراضياً). أما الفواتير التي تتجاوز الحد فلا تُحوَّل، فلا تتعرّض طلباتك الكبيرة للمخاطرة.
- يمكنك تفعيلها أو إيقافها وتحديد حدّك الأعلى في أي وقت عبر
PUT /v1/credit/autoأو من لوحة التحكم.
تنبيهات الرصيد
حين ينزل رصيدك دون العتبة الحرجة يُطلَق حدث credit.low، وعند نفاده يُطلَق credit.exhausted، فتعلم بالأمر مسبقاً.
الشبكات والأصول المدعومة
يلخّص الجدول التالي سجلّ الشبكات الحيّة. وللحصول على القائمة اللحظية، استخدم نقطة GET /v1/networks.
| الشبكة | الأصل | الخانات العشرية | نموذج التحصيل | عنوان العقد |
|---|---|---|---|---|
| TRON | TRX | 6 | عنوان ديناميكي لكل فاتورة | عملة أصلية |
| TRON | USDT | 6 | عنوان ثابت + مبلغ فريد | TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t |
| BSC | BNB | 18 | عنوان ديناميكي لكل فاتورة | عملة أصلية |
| BSC | USDT | 18 | عنوان ثابت + مبلغ فريد | 0x55d398326f99059fF775485246999027B3197955 |
| TON | GRAM | 9 | عنوان ديناميكي لكل فاتورة | عملة أصلية |
| TON | USDT | 6 | عنوان ثابت + مبلغ فريد | EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs |
نقاط الواجهة البرمجية
| الطريقة | المسار | الوظيفة | نطاق الصلاحية |
|---|---|---|---|
| GET | /healthz | فحص سلامة الخدمة | public |
| GET | /readyz | حالة التبعيات والجاهزية | public |
| GET | /v1/networks | الشبكات والأصول المدعومة | public |
| POST | /v1/invoices | إنشاء فاتورة دفع | invoices:write |
| GET | /v1/invoices | سرد الفواتير (مع ترقيم الصفحات) | invoices:read |
| GET | /v1/invoices/{invoice_id} | جلب تفاصيل الفاتورة | invoices:read |
| POST | /v1/invoices/{invoice_id}/cancel | إلغاء الفاتورة | invoices:write |
| GET | /v1/rates | الحصول على عرض سعر قابل للتثبيت | invoices:read |
| GET | /v1/balances | أرصدة المحافظ بحسب الشبكة والأصل | invoices:read |
| GET | /v1/deposits/unmatched | سرد التحويلات الواردة غير المطابَقة | invoices:read |
| POST | /v1/deposits/{deposit_id}/attach | ربط تحويل وارد بفاتورة يدوياً | invoices:write |
| POST | /v1/addresses | تسجيل محفظة تحصيل ثابتة | invoices:write |
| GET | /v1/addresses | سرد العناوين الثابتة المسجّلة | invoices:read |
| POST | /v1/sweep-addresses | تسجيل محفظة تجميع للعملات الأصلية | invoices:write |
| GET | /v1/credit | رصيد العمولة الحالي | invoices:read |
| GET | /v1/credit/entries | حركات الرصيد وسجلّ العمليات | invoices:read |
| POST | /v1/credit/deposit-addresses | الحصول على عنوان شحن الرصيد | invoices:write |
| PUT | /v1/credit/auto | تحديث إعداد التعبئة التلقائية | invoices:write |
| PUT | /v1/merchants/{merchant_id}/credit/fee | تحديث سعر العمولة الخاص بالمتجر | admin |
| POST | /v1/webhooks | تسجيل نقطة Webhook | webhooks:write |
| GET | /v1/webhooks/deliveries | سجلّ الإشعارات والتسليم | webhooks:read |
| POST | /v1/webhooks/deliveries/{delivery_id}/replay | إعادة إرسال إشعار فاشل | webhooks:write |