انتقل إلى المحتوى
التوثيق
في هذه الصفحة

دليل التكامل

وثائق المطورين

أنشئوا فاتورة، ووجّهوا العميل إلى صفحة الدفع، واستلموا إشعار webhook موقّعًا عند السداد. ابدأوا بالخطوات الأربع أدناه.

  1. انسخوا مفتاح API الخاص بمشروعكم.
  2. وقّعوا طلبات الكتابة.
  3. أنشئوا أول فاتورة.
  4. تحققوا من إشعار webhook الخاص بالدفع.

ما الذي ندعمه

تقبل البوابة USDT وUSDC. يطابق هذا الجدول GET /v1/services، وقد جرى التحقق منه في 4 نوفمبر 2026. اطّلعوا على الكتالوج الحالي قبل بناء أداة اختيار طريقة الدفع.

الأصلالشبكةالتأكيدات
USDTTron20
USDTEthereum12
USDTSolana32
USDTPolygon8
USDTArbitrum12
USDTTON5
USDCEthereum12
USDCSolana32
USDCPolygon8
USDCBase12
USDCArbitrum12

المدفوعات احتجازية (custodial): تصل الأموال إلى عناوين تتحكم بها المنصة. رصيد التاجر لديكم هو المبلغ المستحق لكم. أما محفظة Telegram المنفصلة فهي غير احتجازية (non-custodial).

القيود

  • المدفوعات الصادرة والاسترداد غير متاحة بعد: يُظهر GET /v1/services القيمتين boundaries.livePayouts وboundaries.liveRefunds على أنهما false.
  • لا يفحص أي مزوّد KYT المدفوعات الواردة.
  • تُرفض الفواتير التي تتجاوز رسومها 20% من المبلغ مع الخطأ AMOUNT_BELOW_MINIMUM.
  • لا توجد اتفاقية SLA تعاقدية.

الرسوم الأولية 0.40% إضافةً إلى رسوم الشبكة. اطلبوا عرض سعر لمعرفة إجمالي الرسوم والحد الأدنى للمبلغ؛ راجعوا الأسعار.

المصادقة

تستخدم الاستدعاءات بين الخوادم الترويسة x-api-key. يقتصر نطاق مفاتيح المشروع على مشروع تاجر واحد. احتفظوا بها على خادمكم.

تبدأ مفاتيح الاختبار بـ ncp_test_…، وتبدأ المفاتيح المباشرة بـ ncp_live_…. أما المفاتيح الصادرة سابقًا فتبدأ بـ ncp_sandbox_، وتظل تعمل.

curl -sS "https://api.veilnebu.la/v1/balances" \
  -H "x-api-key: $PROJECT_API_KEY"

توقيع طلبات الكتابة

تحتاج طلبات GET إلى x-api-key فقط. أما كل طلب POST وPUT وPATCH وDELETE يُصادَق عليه بمفتاح مشروع فيحتاج أيضًا إلى ترويستين، وإلا يردّ API بـ 401 Invalid API signature:

  • x-ncp-timestamp: بصيغة ISO-8601، وفي حدود 5 دقائق من الوقت الحالي.
  • x-ncp-signature: hex(HMAC-SHA256(your API key, "<timestamp>.<METHOD>.<path incl. query>.<raw body>")).

اكتبوا أسماء الطرق بأحرف كبيرة. وقّعوا البايتات التي ترسلونها كما هي بالضبط، بما في ذلك سلسلة الاستعلام في المسار. الطلب الذي لا يحتوي على متن يوقّع سلسلة فارغة. تتطلب الأدوات المساعدة أدناه مفتاح مشروعكم؛ وتتطلب أداة shell المساعدة Bash وcurl وOpenSSL، بينما تستخدم أداة Node.js المساعدة fetch المدمجة.

export API_BASE_URL="https://api.veilnebu.la"
export PROJECT_API_KEY="ncp_test_…"

ncp() {
  local method="$1" path="$2" body="" ts sig
  shift 2
  if [ "$#" -gt 0 ]; then body="$1"; shift; fi
  ts=$(date -u +%Y-%m-%dT%H:%M:%S.000Z) || return
  sig=$(printf '%s.%s.%s.%s' "$ts" "$method" "$path" "$body" |
    openssl dgst -sha256 -hmac "$PROJECT_API_KEY" -hex) || return
  sig=${sig##* }
  if [ -n "$body" ]; then
    curl -sS --request "$method" "$API_BASE_URL$path" \
      -H "x-api-key: $PROJECT_API_KEY" \
      -H "x-ncp-timestamp: $ts" -H "x-ncp-signature: $sig" \
      -H "content-type: application/json" --data-binary "$body" "$@"
  else
    curl -sS --request "$method" "$API_BASE_URL$path" \
      -H "x-api-key: $PROJECT_API_KEY" \
      -H "x-ncp-timestamp: $ts" -H "x-ncp-signature: $sig" "$@"
  fi
}

إنشاء أول فاتورة

أنشئوا مشروعًا في لوحة التحكم، وانسخوا مفتاح API الخاص به، وأنشئوا فاتورة. تحتوي الاستجابة على checkoutUrl: وجّهوا عميلكم إليه.

ncp POST /v1/payments '{
  "amountMinor":"10000",
  "currencyCode":"USD",
  "settlementAssetCode":"USDT",
  "externalId":"order-1001"
}' -H "idempotency-key: order-1001"

