الانتقال إلى المحتوى
الانتقال إلى المحتوى

للمطورين

وثائق واجهة برمجة التطبيقات لتحصيل المدفوعات

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

  • عنوان URL الأساسيhttps://www.cryptoavans.com/api/v3
  • إصدار الواجهة3.0.0

البدء

البيئة

اختبر دون تحريك أموال حقيقية.

مدفوعات حقيقية. يلزم مفتاح الإنتاج.

البدء

من إنشاء الحساب إلى أول دفعة حقيقية في أربع خطوات.

  1. احصل على مفاتيحك

    أنشئ حسابًا عبر “إنشاء حساب” أو سجّل الدخول، ثم افتح “API Key والتكامل” وأنشئ مفتاح sandbox. أضف أولًا عنوان IP لخادمك وعنوان webhook. التفاصيل: الحصول على مفتاح.

  2. أنشئ دفعة

    أرسل POST /api/v3/payments موقّعًا (تتوفر أمثلة برمجية) ووجّه العميل إلى payment_url المُعاد. الحقول والقواعد في المدفوعات.

  3. اختبر واستلم النتيجة

    أكمل دفعة اختبارية ببطاقة اختبار وراجعها في “طلبات API التجريبية”. يتلقى webhook لديك إشعار payment.paid موقّعًا (Webhooks)، ويمكنك أيضًا قراءة GET /api/v3/payments/{code}. أكمل الطلب فقط عندما تكون الحالة paid.

  4. انتقل إلى الإنتاج

    بعد أول طلب في sandbox أنشئ مفتاح الإنتاج، وأضف قائمة IP وعنوان webhook للإنتاج، ثم أكمل قائمة التحقق قبل الإطلاق.

اصطلاحات عامة

عنوان URL الأساسي
https://www.cryptoavans.com/api/v3
التنسيق
application/json · UTF-8
المبالغ
سلاسل عشرية: مبالغ العملات التقليدية بمنزلتين عشريتين (2)، مثل "250.00". وللقيمة usdt_amount ست منازل (6).
العملات
USD EUR AED TRY
الطوابع الزمنية
UTC · ISO 8601
لغة الأخطاء
تُحدَّد عبر ترويسة Accept-Language، والافتراضي هو التركية.

الرصيد والاستردادات والسحوبات والنزاعات وعمليات تسليم webhook موصوفة في صفحة عمليات الحساب.

لجمع بيانات البطاقة في نموذج الدفع الخاص بك بدلًا من ذلك، راجع Direct API (v4).

الحصول على مفتاح

لكل من بيئة الاختبار والبيئة الفعلية مفاتيح وقوائم IP وعناوين webhook مستقلة. يمكن تفعيل المفتاحين في الوقت نفسه.

  1. افتح “API Key والتكامل”.

  2. انسخ رقم الحساب المكوّن من 6 خانات في أعلى الصفحة. أرسله في الترويسة X-CAV-Account-No مع الطلبات الموقّعة.

  3. في “بيئة الاختبار”، أضف عنوان IP العام لخادمك إلى “قائمة عناوين IP المسموح بها” عبر “إضافة IP”. أدخل عناوين IPv4 أو IPv6 كلًا على حدة. نطاقات CIDR غير مدعومة.

  4. أدخل عنوان HTTPS عامًا في “رابط إشعارات HTTPS”. العنوان مطلوب ويُحفظ عند إنشاء المفتاح. لتغييره لاحقًا دون تجديد المفتاح، استخدم “حفظ رابط الإشعارات”.

  5. اضغط “إنشاء مفتاح”. احفظ مفتاح API والمفتاح السري، وحدّد “حفظت المفتاح السري بأمان.”، ثم اضغط “حفظته، إغلاق”.

يُفتح قسم الإنتاج بعد أن تنشئ طلبًا واحدًا على الأقل في sandbox. ولا يلزم إتمام الدفع.

  1. أنشئ طلبًا واحدًا على الأقل بمفتاح sandbox الخاص بك (POST /api/v3/payments). يُفتح قسم الإنتاج في اللوحة.

  2. في قسم الإنتاج، أضف عناوين IP لخوادم الإنتاج لديك إلى “قائمة عناوين IP المسموح بها”. القائمة منفصلة عن قائمة sandbox. وما دامت فارغة يُرفض كل طلب في الإنتاج.

  3. أدخل عنوان webhook عبر HTTPS لبيئة الإنتاج ضمن “رابط إشعارات HTTPS”. يُحفظ العنوان مع المفتاح في الخطوة التالية.

  4. اضغط “إنشاء مفتاح” واحفظ المفتاح السري لبيئة الإنتاج على خادمك.

  5. اضبط التكامل بمفتاح API الفعلي ومفتاحه السري. يبقى عنوان API الأساسي وتنسيق الطلب كما هما.

قائمة عناوين IP المسموح بها

تُقبل الطلبات فقط من عناوين IP الموجودة في قائمتك المسموح بها. يُرفض الطلب القادم من أي عنوان آخر بالرمز 403 (9000012) ولا تتم معالجة أي عملية.

  • القائمة إلزامية. لكل من sandbox والإنتاج قائمة مستقلة، ولا يمكن إنشاء مفتاح ما دامت قائمة تلك البيئة فارغة.
  • أدخل عناوين IPv4 أو IPv6 منفردة فقط؛ ولا توجد نطاقات (CIDR). وإذا كان خادمك يتصل بالخارج عبر IPv4 وIPv6 معًا فأضف العنوانين.
  • العنوان المعتمد هو العنوان العام الذي يتصل منه خادمك بالخارج. العناوين الخاصة (10.x، 192.168.x) لا تفيد؛ وإذا كنت خلف NAT أو وكيل (proxy) فأضف عنوانه الصادر.
  • اضغط “إزالة IP” بجانب العنوان وأكّد الإزالة. لإزالة آخر عنوان IP مع وجود مفتاح فعّال، أضف عنوانًا آخر أولًا أو ألغِ المفتاح.
  • تسري التغييرات فورًا. ولا حاجة إلى تجديد المفتاح.

