Povitoالمطوّرون

صفحة الدفع المستضافة#

جلسة الدفع هي وحدة العمل: سلّة واحدة، متسوّق واحد، نتيجة واحدة. هذه الصفحة دليل حقلاً بحقل لـ POST /v1/checkout/sessions، ولما تفعله الصفحة على checkout.povito.com/c/{code} بكل حقل. أما التدفّق الكامل ففي البداية السريعة.

دورة حياة الجلسة#

text
open ──▶ processing ──▶ completed
  │          │
  │          └──▶ open        (فشلت محاولة؛ يختار المتسوّق طريقة أخرى)
  ├──▶ expired               (انقضى expires_at، أو استُدعي POST /expire)
  └──▶ canceled              (تراجع المتسوّق على الصفحة)

status يخبرك أين الجلسة؛ وpayment_status يخبرك عن المال: unpaid → paid | requires_offline_collection | failed، وبعد الاستردادات partially_refunded | refunded. قد تحمل الجلسة الواحدة محاولات دفع عديدة — المتسوّق الذي رُفض على زين كاش يمكنه أن ينجح على FIB دون أن تُنشئ شيئاً جديداً.

استدعاء الإنشاء#

الحقل النوع مطلوب القواعد
reference_id نص، 1–128 نعم معرّفك الخاص (طلب، سلّة، فاتورة). ما دامت جلسة له مفتوحة في هذا الوضع، فإنّ إنشاءً ثانياً بنفس amount وcurrency وline_items يعيد تلك الجلسة بـ 200؛ أما الإنشاء بسعر مختلف (تغيّرت السلّة) فيُنهي الجلسة المفتوحة — ويُطلق checkout.session.expired — ويُنشئ جلسة جديدة بـ 201.
amount عدد صحيح بأصغر وحدة نعم يجب أن يكون موجباً ومساوياً لصافي line_items.
currency ISO 4217 نعم IQD (دنانير كاملة) أو USD (سنتات) حالياً.
line_items[] انظر أدناه نعم، 1–100 charge وshipping وfee تُجمع؛ discount يُطرح. amount للوحدة الواحدة؛ وquantity (افتراضياً 1) تضربه.
payment_method_types[] رموز الطرق، 1–20 لا افتراضياً كل طريقة مفعّلة على تاجرك في هذا الوضع. رمز غير مفعّل يُفشل الإنشاء كلّه بـ 422 method_not_enabled.
customer {phone?, name?, address?} لا تعبئة مسبقة. يُخزَّن phone بصيغة E.164 (+9647…)؛ وتُطبَّع الصيغ العراقية المحلية تلقائياً — 0780 123 4567 و7801234567 و009647801234567 و+964 780 123 4567 تصير كلّها +9647801234567. يمكن للمتسوّق تغييره؛ والهاتف الذي يتحقّق منه هو ما يعود إليك.
collect {phone, name, shipping_address} لا كل منها required أو optional أو none. الافتراضي required، required، none.
presentment {currency, fx_rate} لا للطرق التي لا تستطيع عرض عملة الجلسة فقط — Stripe يعرض بالدولار. راجع العرض بالدولار.
locale ar، ku، en لا لغة الصفحة. الافتراضي: اللغة الافتراضية لتاجرك.
success_url رابط https نعم حيث يصل المتسوّق بعد الإكمال. يجب أن يكون النطاق في قائمتك المسموح بها.
cancel_url رابط https لا حيث يصل المتسوّق بعد الإلغاء. قاعدتا https:// والنطاق نفسهما.
expires_at ISO 8601 لا الافتراضي الآن + 60 دقيقة. يجب أن يكون بين الآن + 5 دقائق والآن + 24 ساعة، وإلا 422 expiry_out_of_range.
metadata كائن لا حتى 20 مفتاحاً بطول ≤ 64 حرفاً، قيم نصية ≤ 500 حرف. يُعاد كما هو في الجلسة والـ webhooks؛ لا يُعرض على المتسوّق أبداً؛ لا يُستخدم للمال أبداً.

الحقول المجهولة تُرفض (400 validation_error مع اسم الحقل في details[])، فلا يمكن لخطأ إملائي أن يُسقط خياراً بصمت.

البنود#

json
"line_items": [
  { "label": "غلاية زرقاء", "amount": 21000, "quantity": 2, "type": "charge" },
  { "label": "التوصيل — Hi-Express", "amount": 5000, "type": "shipping" },
  { "label": "قسيمة RAMADAN", "amount": 2000, "type": "discount" }
]

الصافي: 21,000 × 2 + 5,000 − 2,000 = 45,000، فيجب أن يكون amount هو 45000. أي شيء آخر يعطي 400 amount_mismatch:

