# المدفوعات غير المتزامنة (/ar/integrate/asynchronous-payments)

بعض طرق الدفع بتتدفع بعد الـ checkout. الجلسة بتكتمل الأول والفلوس بتوصل بعدين، فنفّذ الطلب بناءً على paymentStatus مش على الاكتمال.

فوري متاحة على كل حساب وفي كل طريقة تكامل، بما فيها الدفع المستضاف وروابط الدفع، فكل معالِج webhook لازم يتعامل مع ده. مع فوري، العميل بياخد رقم مرجعي عند الدفع وبيدفعه بعدين في أي منفذ أو من تطبيق فوري. الـ checkout بيخلص قبل ما الفلوس توصل.

قاعدة واحدة بتغطي الموضوع: `status` بتقول إذا كان العميل خلّص الـ checkout، و`paymentStatus` بتقول إذا كانت الفلوس وصلت. نفّذ الطلب بناءً على `paymentStatus`، ومتعتمدش أبدًا على `status` لوحدها.

<Callout type="warn">
  `checkout.session.completed` بتوصل بـ `paymentStatus: "unpaid"` لدفعة فوري. المعالِج اللي بينفّذ
  الطلب على الحدث من غير ما يقرا `paymentStatus` هيشحن طلبات ماتدفعتش.
</Callout>

## الأحداث التلاتة [#الأحداث-التلاتة]

| الحدث                                      | `paymentStatus` | اللي تعمله                                   |
| ------------------------------------------ | --------------- | -------------------------------------------- |
| `checkout.session.completed`               | `unpaid`        | سجّل الطلب على إنه في انتظار الدفع. متنفّذش. |
| `checkout.session.async_payment_succeeded` | `paid`          | نفّذ الطلب.                                  |
| `checkout.session.async_payment_failed`    | `unpaid`        | اقفل الطلب. العميل محتاج جلسة جديدة.         |

البطاقات وValU بتطلّق `checkout.session.completed` بـ `paymentStatus: "paid"` ومفيش غيره.

الـ `data.object` في الأحداث التلاتة هو <ApiLink href="/api-reference/objects/checkout-session">Checkout Session</ApiLink> كاملة، فمعالِج واحد بيكفي لكل الأحداث. الرقم المرجعي اللي العميل بيدفعه موجود في `paymentIntent.nextAction.displayVoucherDetails` (`reference` و`expiresAt` و`instructions`).

## دالة تنفيذ واحدة [#دالة-تنفيذ-واحدة]

خلّي التنفيذ مشروط بـ `paymentStatus` واستدعيه من حدثَي النجاح الاتنين. خلّيه آمن لو اتنفّذ مرتين لنفس الجلسة: التسليم بيتعاد.

```typescript
async function fulfillCheckout(session: CheckoutSession) {
  if (session.paymentStatus !== "paid") return;
  if (await orders.isFulfilled(session.id)) return;
  await orders.fulfill(session.id, session.lineItems);
}

app.post("/webhooks/xpay", async (req, res) => {
  const event = verifyAndParse(req);
  const session = event.data.object;

  switch (event.type) {
    case "checkout.session.completed":
    case "checkout.session.async_payment_succeeded":
      await fulfillCheckout(session);
      break;
    case "checkout.session.async_payment_failed":
      await orders.markUnpaid(session.id);
      break;
  }

  res.sendStatus(200);
});
```

## اللي العميل بيشوفه [#اللي-العميل-بيشوفه]

في الـ Hosted Checkout، العميل بيشوف الرقم المرجعي، بيدوس **Done**، وبيوصل لصفحة التأكيد في حالة انتظار الدفع أو لـ `afterCompletion.redirect.url` بتاعك. فتح الجلسة تاني بيعرض الرقم المرجعي لحد ما يتدفع.

مع Drop-in وElements، الرقم المرجعي بيتفتح في نفس الطبقة اللي بيتفتح فيها تحدي 3D Secure. عند **Done**، `confirm()` بتتحلّ بـ `{ type: "success", session }` والـ `session.status.paymentStatus` بتبقى `"unpaid"`، والـ `onComplete` بتاعة Drop-in بتتطلّق بـ `paymentStatus: "unpaid"`. اعرض صفحة انتظار دفع، مش صفحة شكر.

الجلسة المكتملة مينفعش تتدفع بطريقة تانية. العميل اللي غيّر رأيه بيبدأ جلسة جديدة.

## الخطوة الجاية [#الخطوة-الجاية]

<Cards>
  <Card icon="<Webhook />" title="مرجع الأحداث" href="/integrate/webhooks/event-reference">
    كل حدث وإمتى بيتطلّق.
  </Card>

  <Card icon="<Braces />" title="Checkout Session" href="/integrate/checkout-session/overview">
    حالات `status` و`paymentStatus`.
  </Card>

  <Card icon="<FlaskConical />" title="وضع الاختبار" href="/get-started/test-mode">
    ادفع رقم فوري تجريبي وشوف الأحداث بتوصل.
  </Card>

  <Card icon="<ListTree />" title="بعد الاكتمال" href="/integrate/checkout-session/after-completion">
    صفحة العودة بتعمل إيه لما الدفعة لسه ماتأكدتش.
  </Card>
</Cards>