أضف كل عنوان تختبر منه، مثل خادم التطوير أو العنوان الصادر لمكتبك.

أضف عناوين خوادم الإنتاج إلى قائمة البيئة الفعلية. قائمة الاختبار لا تمنح صلاحية للطلبات الفعلية.

تجديد المفاتيح وإلغاؤها

إذا فقدت المفتاح السري، استخدم “تجديد المفتاح”. يُلغى المفتاح القديم لهذه البيئة فورًا، وتحتفظ البيئة الأخرى بمفتاحها.

  • لكل بيئة مفتاح فعّال واحد. يُصدر “تجديد المفتاح” زوجًا جديدًا ويُلغي القديم فورًا. وتفشل الطلبات الموقّعة بالمفتاح القديم بالرمز 9000003.
  • يعطّل “إلغاء المفتاح” المفتاح دون إصدار مفتاح جديد. وتُرفض الطلبات فورًا.
  • يستمر المفتاح السري للمفتاح الملغى في توقيع إشعارات webhook للدفعات المنشأة به. احتفظ بالمفتاح السري المقابل بأمان لهذه الدفعات. إذا فُقد أو تسرّب، تحقق من حالتها بطلبات GET مصادق عليها بمفتاحك الحالي؛ ولا تثق بإشعارات موقّعة بمفتاح سري متسرّب.

المصادقة

وقّع الطلبات التي تتطلب مصادقة باستخدام HMAC-SHA256 والمفتاح السري. يشمل التوقيع الطابع الزمني والطريقة والمسار وبصمة الجسم. GET /api/v3/service-status متاح للجميع دون توقيع.

الترويسات المطلوبة

الترويسةالوصف
X-CAV-Account-Noرقم حسابك المكوّن من 6 خانات.
X-CAV-Api-Keyمفتاح API للبيئة التي تستدعيها (sandbox أو الإنتاج).
X-CAV-Timestampالوقت الحالي بنظام Unix بالثواني، من 10 خانات. تُرفض الطلبات التي يتجاوز انحراف طابعها الزمني 300 ثانية.
X-CAV-SignatureHMAC-SHA256 للنص القانوني، 64 خانة hex بأحرف صغيرة.
Idempotency-Keyمطلوب لطلبات الكتابة. استخدم 8–128 محرفًا من A-Z a-z 0-9 _ -. يمكن استخدام UUID.
Content-Typeapplication/json في الطلبات التي تحتوي على جسم.

لحفظ المفتاح السري، اتبع إرشادات الحصول على مفتاح.

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

  1. حوّل جسم JSON إلى نص مرة واحدة واحسب sha256(body) بصيغة hex بأحرف صغيرة. للطلبات بلا جسم استخدم sha256("").

  2. كوّن النص المعياري من timestamp وMETHOD وpath وتجزئة الجسم، مفصولةً بأسطر جديدة (\n ودون سطر جديد في النهاية). لا يتضمن المسار سلسلة استعلام.

  3. احسب HMAC-SHA256(secret, canonical) وضعه في صيغة 64 خانة hex بأحرف صغيرة.

  4. أرسل ترويسات X-CAV-* الأربع (رقم الحساب، مفتاح API، الطابع الزمني، التوقيع)، وأضف Idempotency-Key في طلبات الكتابة.

نص التوقيع القانوني

timestamp + "\n" + METHOD + "\n" + path + "\n" + hex(sha256(body))
signature = hmac_sha256(secret, canonical)  // 64 lowercase hex characters

path هو المسار الكامل دون سلسلة الاستعلام (مثل /api/v3/payments). يستخدم الطلب الذي لا جسم له تجزئة السلسلة الفارغة (sha256("")).

وقّع البايتات نفسها التي ترسلها: حوّل JSON إلى نص مرة واحدة واستخدم النص نفسه للتجزئة (hash) ولجسم الطلب.

مثال (ببيانات وهمية)

جميع القيم أدناه وهمية: رقم الحساب 123456 والمفتاح السري sk_test_EXAMPLE. استخدم القيم الموجودة في لوحتك.

جسم الطلب

{"amount":"250.00","currency_code":"USD","merchant_reference":"ORDER-2026-0001"}

sha256(body)

280db1ad2f6a457444b4838e9da76682edbd328a1b62f7b9b762852d9c4354f7

نص التوقيع القانوني

1790000000
POST
/api/v3/payments
280db1ad2f6a457444b4838e9da76682edbd328a1b62f7b9b762852d9c4354f7

X-CAV-Signature

3d3a95fcbda890bb424184c914ef3e2371c43602988cf3a698dc357b8074c5e9

إرسال طلب موقّع

ينشئ كل مثال دفعة. تُقرأ بيانات الاعتماد من متغيرات البيئة. لا تضع المفتاح السري في الشيفرة. يستخدم كل تشغيل merchant_reference جديدًا.

#!/usr/bin/env bash
# export MERCHANT_ACCOUNT_NO=... MERCHANT_API_KEY=... MERCHANT_API_SECRET=...   (sandbox key)
# GET requests: METHOD=GET, REQ_PATH=/api/v3/payments/<code>, BODY='' (sha256 of the empty string), no Content-Type / --data-binary
set -euo pipefail

BASE_URL="https://www.cryptoavans.com"
METHOD="POST"
REQ_PATH="/api/v3/payments"
REFERENCE="ORDER-$(date +%s)-$(openssl rand -hex 4)"   # a new merchant_reference for every payment
BODY="{\"amount\":\"250.00\",\"currency_code\":\"USD\",\"merchant_reference\":\"$REFERENCE\",\"return_url\":\"http://localhost/return\"}"

TIMESTAMP="$(date +%s)"
BODY_SHA256="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
CANONICAL="$(printf '%s\n%s\n%s\n%s' "$TIMESTAMP" "$METHOD" "${REQ_PATH%%\?*}" "$BODY_SHA256")"
# the secret travels in the environment, not on the command line
SIGNATURE="$(printf '%s' "$CANONICAL" | python3 -c 'import hashlib,hmac,os,sys; print(hmac.new(os.environ["MERCHANT_API_SECRET"].encode(), sys.stdin.buffer.read(), hashlib.sha256).hexdigest())')"