400 amount_mismatchjson
{
  "error": {
    "code": "amount_mismatch",
    "message": "Line items net to 45000 but amount is 46000.",
    "details": [{ "field": "line_items", "issue": "sum_differs" }],
    "request_id": "req_5c1b0a4e-2f6d-4a0b-9d3e-7b8c1f2a3d4e"
  }
}

تعرض الصفحة label والمبلغ الإجمالي لكل بند، بلغة المتسوّق، والدينار بدنانير كاملة.

طرق الدفع في الجلسة#

مرّر payment_method_types لتضييق الخيارات — مثلاً ["zaincash"] حين يكون المتسوّق قد اختار زين كاش عندك، فتذهب الصفحة مباشرة إلى المزوّد بعد التحقّق من الهاتف. واحذفه لعرض كل الطرق المفعّلة. القائمة التي يجوز تمريرها هي GET /v1/payment_methods مقتصرةً على enabled: true؛ ويخبرك كل صفّ أيضاً بـ flow وcurrencies وcapabilities للطريقة — راجع طرق الدفع.

الطريقة المفعّلة لكنّها غير صالحة لهذه الجلسة (عملة مختلفة، مبلغ خارج حدود المزوّد، مزوّد غير مهيّأ في هذا الوضع) لا تمنع نجاح الإنشاء؛ تعرضها الصفحة غير متاحة مع السبب، وتفشل محاولة الدفع بها بـ 422 method_not_available.

التعبئة المسبقة وما تجمعه#

customer يعبّئ مسبقاً؛ وcollect يقرّر ما تُصرّ الصفحة عليه.

collect.phone السلوك
required (الافتراضي) يجب أن يتحقّق المتسوّق من رقم هاتف برمز نصي قبل أي محاولة. يُخزَّن الهاتف المتحقَّق منه في الجلسة.
optional تعرض الصفحة التحقّق، لكن يمكن للمحاولة أن تمضي من دونه.
none لا خطوة هاتف. للدفع عند الاستلام حين تكون قد تحقّقت من الهاتف بنفسك.

يعمل collect.name وcollect.shipping_address بالطريقة نفسها؛ حين يكونان required، تفشل المحاولة من دونهما بـ 400 validation_error مع ذكر name أو address. تستخدم العناوين شكل المنصّة: governorate (رمز المحافظة العراقية)، city، line، landmark، country (افتراضياً IQ) وgeo: {lat, lng} اختيارياً.

ما يُدخله المتسوّق يعود في الجلسة:

json
"customer": {
  "id": null,
  "phone": "+9647800000000",
  "name": "علي محمد إسماعيل",
  "address": { "governorate": "BG", "city": "الكرادة", "line": "شارع 12، بناية 4", "landmark": "يم الصيدلية", "country": "IQ" }
}

customer.id هو معرّف Povito للمتسوّق ويبقى null حتى تصدر ملفّات العملاء في المرحلة الثانية.

اللغة#

locale تختار لغة الصفحة: ar (العربية)، ku (الكردية السورانية) أو en. يمكن للمتسوّق التبديل على الصفحة؛ وتحتفظ الجلسة باللغة التي حدّدتها. أسماء طرق الدفع تترجمها Povito (display_name في GET /payment_methods يحمل اللغات الثلاث).

الانتهاء#

تنتهي الجلسات عند القراءة: GET بعد expires_at يعيد status: "expired"، وتتكفّل مهمّة دورية بما لا يقرؤه أحد. يُصدر الانتهاء الحدث checkout.session.expired. ولإغلاق جلسة مبكراً — غيّر المتسوّق سلّته — استدعِ POST /v1/checkout/sessions/{id}/expire؛ وهو 409 session_not_open لأي جلسة غير مفتوحة.

إنهاء جلسةbash
curl -X POST https://api.checkout.povito.com/v1/checkout/sessions/cs_test_7KQ4M2XA9BC3DEFGH1JK/expire \
  -H "Authorization: Bearer $POVITO_SECRET_KEY"

الجلسة في حالة processing (محاولة عند مزوّد) لا يمكنك إنهاؤها؛ تعود إلى open إن فشلت المحاولة، وتكتمل إن نجحت.

روابط العودة#

يجب أن يكون success_url وcancel_url بـ https://، وأن يطابق نطاقهما — أو يكون نطاقاً فرعياً من — نطاقاً في قائمة نطاقات العودة المسموح بها لتاجرك، التي تضبطها Povito عند إنشاء حسابك وتغيّرها عند الطلب. وإلا فشل الإنشاء بـ 422 return_host_not_allowed.