مقتطف من الاستجابة. يُنشأ المعرّف لكل فاتورة على حدة.

{
  "id": "cm3k9x2f40001qz8e7h5v1abc",
  "status": "new",
  "amountMinor": "10000",
  "currencyCode": "USD",
  "settlementAssetCode": "USDT",
  "checkoutUrl": "https://app.veilnebu.la/checkout/cm3k9x2f40001qz8e7h5v1abc"
}

amountMinor سلسلة نصية لعدد صحيح بأصغر وحدة في العملة: 10000 = 100.00 USD. لا يُقبل الحقل العشري amount إلا في نقطة نهاية المتصفح /v1/public/payments.

الدفع عبر المتصفح والمصادر (origins) المسموح بها

أنشئوا مفتاحًا قابلًا للنشر (publishable) في لوحة التحكم، وأدرجوا المصادر الدقيقة التي يستخدمها متجركم. ترسله طلبات المتصفح في x-publishable-key إلى POST /v1/public/payments.

يجب أن يكون كلٌّ من المصدر (Origin) وsuccessUrl وcancelUrl ضمن قائمة المصادر المسموح بها للمفتاح. المصدر هو المخطط (scheme) والمضيف والمنفذ الاختياري، دون مسار أو حرف بدل. تُرفض المصادر المفقودة أو غير المدرجة.

يقبل المبلغ العشري حتى رقمين بعد الفاصلة للعملات الورقية، وحتى عدد الخانات العشرية الخاص بالأصل للعملات المشفرة (USDT وUSDC: 6 على كل شبكة ندعمها اليوم).

const response = await fetch('https://api.veilnebu.la/v1/public/payments', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'x-publishable-key': 'pk_test_…',
  },
  body: JSON.stringify({
    amount: '100.00',
    currencyCode: 'USD',
    settlementAssetCode: 'USDT',
    successUrl: 'https://shop.example/thanks',
    cancelUrl: 'https://shop.example/cart',
  }),
});
if (!response.ok) throw new Error(await response.text());
const invoice = await response.json();
window.location.assign(invoice.checkoutUrl);

حالات الدفع

الحالةالإجراء التالي
newمشاركة رابط صفحة الدفع
pendingبانتظار الدفع
confirmingبانتظار التأكيدات
paidجاهزة للتسوية
partially_paidاطلبوا من العميل إكمال الدفع
overpaidمراجعة المبلغ الإضافي المقيَّد
expiredإنشاء فاتورة جديدة
cancelledلا يلزم أي إجراء
failedفتح التفاصيل
refundedلا يلزم أي إجراء

مفاتيح منع التكرار

عند إنشاء الفواتير، أرسلوا Idempotency-Key ثابتًا مشتقًا من معرّف طلبكم. إعادة المحاولة بالمفتاح والمتن نفسيهما تُرجع الفاتورة الأصلية. أما إعادة استخدامه مع متن مختلف فتُرجع 409. أبقوا المفتاح والمتن دون تغيير عند إعادة المحاولة بعد انتهاء المهلة.

راجعوا مثال أول فاتورة للاطلاع على الطلب.

الأخطاء

الأخطاء بصيغة JSON. message سلسلة نصية، أو مصفوفة من السلاسل النصية في أخطاء التحقق.

{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "Invalid API key"
}
حالة HTTPالمعنى والإجراء
400حقل غير صالح؛ يذكر message اسم الحقل. صحّحوه قبل إعادة المحاولة.
401Invalid API key أو Invalid API signature. تحققوا من المفتاح والبايتات الموقّعة والطابع الزمني.
404Payment invoice not found. تحققوا من المعرّف والمشروع.
409Idempotency-Key نفسه مع متن مختلف، أو Insufficient merchant payable balance.
429تم تجاوز حد المعدل. انتظروا قبل إعادة المحاولة.
5xxأعيدوا المحاولة بـ Idempotency-Key والمتن نفسيهما.

إشعارات Webhook (HMAC-SHA256)

يحمل كل تسليم الترويسة x-nebula-signature: t=<unix>,v1=<hex>. تحققوا من HMAC باستخدام سر نقطة النهاية الذي أُعيد عند إنشائها. ارفضوا الطوابع الزمنية التي تبعد أكثر من 300 ثانية عن الوقت الحالي.