curl --silent --show-error --request "$METHOD" "$BASE_URL$REQ_PATH" \
  --header "X-CAV-Account-No: $MERCHANT_ACCOUNT_NO" \
  --header "X-CAV-Api-Key: $MERCHANT_API_KEY" \
  --header "X-CAV-Timestamp: $TIMESTAMP" \
  --header "X-CAV-Signature: $SIGNATURE" \
  --header "Idempotency-Key: $(openssl rand -hex 16)" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --data-binary "$BODY"
<?php
$config = [
    'base'    => 'https://www.cryptoavans.com',
    'account' => getenv('MERCHANT_ACCOUNT_NO'),
    'key'     => getenv('MERCHANT_API_KEY'),
    'secret'  => getenv('MERCHANT_API_SECRET'),
];

function signedRequest(array $config, string $method, string $path, ?array $payload = null, ?string $idempotencyKey = null): array
{
    $method = strtoupper($method);
    $signingPath = explode('?', $path, 2)[0];
    // serialise once: the same bytes are hashed and sent
    $body = $payload === null ? '' : json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    $timestamp = (string) time();
    $canonical = implode("\n", [$timestamp, $method, $signingPath, hash('sha256', $body)]);
    $signature = hash_hmac('sha256', $canonical, $config['secret']);

    $headers = [
        'X-CAV-Account-No: ' . $config['account'],
        'X-CAV-Api-Key: ' . $config['key'],
        'X-CAV-Timestamp: ' . $timestamp,
        'X-CAV-Signature: ' . $signature,
        'Content-Type: application/json',
        'Accept: application/json',
    ];
    if ($idempotencyKey !== null) {
        $headers[] = 'Idempotency-Key: ' . $idempotencyKey;
    }

    $ch = curl_init($config['base'] . $path);
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_HTTPHEADER     => $headers,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 30,
    ]);
    if ($body !== '') {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
    }
    $response = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    if ($response === false) {
        throw new RuntimeException('No response; retry the same operation and Idempotency-Key.');
    }

    return [$status, json_decode((string) $response, true)];
}

// a new merchant_reference for every payment: seconds plus a random suffix
[$status, $payment] = signedRequest(
    $config,
    'POST',
    '/api/v3/payments',
    ['amount' => '250.00', 'currency_code' => 'USD', 'return_url' => 'http://localhost/return', 'merchant_reference' => 'ORDER-' . time() . '-' . bin2hex(random_bytes(4))],
    bin2hex(random_bytes(16))
);
echo $status, ' ', json_encode($payment, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), PHP_EOL;

if (!in_array($status, [200, 201], true)) {
    exit(1); // the error code is not a payment code
}

// GET: no body, so the hash is sha256("")
[$status, $fetched] = signedRequest($config, 'GET', '/api/v3/payments/' . $payment['code']);
echo $status, ' ', $fetched['status'] ?? '', PHP_EOL;

تُرفض الطلبات ذات الطابع الزمني القديم، لذا أبقِ ساعة خادمك متزامنة عبر NTP. ويؤدي تكرار التوقيعات الخاطئة من عنوان IP واحد إلى حظر مؤقت (429).

قواعد الطلبات

Idempotency

أرسل ترويسة Idempotency-Key في كل طلب كتابة. فهي تجعل إعادة المحاولة آمنة: مهما كررت إرسال الطلب نفسه، تُنفَّذ العملية مرة واحدة.

  • يُرجع تكرار الطلب بالمفتاح والجسم نفسيهما النتيجة الأصلية ولا يُنشئ عملية ثانية أبدًا. استخدم GET عندما تحتاج إلى الحالة الحالية.
  • المفتاح نفسه مع جسم مختلف يُرجع 409 بالرمز 9000010؛ والمفتاح غير الصالح يُرجع 422 بالرمز 1000009.
  • أنشئ مفتاحًا عشوائيًا جديدًا (يكفي UUID) لكل عملية جديدة واحفظه مع طلبك ليستخدمه أي تكرار.

حدود المعدل

تُحدَّد الطلبات لكل مفتاح API ولكل عنوان IP. عند تلقي 429 تمهّل وأعد المحاولة لاحقًا.

  • عند 429 انتظر المدة في Retry-After إن وُجدت، ثم أعد المحاولة بتراجع أُسّي مع تشويش عشوائي.
  • عند 503 أعد المحاولة لاحقًا بالمفتاح Idempotency-Key والجسم نفسيهما. عالج أخطاء 4xx الأخرى قبل إعادة المحاولة؛ ولا تستخدم مفتاحًا جديدًا إلا لعملية جديدة.

إذا رُفض طلب فابحث عن code في رموز الأخطاء. أكثر الأسباب شيوعًا: توقيع خاطئ (9000001)، طابع زمني قديم (9000002)، بيانات اعتماد خاطئة (9000003)، وعنوان IP غير مدرج في قائمة السماح (9000012).

المدفوعات

أنشئ دفعة واقرأ حالتها.

حالات الدفعة

الحقلالوصف
createdالدفعة مفتوحة. موعدها النهائي في expires_at؛ وصفحة الدفع صالحة لمدة 24 ساعة.
paidحُصِّلت الدفعة. يُرسل إشعار payment.paid.
expiredانتهت صلاحية صفحة الدفع دون تأكيد الدفع.

اقرأ الحالة الحالية عبر GET /api/v3/payments/{code}. بعد الرفض يمكن للعميل المحاولة مجددًا على payment_url نفسه ما دامت الدفعة created. وإذا كانت expired فأنشئ دفعة جديدة بـ merchant_reference وIdempotency-Key جديدين.

