الاختبار#
وضع الاختبار وضعٌ للمفتاح لا بيئة منفصلة. مفتاح povito_ck_test_… على مضيف الواجهة العادي يُنشئ كائنات livemode: false لا تمسّ مالاً ولا تشارك كائنات الإنتاج صفّاً واحداً. كل ما في هذا الموقع يعمل بالطريقة نفسها في الوضعين.
مفاتيح الاختبار#
| المفتاح | مكانه | يفتح |
|---|---|---|
povito_ck_test_<key_id>_<secret> |
خادمك | واجهة التاجر، في وضع الاختبار |
povito_pk_test_<key_id>_<secret> |
المتصفّح أو التطبيق | /v1/public/* لجلسات الاختبار الخاصة بك — لا تحتاجه إلا للمكوّن المضمَّن في المرحلة الثانية |
تُعرض المفاتيح مرّة واحدة عند إصدارها وتُخزَّن مجزّأة. لا يستطيع مفتاح اختبار قراءة كائن إنتاج (404، ولا أي تلميح بوجوده) ولا تسجيل نقطة webhook للإنتاج (403 mode_mismatch). الصلاحيات هي المجموعة نفسها في الإنتاج: sessions:write، sessions:read، refunds:write، refunds:read، webhooks:manage، events:read، customers:read.
البوابة التجريبية test#
في وضع الاختبار يمكن لتاجرك تفعيل طريقة اسمها test: مزوّد وهمي بصفحة أزرار بدل مصرف. تأكّد في GET /v1/payment_methods من "code": "test", "enabled": true؛ وإن لم تكن موجودة فاطلب من Povito تفعيلها لتاجر الاختبار عندك. تعرض بالدينار والدولار، وتدعم الاسترداد الجزئي، وتُرفض تماماً في وضع الإنتاج.
{ "…": "…", "payment_method_types": ["test"] }حذف payment_method_types على تاجر اختبار لا يُدرج test — يجب طلبها صراحةً، حتى يتصرّف مفتاح الاختبار مع طرقك الحقيقية المفعّلة كما في الإنتاج.
الأزرار#
بعد التحقّق من الهاتف تحوّل الصفحة إلى checkout.povito.com/t/{attempt_id} — المزوّد الوهمي — عارضةً المبلغ وأربعة أزرار:
| الزرّ | ما يجيب به «المزوّد» | النتيجة |
|---|---|---|
| نجاح | حُصِّل المبلغ المعروض بالضبط | المحاولة succeeded، الجلسة completed، payment_status: paid، ويُطلق checkout.session.completed |
| فشل | رُفض (simulated_decline) |
المحاولة failed، الجلسة تعود إلى open، ويُطلق payment.attempt.failed؛ يمكن للمتسوّق اختيار طريقة أخرى |
| إلغاء | ألغى المتسوّق | المحاولة canceled، الجلسة تعود إلى open |
| ياخذ 90 ثانية | لا جواب بعد | تعود الصفحة وتستعلم؛ تُحسم المحاولة بحسب قاعدة المبلغ أدناه — اختر مبلغاً ينتهي بـ …003 لتراها معلّقة فعلاً |
يعيد كل زرّ المتسوّق إلى checkout.povito.com/r/{attempt_id} — مسار العودة نفسه الذي يستخدمه المزوّد الحقيقي — حيث تطلب الصفحة من الواجهة التحقّق، تماماً كما في الإنتاج. يمكن للأتمتة الضغط على الأزرار عبر الواجهة العامة بمفتاح قابل للنشر:
curl -X POST https://api.checkout.povito.com/v1/public/test-gateway/pa_test_3M8XQ2K7A9BC4DEFGH1J/decide \
-H "Authorization: Bearer $POVITO_PUBLISHABLE_KEY" \
-H "Content-Type: application/json" \
-d '{ "decision": "succeeded" }'{ "attempt_id": "pa_test_3M8XQ2K7A9BC4DEFGH1J", "status": "succeeded", "return_url": "https://checkout.povito.com/r/pa_test_3M8XQ2K7A9BC4DEFGH1J" }decision هو succeeded أو failed أو canceled أو slow. الاستدعاء يعيد 403 لأي محاولة ليست على البوابة التجريبية.
النتائج المحكومة بالمبلغ#
للاختبارات غير المراقبة تقرّر الأرقام الثلاثة الأخيرة من المبلغ المعروض النتيجة، ما لم يُضغط زرّ:
| المبلغ ينتهي بـ | النتيجة |
|---|---|
…001 (مثل 45001) |
رفض فوري — payment.attempt.failed بالسبب simulated_decline |
…002 (مثل 45002) |
تبقى معلّقة؛ تعيد مهمّة المطابقة فحصها على فترات متزايدة، وتنتهي المحاولة بعد 24 ساعة بالسبب timeout |
…003 (مثل 45003) |
معلّقة 30 ثانية ثم تنجح — تختبر مسار «غادر المتسوّق صفحة المزوّد، ووصل الـ webhook لاحقاً» |
| أي شيء آخر | تنجح |
يجب أن تظلّ line_items متطابقة، فارفع أحد بنود charge بالمقدار نفسه.
رقم الهاتف ورمز التحقّق التجريبيان#
في وضع الاختبار يستقبل الرقم +9647500000000 دائماً الرمز 000000، ولا تُرسل أي رسالة. أي رقم آخر يمرّ بمزوّد الرسائل الحقيقي (بالحدّ نفسه: 5 كل 10 دقائق)، فاستخدم الرقم التجريبي في الأتمتة. تعرض الصفحة تلميحاً بالرمز حين تكون الجلسة في وضع الاختبار.
يعطي الهاتف المتحقَّق منه customer_token مرتبطاً بالجلسة؛ تحتفظ به الصفحة المستضافة في ملف تعريف ارتباط HTTP-only، ويمرّره السكربت في الترويسة X-Povito-Customer-Token على الواجهة العامة.
بيئات المزوّدين التجريبية#
إلى جانب البوابة الوهمية، يمكن لفريق تشغيل Povito ربط بيئات المزوّدين التجريبية الحقيقية — ZainCash UAT، وFIB staging، وStripe test — بتاجر الاختبار عندك، فتذهب جلسات zaincash أو fib أو card في وضع الاختبار إلى بيئة اختبار المزوّد نفسه. اطلب ذلك حين تكون جاهزاً للتحقّق من التكامل؛ لكل مزوّد حسابات وبطاقات اختبار خاصة يشاركها معك دعم Povito.
الـ webhooks في وضع الاختبار#
سجّل نقطة بـ "mode": "test" بمفتاح الاختبار؛ أحداث الاختبار لا تذهب إلا إلى نقاط الاختبار. ترسل أداة الاختبار حدثاً اصطناعياً بأي نوع:
curl -X POST https://api.checkout.povito.com/v1/webhook_endpoints/we_2K7A9BC4DEFGH1J3M8XQ/test \
-H "Authorization: Bearer $POVITO_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "refund.succeeded" }'للتطوير المحلي يجب أن تكون نقطتك متاحة لـ Povito عبر HTTPS — استخدم نفقاً. تُرفض أهداف الشبكة الخاصة وlocalhost عند التسجيل.
تشغيل كامل، من البداية إلى النهاية#
هذا التسلسل هو ما يجريه اختبار Povito الشامل نفسه على الواجهة — كل استدعاء في هذا الموقع، بالترتيب. $SK مفتاح اختبار سرّي، و$PK مفتاح اختبار قابل للنشر.
API=https://api.checkout.povito.com/v1
# 1. سجّل نقطة webhook (السرّ يُعرض مرّة واحدة)
curl -s -X POST $API/webhook_endpoints -H "Authorization: Bearer $SK" -H "Content-Type: application/json" \
-d '{"url":"https://your-store.example/webhooks/povito","events":["*"],"mode":"test"}'
# 2. أنشئ جلسة 45,000 دينار تعرض البوابة التجريبية والدفع عند الاستلام
curl -s -X POST $API/checkout/sessions -H "Authorization: Bearer $SK" -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
-d '{"reference_id":"POV-1041","amount":45000,"currency":"IQD",
"line_items":[{"label":"Basket","amount":42000,"type":"charge"},{"label":"Delivery","amount":5000,"type":"shipping"},{"label":"Coupon","amount":2000,"type":"discount"}],
"payment_method_types":["test","cashondelivery"],
"customer":{"phone":"+9647500000000","name":"Ali M. Ismail"},
"success_url":"https://your-store.example/thanks","cancel_url":"https://your-store.example/cart","locale":"ar"}'
# ← id cs_test_…، code 7KQ4…، status open، payment_status unpaid، payment null
# 3. ما تراه الصفحة (مفتاح قابل للنشر + الرمز). لاحظ: لا metadata، وكل طريقة مع توافرها.
curl -s $API/public/sessions/$CODE -H "Authorization: Bearer $PK"
# 4. التحقّق من الهاتف بالرمز التجريبي ← customer_token
curl -s -X POST $API/public/sessions/$CODE/otp -H "Authorization: Bearer $PK" -H "Content-Type: application/json" -d '{"phone":"+9647500000000"}'
# ← 202 { "sent": true, "retry_after": 30 }
curl -s -X POST $API/public/sessions/$CODE/otp/verify -H "Authorization: Bearer $PK" -H "Content-Type: application/json" -d '{"phone":"+9647500000000","code":"000000"}'
# ← { "customer_token": "…", "expires_at": "…", "profile": { "name": null, "addresses": [], "instruments": [] } }
# 5. ابدأ محاولة على البوابة التجريبية ← kind redirect، redirect_url https://checkout.povito.com/t/pa_test_…
curl -s -X POST $API/public/sessions/$CODE/attempts -H "Authorization: Bearer $PK" -H "X-Povito-Customer-Token: $TOKEN" -H "Content-Type: application/json" -d '{"method":"test"}'
# محاولة ثانية وهذه مفتوحة ← 409 attempt_in_progress
# 6. اضغط «فشل»: تعود الجلسة إلى open، وredirect_to يساوي null
curl -s -X POST $API/public/test-gateway/$ATTEMPT/decide -H "Authorization: Bearer $PK" -H "Content-Type: application/json" -d '{"decision":"failed"}'
curl -s $API/public/sessions/$CODE/attempts/$ATTEMPT -H "Authorization: Bearer $PK"
# ← { "attempt_id": "pa_test_…", "code": "7KQ4…", "method": "test", "status": "failed", "session_status": "open", "payment_status": "unpaid",
# "redirect_to": null, "failure_reason": "simulated_decline", "present": null, "cancel_url": "https://your-store.example/cart" }
# 7. حاول مجدداً واضغط «نجاح»: يعيد الاستعلام الآن رابط العودة مع session_id وreference_id
curl -s -X POST $API/public/sessions/$CODE/attempts -H "Authorization: Bearer $PK" -H "X-Povito-Customer-Token: $TOKEN" -H "Content-Type: application/json" -d '{"method":"test"}'
curl -s -X POST $API/public/test-gateway/$ATTEMPT2/decide -H "Authorization: Bearer $PK" -H "Content-Type: application/json" -d '{"decision":"succeeded"}'
curl -s $API/public/sessions/$CODE/attempts/$ATTEMPT2 -H "Authorization: Bearer $PK"
# ← { "attempt_id": "pa_test_…", "code": "7KQ4…", "method": "test", "status": "succeeded", "session_status": "completed", "payment_status": "paid",
# "redirect_to": "https://your-store.example/thanks?session_id=cs_test_…&reference_id=POV-1041", "failure_reason": null, "present": null, "cancel_url": "https://your-store.example/cart" }
# الاستعلام نفسه بلا رمز الجلسة، لصفحة عودة فُتحت في متصفّح آخر (يضيف merchant وlocale وlivemode):
curl -s $API/public/attempts/$ATTEMPT2 -H "Authorization: Bearer $PK"
# 8. تحقّق من الخادم: payment.method test، المحصَّل 45000 IQD، customer.phone +9647500000000
curl -s $API/checkout/sessions/$SESSION -H "Authorization: Bearer $SK"
# …ويصل webhook الحدث checkout.session.completed إلى نقطتك موقّعاً؛ ويُظهر GET /events/{id} أنّ deliveries[0].status_code يساوي 200
# 9. استرداد 5,000 ← partially_refunded؛ 41,000 ← 409 refund_exceeds_captured؛ 40,000 ← refunded
curl -s -X POST $API/refunds -H "Authorization: Bearer $SK" -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
-d '{"session_id":"'$SESSION'","amount":5000,"reason":"Customer returned the delivery item, ticket #1"}'
# 10. الدفع عند الاستلام يكتمل خارج المنصّة دون تحويل أي مبلغ
curl -s -X POST $API/checkout/sessions -H "Authorization: Bearer $SK" -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
-d '{"reference_id":"POV-1042","amount":45000,"currency":"IQD",
"line_items":[{"label":"Basket","amount":42000,"type":"charge"},{"label":"Delivery","amount":5000,"type":"shipping"},{"label":"Coupon","amount":2000,"type":"discount"}],
"payment_method_types":["cashondelivery"],"collect":{"phone":"none","name":"optional","shipping_address":"none"},
"success_url":"https://your-store.example/thanks"}'
curl -s -X POST $API/public/sessions/$CODE2/attempts -H "Authorization: Bearer $PK" -H "Content-Type: application/json" -d '{"method":"cashondelivery"}'
# ← { "kind": "offline", … }؛ اجلب الجلسة: status completed، payment_status requires_offline_collection، payment.captured_amount nullما يستحقّ الاختبار عندك#
- Idempotency: أعد إنشاءً بالمفتاح والجسم نفسيهما (
201مجدداً،Idempotent-Replayed: true)، ثم المفتاح نفسه بجسم مختلف (409 idempotency_key_conflict). - المطابقة: أنشئ بـ
reference_idوالجسم نفسيهما مرّتين والجلسة الأولى مفتوحة — تعود إليك الأولى بـ200. أنشئ مجدداً بـamountمختلف — تصير الأولىexpiredوتحصل على جلسة جديدة بـ201. - التحقّق من الصحّة:
amount: 46000على السلّة النموذجية (400 amount_mismatch)، وsuccess_urlعلى نطاق غير مسجّل (422 return_host_not_allowed)، وطريقة لم تفعّلها (422 method_not_enabled). - العزل: اقرأ جلسة اختبار بمفتاح إنتاج —
404. - توقيع الـ webhook: تحقّق من إرسال حقيقي، ثم أعد التحقّق بسرّ خاطئ وبـ
tمزاح عشر دقائق؛ يجب أن يفشل الاثنان. - العودة المتأخّرة: اضغط ياخذ 90 ثانية بمبلغ ينتهي بـ
…003، وأغلق الصفحة، وتأكّد أنّ معالج الـ webhook وحده يُكمل الطلب.