التحقق في Node.js

لا تستخدم أداة التحقق في Node.js سوى وحدة crypto المدمجة. مرّروا إليها ترويسة التوقيع وبايتات المتن الخام وسر نقطة النهاية.

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(header, rawBody, secret) {
  const parts = {};
  for (const piece of (header ?? '').split(',')) {
    const separator = piece.indexOf('=');
    if (separator <= 0) continue;
    parts[piece.slice(0, separator).trim()] = piece.slice(separator + 1).trim();
  }
  if (!/^[0-9]{1,16}$/.test(parts.t ?? '')) return false;
  if (!/^[0-9a-f]{64}$/.test(parts.v1 ?? '')) return false;
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = createHmac('sha256', secret)
    .update(parts.t + '.')
    .update(rawBody)
    .digest('hex');
  if (expected.length !== parts.v1.length) return false;
  return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

تحققوا مقابل متن الطلب الخام، قبل تحليل JSON. يتم التسليم مرة واحدة على الأقل: أزيلوا التكرار بالاعتماد على x-nebula-event-id، الذي يطابق eventId في الحمولة. تحتفظ إعادات المحاولة التلقائية بمعرّف التسليم نفسه؛ أما إعادة الإرسال اليدوية فتُنشئ معرّف تسليم جديدًا بمعرّف الحدث نفسه. احفظوا الحدث قبل الرد بـ 2xx.

اختبار إشعارات webhook محليًا

يجب أن تستخدم عناوين URL الخاصة بإشعارات webhook مضيفًا عامًا على الإنترنت. تُرفض العناوين المحلية والخاصة. اكشفوا المستقبِل لديكم عبر نفق HTTPS عام، ثم سجّلوا عنوان URL الخاص به باستخدام أداة التوقيع المساعدة.

ncp POST /v1/webhooks/endpoints '{
  "url":"https://receiver.example/webhook",
  "events":["payment.paid","payment.reorged"]
}'

استبدلوا عنوان URL بعنوان المستقبِل لديكم. احفظوا سر نقطة النهاية الذي يُعاد عند الإنشاء.

أنواع الأحداث

المدفوعات الصادرة والاسترداد غير متاحة بعد (يُظهر GET /v1/services القيمة boundaries.livePayouts على أنها false). وحتى ذلك الحين، تقتصر المدفوعات الصادرة وحزمها على تسجيل التحويل في رصيدكم؛ ولا يعني payout.completed وrefund.completed أن الأموال قد غادرت.

يطابق الكتالوج أدناه GET /v1/services. وجود إدخال في الكتالوج لا يعني أن حركة الأموال المقابلة متاحة فعليًا.

الحدثالمعنى
webhook.testتسليم تجريبي مطلوب لنقطة نهاية webhook.
payment.pendingاختار العميل طريقة دفع؛ بانتظار الإيداع الأول.
payment.confirmingرُصد الإيداع؛ بانتظار التأكيدات المطلوبة.
payment.paidسُدّدت الفاتورة بالكامل وسُوّيت في السجل.
payment.partially_paidاستُلم مبلغ أقل من المطلوب؛ بانتظار الباقي.
payment.overpaidاستُلم مبلغ أكبر من المطلوب؛ سُوّيت في السجل مع المبلغ الزائد.
payment.expiredانتهت صلاحية الفاتورة دون أموال كافية.
payment.cancelledأُلغيت الفاتورة قبل الدفع.
payment.failedفشل الدفع بسبب خطأ في السلسلة أو في سياسة الرسوم أو في الرصد.
payment.risk_flaggedحدث مخاطر استشاري لإيداع مقيَّد بالفعل. فحص KYT للمدفوعات الواردة غير مفعّل.
payment.reorgedأُسقط إيداع مؤكَّد بسبب إعادة تنظيم السلسلة؛ لم تعد الفاتورة مدفوعة، لذا أوقفوا تنفيذ الطلب أو تراجعوا عنه.
payment.unsupported_assetأُودع رمز غير مدعوم؛ ولم يُقيَّد في الرصيد.
payout.processingحالة معالجة الدفعة الصادرة. التحويلات الفعلية غير متاحة.
payout.completedسُجّلت الدفعة الصادرة في الرصيد؛ وهذا لا يعني أن الأموال قد غادرت.
payout.failedفشلت الدفعة الصادرة.
payout.cancelledأُلغيت الدفعة الصادرة قبل اكتمالها.
refund.processingحالة معالجة الاسترداد. عمليات الاسترداد الفعلية غير متاحة.
refund.completedسُجّل الاسترداد في الرصيد؛ وهذا لا يعني أن الأموال قد غادرت.
refund.failedفشل الاسترداد.
internal_transfer.sentنُفّذ تحويل رصيد بين التجار لدى المرسِل.
internal_transfer.receivedقُيّد تحويل رصيد بين التجار لصالح المستلِم.
static_wallet.createdأُنشئ عنوان إيداع طويل الأمد لعميل.
static_wallet.blockedحُظرت محفظة ثابتة.
static_wallet.unblockedرُفع الحظر عن محفظة ثابتة.
static_wallet.topup.completedرُصد إيداع في محفظة ثابتة وسُوّي في السجل.
dispute.openedفُتح نزاع على فاتورة.
dispute.resolvedحُلّ النزاع. الحل وحده لا يثبت حدوث استرداد على السلسلة.
dispute.rejectedرُفض النزاع؛ لا يلزم أي إجراء من التاجر.
subscription.invoice_createdأنشأت دورة فوترة فاتورة جديدة.