القواعد

  • يجب أن يكون المبلغ ضمن حدود الدفع الخاصة بحسابك. يُفحص الحد بالدولار الأمريكي بعد التحويل؛ والحد الافتراضي من 3.00 إلى 1100.00 USD. وخارج هذا النطاق تُرجع الواجهة 422 بالرمز 9000005. لطلب حد مختلف تواصل مع CryptoAvans.
  • merchant_reference بحد أقصى 64 حرفًا وفريد لكل حساب ولكل بيئة. ولا يمكن إعادة استخدامه حتى بعد انتهاء صلاحية الدفعة (409، 9000007)، فأرسل قيمة جديدة مع كل دفعة جديدة.
  • وجّه العميل إلى payment_url كصفحة عادية (تنقّل على المستوى الأعلى). لا يمكن عرض صفحة الدفع داخل iframe.
  • return_url اختياري. عند انتهاء الدفع تعرض صفحة النتيجة زرًّا يعيد العميل إليه، مع إضافة code وstatus وmerchant_reference (إن أرسلته) إلى سلسلة الاستعلام. لا توجد إعادة توجيه تلقائية.
  • تُتجاهل حقول الطلب غير المعروفة. تحقق من الدفع عبر webhook موقّع أو GET من خادمك؛ عودة العميل إلى موقعك للمعلومات فقط.

في sandbox تفتح payment_url صفحة الدفع نفسها؛ ادفع ببطاقة اختبار من بيئة الاختبار.

في الإنتاج تُحصَّل الدفعة من بطاقة حقيقية وتُضاف إلى رصيدك بعد تاريخ القيمة.

POST/api/v3/paymentsيتطلب Idempotency-Key

إنشاء دفعة

تتضمن استجابة 201 الحقلين payment_url وexpires_at. وجّه العميل إلى payment_url؛ وصفحة الدفع صالحة لمدة 24 ساعة. إذا أُعيد 409 / 9000039 فكرّر الطلب نفسه بالمفتاح Idempotency-Key نفسه.

جسم الطلب

الحقلالنوعمطلوبالوصف
amountstringمطلوبمبلغ موجب على هيئة سلسلة عشرية ثابتة المقياس بخانتين عشريتين (مثل "250.00") بعملة currency_code. ويجب أن يكون ضمن حدود الدفع الخاصة بحسابك (تُفحص بعملة USD بعد التحويل بالسعر الحالي؛ راجعوا حدودكم في صفحة API Key والتكامل في اللوحة)، وإلا يُرجع الطلب 422 9000005.
currency_codestringمطلوبعملة المصدر.
USDEURAEDTRY
merchant_referencestring | nullاختياريمرجع طلبك الخاص (بحد أقصى 64 حرفًا). فريد لكل حساب ولكل بيئة: فلا يمكن إعادة استخدام المرجع، حتى لدفعة انتهت صلاحيتها (409 9000007). اختياري.
maxLength 64
webhook_urlstring | nullاختياريعنوان https:// الذي يستقبل الإشعارات الموقّعة (payment.paid وpayment.refunded, payment.failed). يجب أن يُحلّ اسم المضيف عبر DNS إلى عنوان عام (دون عنوان IP خاص أو محجوز ودون localhost)، وألا يزيد طول العنوان عن 500 حرف، وألا يتضمن اسم مستخدم أو كلمة مرور. اختياري: عند عدم إرساله يُستخدم عنوان webhook المحفوظ في اللوحة لبيئة مفتاح التوقيع لديك؛ وإذا لم يكن هناك عنوان محفوظ لا يُرسل أي إشعار.
maxLength 500
merchant_namestring | nullاختياريالاسم المعروض على شاشة الدفع. اختياري؛ وقيمته الافتراضية Hesap <account number> (رقم حسابك المكوّن من 6 خانات). القيمة التي تزيد عن 120 حرفًا تُقتطع ولا تُرفض.
return_urlstring · uri | nullاختياريعنوان return_url اختياري (حتى 2048 حرفًا). يتطلب الإنتاج HTTPS؛ وتقبل sandbox أيضًا HTTP على localhost أو 127.0.0.1 أو [::1]. تُمنع معلومات المستخدم والأجزاء ومحارف التحكم والمسافات. تُحوّل أسماء النطاقات الدولية إلى ASCII. عنوان URL غير صالح: 422 / 9000038. عند انتهاء الدفع تعرض صفحة النتيجة زرًّا يأخذ العميل إلى هذا العنوان، مع إضافة code وstatus وmerchant_reference (إن أرسلته) إلى سلسلة الاستعلام؛ ولا توجد إعادة توجيه تلقائية. لا تغيّره عند إعادة المحاولة بالمفتاح Idempotency-Key نفسه.
maxLength 2048

جسم الطلب · مثال

{
  "amount": "10.00",
  "currency_code": "USD",
  "merchant_reference": "INV-104",
  "merchant_name": "Test Shop",
  "return_url": "https://shop.example.invalid/return?basket=104"
}

يتضمن الحقل limits القيم currency_code وmin_amount وmax_amount (null: لا حد أعلى). المبالغ أدناه أمثلة وليست حدودك.

HTTP 422 / 9000005

{
  "type": "https://www.cryptoavans.com/errors/9000005",
  "title": "Invalid amount",
  "status": 422,
  "code": "9000005",
  "correlation_id": "example",
  "detail": "Amount outside the account limits.",
  "limits": {
    "currency_code": "USD",
    "min_amount": "25.00",
    "max_amount": null
  }
}

الاستجابة · 201

أُنشئت الدفعة. وعند إعادة الإرسال بالمفتاح Idempotency-Key نفسه يُرجع 200 بدلًا من ذلك.

Payment