بعد الإكمال تُضيف الصفحة session_id وreference_id إلى success_url (بـ & إن كان فيه استعلام أصلاً). لا يُضاف شيء آخر أبداً: لا حالة، لا مبلغ، لا طريقة. اقرأ session_id، واستدعِ GET للجلسة، وتصرّف بناءً على ذلك.

يُستخدم cancel_url كما هو.

البيانات الوصفية#

metadata لمفاتيح الربط الخاصة بك — معرّف السلّة، مجموعة الطلب، الحملة. يُعاد في الجلسة وفي كل حدث عنها، ولا يُعرض على الصفحة أبداً. لا تضع فيه مالاً أو أسعاراً أو أي شيء يجب ألا يراه المتسوّق؛ الصفحة لا تستقبله، لكن سجلّات الـ webhook عندك ستحتويه.

كائن الجلسة#

كل قراءة للجلسة تعيد الشكل نفسه (GET، استجابة الإنشاء، data.object في الأحداث):

الحقل المعنى
id cs_live_… أو cs_test_… — 20 حرفاً بعد الوضع.
object دائماً checkout.session.
code الرمز القصير غير القابل للتخمين في url؛ 26 حرفاً، نحو 130 بت من العشوائية.
url أرسل المتسوّق إلى هنا.
livemode false لمفاتيح الاختبار.
status، payment_status راجع دورة الحياة.
reference_id، amount، currency، line_items، payment_method_types، collect، presentment، locale، success_url، cancel_url، expires_at، metadata كما أُنشئت (line_items تعود مع quantity معبّأة).
customer {id, phone, name, address} — ما تحقّق منه المتسوّق وأدخله، والتعبئة المسبقة قبل ذلك.
payment null حتى تنجح محاولة، ثم {attempt_id, method, gateway, gateway_reference, captured_amount, captured_currency, presented_amount, presented_currency, fx_rate, captured_at, instrument}. للدفع عند الاستلام يكون captured_amount وcaptured_currency بقيمة null — لم يُحصَّل شيء.
refunded_amount مجموع الاستردادات الناجحة، بالعملة المحصَّلة.
completed_at متى صارت status تساوي completed.
created_at، updated_at ISO 8601، بالتوقيت العالمي.

البحث عن الجلسات#

بمعرّفكbash
curl "https://api.checkout.povito.com/v1/checkout/sessions?reference_id=POV-1041" \
  -H "Authorization: Bearer $POVITO_SECRET_KEY"

تقبل القوائم reference_id وstatus وcreated_after وlimit (1–100، افتراضياً 20) وcursor؛ وتعيد { data: [...], next_cursor }، الأحدث أولاً، وnext_cursor: null في الصفحة الأخيرة. الجلسة المنشأة بمفتاح اختبار غير مرئية لمفتاح إنتاج والعكس — يعيد البحث لا شيء بدل 403.

الهوية البصرية#

تعرض الصفحة display_name وlogo_url ولون تمييز، تضبطها Povito في سجلّ تاجرك. يُفحص تباين لون التمييز عند الحفظ؛ تلتقطه الأزرار وحلقات التركيز، ويبقى كل شيء آخر على لوحة Povito المحايدة حتى تكون صفحة كل تاجر السطح الآمن نفسه الذي يعرفه المتسوّق. الصفحة بالعربية والكردية والإنجليزية، من اليمين إلى اليسار حيث تقتضي اللغة، وتلبّي WCAG 2.1 AA.

ما تفعله الصفحة، بالترتيب#

  1. الهاتف — يُدخل المتسوّق رقم موبايل عراقي ورمزاً من ستة أرقام يصله برسالة نصية (5 رموز كل 10 دقائق لكل رقم؛ يعيش الرمز 5 دقائق؛ 5 محاولات خاطئة تقفله). في وضع الاختبار يقبل +9647500000000 الرمز 000000.
  2. الهوية — الاسم والعنوان بحسب collect، معبّأين مسبقاً من customer.
  3. الطريقة — بطاقة لكل طريقة في payment_method_types، بترتيب العرض عند تاجرك، وغير المتاح منها باهت مع السبب.
  4. الدفع — تحويل إلى المزوّد، أو رمز QR وروابط تطبيق للمسح، أو إكمال فوري، بحسب تدفّق الطريقة.
  5. العودة — يعيد المزوّدون المتسوّق إلى checkout.povito.com/r/{attempt_id}؛ تتجاهل الصفحة كل معاملات استعلام المزوّد لأغراض المال، وتطلب من الواجهة التحقّق لدى المزوّد، وعندها فقط ترسل المتسوّق إلى success_url، أو تعرض الفشل مع «جرّب طريقة أخرى».