سياسة إعادة المحاولة

إذا لم تردّ نقطة النهاية لديكم بـ 2xx، نحاول 8 مرات إجمالًا: المحاولة الأولى، ثم إعادة المحاولة بعد دقيقة واحدة، و5 دقائق، و30 دقيقة، وساعتين، و6 ساعات، و12 ساعة، و24 ساعة. يبدأ كل تأخير بعد المحاولة الفاشلة السابقة. تُحتسب أخطاء الشبكة وانتهاء المهلة أيضًا حالات فشل. للتسليمات التجريبية محاولة واحدة فقط.

بعد آخر فشل، يُعلَّم التسليم على أنه فاشل؛ أعيدوا إرساله باستخدام POST /v1/webhooks/deliveries/{deliveryId}/resend. وكحل احتياطي، أجروا التسوية باستخدام GET /v1/payments/{id}.

المدفوعات الصادرة والاسترداد غير متاحة بعد (يُظهر GET /v1/services القيمة boundaries.livePayouts على أنها false). وحتى ذلك الحين، تقتصر المدفوعات الصادرة وحزمها على تسجيل التحويل في رصيدكم؛ ولا يعني payout.completed وrefund.completed أن الأموال قد غادرت.

حزم المدفوعات الصادرة

تجمع الحزمة سجلات التحويل. تُعاد سياسة الموافقة وعدد الموافقات المطلوب في approvalPolicy وapprovalsRequired. عند وجود عدة موافِقين، لا يمكن لمنشئ الحزمة الموافقة على حزمته. ويجوز للمشغّل المؤهل الوحيد الموافقة على حزمة أنشأها بنفسه خلال جلسته. وافقوا على الحزم باستخدام WebAuthn في لوحة التحكم.

export PROJECT_API_KEY="ncp_test_…"
ncp POST /v1/payout-batches '{
  "assetCode":"USDT",
  "networkCode":"TRON",
  "externalId":"batch-1001",
  "items":[{
    "destinationAddress":"T…",
    "amountMinor":"15000000",
    "externalRef":"recipient-1001"
  }]
}' -H "idempotency-key: batch-1001"

استبدلوا الوجهة بعنوان صالح في مشروع الاختبار الخاص بكم.

الفوترة المتكررة

يُنشئ الاشتراك فاتورة جديدة في كل دورة فوترة ويُبلغ عنها عبر subscription.invoice_created. ولا يخصم من محفظة العميل تلقائيًا. أرسلوا إلى العميل عنوان URL الجديد لصفحة الدفع.

interval: DAY أو WEEK أو MONTH أو YEAR.

ncp POST /v1/subscriptions '{
  "customerRef":"customer-42",
  "assetCode":"USDT",
  "networkCode":"TRON",
  "amountMinor":"9000000",
  "interval":"MONTH",
  "intervalCount":1
}'

يمكنكم الإيقاف المؤقت أو الاستئناف أو الإلغاء باستخدام عمليات دورة الحياة في مرجع API.

فحص المخاطر

تُفحص وجهات المدفوعات الصادرة والاسترداد مقابل قوائم العقوبات العامة قبل إنشاء التحويل؛ وأي تطابق يؤدي إلى رفض الطلب. لا تُفحص المدفوعات الواردة.

سياسة مكافحة غسل الأموال والعقوبات

بوت Telegram للتجار

يعمل @veilnebula_bot مع مشروع واحد في كل مرة. اربطوه في محادثة خاصة باستخدام مفتاح API لذلك المشروع. يحاول البوت حذف رسالتكم ويشفّر المفتاح المخزَّن. إذا فشل الحذف، فألغوا المفتاح واستبدلوه بمفتاح جديد. لا تنشروا أي مفتاح في محادثة مشتركة أبدًا.

تقتصر الإشعارات على المشروع المرتبط والمفتاح النشط. افتحوا لوحة التحكم للموافقة على المدفوعات الصادرة.

/link <API_KEY>
/balance
/invoices
/notify on
/unlink
دليل التكامل · VeilNebula