الحقلالنوعالوصف
codestringرمز الدفعة المختصر (من 4 إلى 16 حرفًا صغيرًا).
merchant_referencestring | nullمرجع طلبك، يُعاد كما أُرسل.
statusstringحالة دورة حياة الدفعة: created إلى أن يدفع العميل ثم paid؛ وexpired عندما تنتهي صلاحية الرابط دون دفع.
createdpaidexpired
amountstringالمبلغ الإجمالي، على هيئة سلسلة عشرية ثابتة المقياس بخانتين عشريتين، بعملة currency_code.
currency_codestringعملة المصدر لهذه الدفعة؛ اقرأ القيمة المُعادة بدلًا من افتراض عملة المثال.
USDEURAEDTRY
environmentstringالبيئة التي تنتمي إليها هذه الدفعة.
sandboxlive
payment_urlstringوجّه العميل إلى payment_url المُعاد كصفحة عادية (تنقّل على المستوى الأعلى)؛ ولا تنشئ العنوان أو تغيّره بنفسك. لا يمكن تضمين الصفحة في iframe. لا تُرسل بيانات البطاقة إلى هذه الواجهة.
created_atstring · date-timeوقت الإنشاء (UTC، ISO-8601).
expires_atstring · date-timeUTC. تظل صفحة الدفع صالحة 24 ساعة بعد إنشائها. أنشئ دفعة جديدة بعد انتهاء الصلاحية؛ لا توجد نقطة نهاية للتجديد. تحقق من الدفع عبر GET /api/v3/payments/{code} أو webhook موقّع؛ وعودة العميل إلى موقعك للمعلومات فقط.
paid_atstring · date-time | nullوقت الدفع أو null.
payment_url_kindstringدائمًا platform (صفحة الدفع الخاصة بنا). يُحتفظ بالحقل من أجل التكاملات القائمة؛ ولم يعد provider_hosted يُعاد.
platformprovider_hosted
cardMerchantCardSummary | nullمعلومات بطاقة المحاولة الناجحة؛ تكون null قبل الدفع أو عند عدم توفر المعلومات.
failurePaymentFailureSummary | nullآخر محاولة فاشلة إن لم تُدفع الدفعة، وإلا null. ليست نهائية: يمكن الدفع حتى expires_at. يعدّ attempt_count المحاولات الفاشلة بما فيها الإعادات.

الاستجابة · مثال

{
  "code": "a1b2c3d4",
  "merchant_reference": "INV-104",
  "status": "created",
  "amount": "10.00",
  "currency_code": "USD",
  "environment": "live",
  "payment_url": "https://pay.example.com/l/a1b2c3d4",
  "payment_url_kind": "platform",
  "created_at": "2026-10-05T12:00:00Z",
  "expires_at": "2026-10-06T12:00:00Z",
  "paid_at": null,
  "card": null,
  "failure": null
}

استجابات أخرى

  • 200إعادة الطلب بالمفتاح نفسه: تُعاد الدفعة الأصلية ولا تُنشأ دفعة ثانية.
  • 422يعيد المبلغ خارج حدود الحساب (9000005، بما فيه أقل من 0.01 بعد التحويل أو تجاوز السعة) limits بعملة الحساب الأساسية. تستخدم sandbox التحقق نفسه. قد لا تتضمن أخطاء التحقق الأخرى limits.

الأخطاء المحتملة

GET/api/v3/payments/{code}

جلب حالة الدفعة

المعاملات

الحقلالنوعمطلوبالوصف
codestring · pathمطلوبرمز الدفعة.
pattern ^[a-z0-9]{4,16}$

الاستجابة · 200

الدفعة.

الكائن: Payment (الحقول مدرجة في إنشاء دفعة).

الاستجابة · مثال

{
  "code": "ab34cdef",
  "merchant_reference": "ORDER-2026-0001",
  "status": "created",
  "amount": "250.00",
  "currency_code": "USD",
  "environment": "live",
  "payment_url": "https://example.com/l/ab34cdef",
  "created_at": "2026-09-26T09:00:00.000Z",
  "expires_at": "2026-09-27T09:00:00.000Z",
  "paid_at": null,
  "payment_url_kind": "platform"
}

الأخطاء المحتملة

المحاولات الفاشلة

يحتوي Payment.card على معلومات المحاولة الناجحة، وfailure.card على معلومات آخر محاولة فاشلة. قد تكون القيم غير المتاحة null. اسم حامل البطاقة بيانات شخصية؛ عالجوه وفق التزاماتكم القانونية. لا يُقدّم رقم البطاقة الكامل أو تاريخ انتهائها مطلقًا.

يُرسل payment.failed مرة واحدة عند أول محاولة فاشلة: رفض البطاقة أو فشل 3-D Secure أو إلغاء الدافع. تبقى الدفعة created؛ يمكن إعادة المحاولة حتى expires_at وقد يأتي payment.paid لاحقًا. تحدّث الإخفاقات التالية Payment.failure دون حدث جديد. يعدّ attempt_count المحاولات الفاشلة؛ وdetail_code للمعلومات إن توفر. استخدموا قيم failure.code الثابتة أدناه.

PaymentFailure

الحقلالنوعمطلوبالوصف
detail_codestring | nullمطلوبرمز استجابة تفصيلي من شبكة الدفع، مثل 058 أو 511؛ ولا تزال رموز بيئة الاختبار التي تحتوي على نقطة مدعومة. عند عدم وجود معاملة: timeout مع تفصيل null. يجب أن تعتمد عمليات التكامل على الحقل الثابت code.
pattern ^[0-9]{3}(?:\.[0-9]{3})?$
cardMerchantCardSummary | nullمطلوبمعلومات بطاقة اختيارية من المحاولة الناجحة أو الفاشلة المتحقق منها. يستخدم issuing_country صيغة ISO 3166-1 alpha-3. لا يُقدّم رقم البطاقة الكامل أو تاريخ الانتهاء أو رمز الأمان أو معرفات خاصة بالمؤسسة.
codestringمطلوبسبب ثابت وآمن من آخر معاملة دفع، وإلا فمن المصادقة. 511/51: insufficient_funds; 058/004: authentication_failed; 022: timeout; 001/002: cancelled; 527/501: declined; 526: provider_error. عدم وجود معاملة أو نجاح المصادقة دون دفع: timeout. الأخطاء غير المعروفة: declined. لا تتضمن رسالة مزوّد الخدمة أو بيانات بطاقة حساسة.
declinedinsufficient_fundsauthentication_failedcard_invalidcancelledprovider_errorexpired_cardinvalid_cvvtimeout
occurred_atstring · date-timeمطلوبوقت اكتمال المحاولة الفاشلة بصيغة UTC مع أجزاء الثانية بالمللي ثانية.

Webhooks

تشمل إشعارات JSON الموقّعة payment.paid وpayment.failed وpayment.refunded. عالجوا أنواع الأحداث المعروفة فقط وتجاهلوا غير المعروفة؛ فالمحاولة الفاشلة ليست نتيجة دفع نهائية.

تحقق من كل إشعار

  1. اقرأ الجسم الخام قبل تحليل JSON. ارفض التوقيع أو الطابع الزمني المفقود أو غير الصحيح الصيغة. ترفض الأمثلة الطوابع الزمنية التي تختلف عن ساعة خادمك بأكثر من 300 ثانية؛ حافظ على تزامن الساعة.

  2. أعد بناء النص المعياري من X-CAV-Timestamp وPOST ومسار عنوان webhook لديك (دون سلسلة استعلام) وsha256 للجسم الخام.

  3. احسب HMAC-SHA256 بمفتاحك السري وقارنه مع X-CAV-Signature بزمن ثابت. استخدم المفتاح السري للمفتاح الذي أنشأ الدفعة، حتى لو جدّدت مفتاحك بعد ذلك.

  4. بعد التحقق من التوقيع، طابق environment الموقّع ورمز الدفعة والمبلغ والعملة مع طلبك. ميّز بين payment.paid / payment.failed وpayment.refunded باستخدام event_type في الجسم الموقّع؛ يجب ألا يؤدي إشعار الاسترداد إلى تسليم الطلب.

  5. أزل التكرار ذريًا باستخدام event_id من الجسم الذي تم التحقق منه. قارنه مع X-CAV-Delivery دون تمييز حالة الأحرف وارفض عدم التطابق؛ فالترويسة نفسها غير موقّعة. لا تعتمد على ترتيب التسليم؛ تحقق من الأحداث غير المتوقعة عبر نقاط نهاية الحالة.

  6. أعد 2xx سريعًا بعد حفظ الحدث المتحقق منه بشكل دائم أو إتمام معالجته. إذا تعذر حفظه، أعد استجابة فشل للسماح بإعادة المحاولة. أعد 2xx للحدث المكرر المحفوظ مسبقًا. يُتجاهل جسم الاستجابة ولا تُتبع إعادة التوجيه.

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

أمثلة على التحقق من التوقيع

<?php
// $path is the path of YOUR webhook URL (no query string), e.g. /payment-webhook
function isValidWebhook(string $rawBody, array $headers, string $path, string $secret): bool
{
    $headers = array_change_key_case($headers, CASE_LOWER);
    $timestamp = $headers['x-cav-timestamp'] ?? '';
    $signature = $headers['x-cav-signature'] ?? '';
    if (!is_string($timestamp) || !is_string($signature)
        || preg_match('/^[0-9]{10}$/D', $timestamp) !== 1
        || preg_match('/^[0-9a-f]{64}$/D', $signature) !== 1
        || abs(time() - (int) $timestamp) > 300) {
        return false;
    }
    $canonical = implode("\n", [$timestamp, 'POST', $path, hash('sha256', $rawBody)]);

    return hash_equals(hash_hmac('sha256', $canonical, $secret), $signature);
}

$rawBody = file_get_contents('php://input');
$headers = [
    'X-CAV-Timestamp' => $_SERVER['HTTP_X_CAV_TIMESTAMP'] ?? '',
    'X-CAV-Signature' => $_SERVER['HTTP_X_CAV_SIGNATURE'] ?? '',
];
if (!isValidWebhook($rawBody, $headers, '/payment-webhook', getenv('MERCHANT_API_SECRET'))) {
    http_response_code(401);
    exit;
}

// After verification: compare event_id from the signed body with X-CAV-Delivery (case-insensitive),
// save the event once (deduplicate by event_id), then answer 2xx. On a save error answer 5xx so it is retried.
$event = json_decode($rawBody, true);
http_response_code(204);
const crypto = require("node:crypto");

// path is the path of YOUR webhook URL (no query string), e.g. /payment-webhook
function isValidWebhook(rawBody, headers, path, secret) {
  const timestamp = headers["x-cav-timestamp"] ?? "";
  const signature = headers["x-cav-signature"] ?? "";
  if (typeof timestamp !== "string" || typeof signature !== "string"
      || !/^[0-9]{10}$/.test(timestamp) || !/^[0-9a-f]{64}$/.test(signature)
      || Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > 300) return false;
  const bodyHash = crypto.createHash("sha256").update(rawBody).digest("hex");
  const canonical = [timestamp, "POST", path, bodyHash].join("\n");
  const expected = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// rawBody must be the untouched request bytes (Buffer), not re-serialised JSON.
// After verification, deduplicate using event_id from the signed body; check X-CAV-Delivery matches.
// Check event_type and order fields, then durably save before 2xx (see the verification steps).
module.exports = { isValidWebhook };
import hashlib
import hmac
import re
import time


def is_valid_webhook(raw_body: bytes, headers: dict, path: str, secret: str) -> bool:
    """path is the path of YOUR webhook URL (no query string), e.g. /payment-webhook."""
    headers = {key.lower(): value for key, value in headers.items()}
    timestamp = headers.get("x-cav-timestamp", "")
    signature = headers.get("x-cav-signature", "")
    if (not isinstance(timestamp, str) or not isinstance(signature, str)
            or not re.fullmatch(r"[0-9]{10}", timestamp)
            or not re.fullmatch(r"[0-9a-f]{64}", signature)
            or abs(int(time.time()) - int(timestamp)) > 300):
        return False
    canonical = "\n".join([timestamp, "POST", path, hashlib.sha256(raw_body).hexdigest()])
    expected = hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)


# After verification, deduplicate by event_id from the signed body; check X-CAV-Delivery matches.
# Check event_type and order fields, then durably save before 2xx (see the verification steps).

إعادة المحاولات

تُعدّ أي استجابة غير 2xx أو انتهاء المهلة (نحو 10 ثوانٍ) أو خطأ الاتصال محاولة فاشلة. تُجرى 6 محاولات كحد أقصى، وتُراعى بينها مدد الانتظار الموضحة أدناه كحد أدنى. بعد آخر محاولة فاشلة يُوسَم التسليم بالحالة failed ويمكنك إعادته إلى قائمة الانتظار عبر POST /api/v3/webhooks/deliveries/{id}/retry.

الجسم وX-CAV-Delivery متطابقان في كل محاولة، بينما يُعاد حساب X-CAV-Timestamp وX-CAV-Signature في كل مرة. يتطابق id التسليم وترويسة X-CAV-Delivery وevent_id في الجسم، وكلها UUID واحد بأحرف كبيرة؛ قارنه دون مراعاة حالة الأحرف.

المحاولةالانتظار قبلها
1—
260 s (1 min)
3300 s (5 min)
4900 s (15 min)
53600 s (1 h)
621600 s (6 h)

الإشعارات المرسلة إلى خادمك

الحدثمتى يُرسل
payment.paidتم تحصيل الدفعة.
payment.failedأول محاولة فاشلة لدفعة لا تزال مفتوحة. ليست نهائية: يمكن للعميل إعادة المحاولة.
payment.refundedاكتمل استرداد.
POSTpayment.paid

إشعار payment.paid موقّع إلى عنوان التاجر

الترويسة

الحقلالنوعمطلوبالوصف
X-CAV-Eventstring · headerمطلوب
payment.paid
X-CAV-Environmentstring · headerمطلوب
sandboxlive
X-CAV-Deliverystring · uuid · headerمطلوبمعرّف التسليم. لا يتغير عند إعادة المحاولات.
X-CAV-Timestampstring · headerمطلوب
pattern ^[0-9]{10}$
X-CAV-Signaturestring · headerمطلوب
pattern ^[0-9a-f]{64}$

جسم الطلب · مثال

{
  "event_id": "3BAEB108-41A8-40AE-81EF-0A82AA1132D6",
  "event_type": "payment.paid",
  "environment": "sandbox",
  "occurred_at": "2026-10-05T12:01:00.000Z",
  "payment": {
    "code": "a1b2c3d4",
    "merchant_reference": "INV-104",
    "status": "paid",
    "amount": "10.00",
    "currency_code": "USD",
    "created_at": "2026-10-05T12:00:00.000Z",
    "paid_at": "2026-10-05T12:01:00.000Z",
    "card": {
      "brand": "visa",
      "bin": "400000",
      "last4": "0010",
      "holder_name": "TEST CARDHOLDER",
      "issuing_country": "TUR",
      "issuing_bank": "TEST BANK",
      "funding_type": "credit",
      "category": "STANDARD"
    }
  }
}
POSTpayment.failed

إشعار موقّع بفشل محاولة الدفع

جسم الطلب · مثال

{
  "event_id": "3BAEB108-41A8-40AE-81EF-0A82AA1132D6",
  "event_type": "payment.failed",
  "environment": "sandbox",
  "occurred_at": "2026-10-05T12:01:00.000Z",
  "payment": {
    "code": "a1b2c3d4",
    "merchant_reference": "INV-104",
    "status": "created",
    "amount": "10.00",
    "currency_code": "USD",
    "created_at": "2026-10-05T12:00:00.000Z",
    "expires_at": "2026-10-06T12:00:00.000Z"
  },
  "failure": {
    "code": "declined",
    "occurred_at": "2026-10-05T12:01:00.000Z",
    "detail_code": null,
    "card": {
      "brand": "visa",
      "bin": "400000",
      "last4": "0010",
      "holder_name": "TEST CARDHOLDER",
      "issuing_country": "TUR",
      "issuing_bank": "TEST BANK",
      "funding_type": "credit",
      "category": "STANDARD"
    }
  }
}

الإطار هو نفسه في payment.paid. يحتوي payment على expires_at بدل paid_at، ويصف failure المحاولة (انظر المحاولات الفاشلة).

لفحص عمليات التسليم أو إعادة جدولة عملية فاشلة، انظر عمليات تسليم webhook.

بيئة الاختبار

تعمل طلبات sandbox على بيانات افتراضية ولا تحرّك أموالًا حقيقية. افتح payment_url وادفع بإحدى بطاقات الاختبار أدناه.

كيف يعمل

يعمل كل طلب موقّع بمفتاح sandbox على بيانات افتراضية معزولة. القواعد والحقول ورموز الأخطاء هي نفسها في الإنتاج. تفتح payment_url صفحة الدفع نفسها التي يراها عملاؤك في الإنتاج. وعند الموافقة على دفعة اختبارية يُرسل payment.paid إلى عنوان webhook الخاص بالدفعة مع ضبط environment على sandbox.

  • تعرض الصفحة تنبيه اختبار ولا تقبل إلا بطاقات الاختبار أدناه؛ وتُرفض أرقام البطاقات الحقيقية. وفي بطاقات 3-D Secure تطلب منك الموافقة على المصادقة أو رفضها. لا تقبل الواجهة أرقام البطاقات؛ أدخل البطاقة في صفحة الدفع.
  • لا تتدخل أموال حقيقية ولا بطاقة حقيقية ولا معاملة على السلسلة، ولا يصل شيء إلى رصيدك في الإنتاج.

بطاقات اختبار بيئة المحاكاة

استخدم هذه الأرقام الاصطناعية فقط في صفحة الدفع الخاصة بدفعة sandbox، ولا تستخدمها أبدًا في دفعة حقيقية. أدخل أي اسم لحامل البطاقة من حرفين على الأقل، وتاريخ انتهاء في الشهر الحالي أو بعده، ورمز CVV من 3 أو 4 أرقام.

رقم البطاقةرمز النتيجةالسلوك
4000000000000010approvedتُقبل دون 3-D Secure.
4000000000000051declinedتُرفض دون 3-D Secure.
4000000000000002three_ds_approvedتعرض صفحة الدفع خطوة 3-D Secure؛ وعند الموافقة عليها تُقبل الدفعة، وعند رفضها تُرفض الدفعة.
4000000000000093three_ds_declinedتعرض صفحة الدفع خطوة 3-D Secure؛ وتُرفض الدفعة أيًا كانت النتيجة التي تختارها.
4000000000000044timeoutتبقى النتيجة معلّقة (دون نتيجة نهائية)؛ لا تعتبرها رفضًا نهائيًا. وتُغلق المحاولة لاحقًا بوصفها منتهية الصلاحية.

تبقى الدفعة المرفوضة في حالة created وتُرسل payment.failed مرة واحدة عند أول محاولة فاشلة؛ ويمكن للعميل المحاولة مرة أخرى (10 محاولات في الساعة لكل عنوان IP). أما بطاقة انتهاء المهلة والمبالغ التي تنتهي بـ .77 فتترك النتيجة معلّقة: تعرض الصفحة نتيجة معلّقة، ولا يُرسل أي webhook، ولا تُدخل النتيجة يدويًا أبدًا. وتُغلق المحاولة بعد نحو 30 دقيقة بوصفها منتهية الصلاحية؛ وتبقى الدفعة في حالة created ويمكن للعميل البدء من جديد. وتعتمد قاعدة .77 على المبلغ بالدولار الأمريكي.

سجل الطلبات

في اللوحة يعرض “سجل طلبات بيئة الاختبار” أحدث طلبات sandbox لديك: آخر 90 يومًا، مع الاحتفاظ بعنوان IP المصدر 30 يومًا. لا تُسجَّل الطلبات التي فشل فيها التحقق من التوقيع أو من بيانات الاعتماد؛ ابحث عنها بواسطة correlation_id في الاستجابة. ولا تُسجَّل طلبات الإنتاج.

اختبارات الاستردادات والسحوبات والنزاعات موصوفة في صفحة عمليات الحساب.

رموز الأخطاء

تُعاد الأخطاء بصيغة application/problem+json. الحقل code أحد الرموز الواردة أدناه؛ أرفق correlation_id عند التواصل مع الدعم. ويتبع title وdetail ترويسة Accept-Language؛ فاعتمد في شيفرتك على code وحده.

مثال على استجابة خطأ

application/problem+json

{
  "type": "https://www.cryptoavans.com/errors/9000011",
  "title": "Signature headers are missing",
  "status": 400,
  "detail": "X-CAV-Account-No, X-CAV-Api-Key, X-CAV-Timestamp and X-CAV-Signature are mandatory.",
  "code": "9000011",
  "correlation_id": "3BAEB108-41A8-40AE-81EF-0A82AA1132D6"
}

جدول الرموز

إذا فُقدت الاستجابة أو تلقيت 500 أو 503، فقد تكون نتيجة العملية غير معروفة. أعد المحاولة بنفس Idempotency-Key والجسم، مع توقيع جديد وطابع زمني حالي. التزم بمدة Retry-After عند 503. لا تنشئ دفعة جديدة لمجرد انتهاء مهلة الطلب.

الرمزHTTPالعنوان
9000001401التوقيع غير صالح
9000002401الطابع الزمني خارج النافذة المسموحة
9000003401بيانات الاعتماد غير صالحة
9000004403لا يمكن للحساب إجراء المعاملات
9000005422المبلغ غير صالح
9000006422مرجع التاجر غير صالح
9000007409مرجع التاجر مستخدم بالفعل
9000008422عنوان الإشعارات غير صالح
9000009404لم يتم العثور على الدفعة أو الاسترداد
9000010409تعارض Idempotency
9000011400ترويسات التوقيع مفقودة
9000012403عنوان IP غير مسموح به
9000013409بيئة sandbox غير متاحة
9000014409طلب الاسترداد أو الإلغاء موجود بالفعل
9000015404لم يتم العثور على النزاع
9000016409لا يُقبل الرد على النزاع
9000017422عنوان TRC20 غير صالح
9000018422تأكيد العنوان مطلوب
9000019422المبلغ غير صالح
9000020409الرصيد غير كافٍ
9000021409تم تجاوز حد السحب اليومي
9000022409السحب قيد المعالجة
9000023404لم يتم العثور على السحب
9000024404لم يتم العثور على تسليم webhook
9000025409لا يمكن إعادة المحاولة
9000026503مخطط sandbox غير جاهز
9000027409رصيد sandbox مشغول
9000030409رصيد التحصيل في sandbox غير صالح
9000033409رصيد sandbox غير كافٍ
9000038422عنوان العودة غير صالح
9000039409جارٍ إعداد الدفع
9000040403الوصول إلى API متوقف
1000001422خطأ في التحقق مشترك
1000002404لم يتم العثور على سجل مشترك
1000005429تم تجاوز حد الطلبات مشترك
1000006500خطأ في الخادم مشترك
1000007405الطريقة غير مدعومة مشترك
1000009422مفتاح Idempotency غير صالح مشترك
1000011503الخدمة غير متاحة حاليًا مشترك
1000013503بنية المفاتيح غير جاهزة مشترك
1000014503الخدمة قيد الصيانة مشترك
1000020422تنسيق المبلغ غير صالح مشترك
1000021422عملة غير مدعومة مشترك
1000023422المبلغ يتجاوز السعة مشترك
1000030422لا يمكن استخدام هذا العنوان
3000001503سعر الصرف غير متوفر حاليًا مشترك

قائمة التحقق قبل الإطلاق

أكمل كل بند قبل الانتقال إلى الإنتاج.

  • اختبرت في sandbox مسارات الدفع وwebhook والاسترداد والسحب من البداية إلى النهاية.

  • مفتاح الإنتاج والمفتاح السري محفوظان على خادمك فقط ولا يُكتبان في السجلات.

  • قائمة IP المسموح بها وعنوان webhook عبر HTTPS لبيئة الإنتاج مضبوطان.

  • التحقق من توقيع webhook مفعّل ويرفض التوقيعات غير الصالحة؛ ويرد المعالج بسرعة برمز 2xx ويتحمل تكرار التسليم.

  • لا تُكمَل الطلبات إلا عندما يُظهر webhook أو GET /payments/{code} الحالة paid، وليس اعتمادًا على متصفح العميل.

  • ترسل طلبات الكتابة Idempotency-Key وتعيد المحاولات استخدامه؛ وتحصل كل دفعة على merchant_reference جديد.

  • ساعة خادمك متزامنة عبر NTP.

  • عُيّنت طرفية إنتاج لحسابك، وأجريت مع CryptoAvans دفعة حقيقية صغيرة.

تحدّث مع مدير حسابك بشأن طرفية الإنتاج والاختبار الشامل الأخير. دخول اللوحة