Phase 4 — Payment
The payment flow via RideNDine → Stripe. COOCO does not handle money directly — it only initiates the payment session and updates status from webhooks (not from client callbacks).
Overview
COOCO → RideNDine → Stripe ← Customer
↓ (webhook)
COOCO (update status)
| Step | From | To | Action |
|---|---|---|---|
| 4.1 | COOCO | RideNDine | POST /payment/process |
| 4.2 | RideNDine | Stripe | Create PaymentIntent |
| 4.3 | Stripe | RideNDine | PaymentIntent response |
| 4.4 | RideNDine | COOCO | Response POST /payment/process |
| 4.5 | Customer | Stripe | Stripe checkout form |
| 4.6 | Stripe | RideNDine | POST /api/webhooks/stripe |
| 4.7 | RideNDine | COOCO | POST /webhooks/payment-result |
Step 4.1 — Initiate Payment
From: COOCO → To: RideNDine
Endpoint: POST /payment/process
Called via Directus Extension: POST /ridendine/checkout or /ridendine/payment.
Request Body
{
"ridendine_order_id": "<orders.ridendine_order_id>",
"external_order_id": "<orders.id>",
"amount_cents": "<total_cents>",
"currency": "USD",
"return_url": "<success_url>",
"cancel_url": "<cancel_url>"
}
Step 4.2 — Create PaymentIntent (RideNDine Internal)
RideNDine initiates a PaymentIntent on Stripe. COOCO does not participate in this step.
Step 4.3 — Stripe Response (RideNDine Internal)
Stripe returns client_secret / status to RideNDine. COOCO does not participate.
Step 4.4 — Receive Payment Session
From: RideNDine → To: COOCO
Purpose: COOCO receives the information needed to display the Stripe payment form.
Response
{
"client_secret": "<stripe_client_secret>",
"checkout_url": "<optional_hosted_url>",
"payment_intent_id": "<pi_...>"
}
Processing
- Save
orders.payment_intent_id - Pass
client_secrettocookin-webto embed Stripe UI Elements
Step 4.5 — Customer Completes Payment
From: Customer → To: Stripe
Customer interacts directly with the Stripe checkout form. COOCO does not send anything at this step.
Step 4.6 — Stripe Webhook (RideNDine Internal)
Stripe calls POST /api/webhooks/stripe on RideNDine to confirm payment_succeeded / payment_failed. COOCO does not participate.
Step 4.7 — Webhook: Payment Result
From: RideNDine → To: COOCO
Endpoint (COOCO expose): {DIRECTUS_URL}/ridendine/webhooks/payment-result
Method: POST
Purpose: COOCO updates to PAID / FAILED — trusts only the webhook, not the client callback.
Received Payload
{
"event_id": "<uuid>",
"event": "payment.succeeded | payment.failed",
"external_order_id": "<orders.id>",
"payment_intent_id": "<pi_...>",
"paid_at": "<ISO8601>"
}
Processing on payment.succeeded
orders.payment_status = PAIDorders.status = PENDING_CHEF_APPROVAL- Log to
integration_eventswithevent_id(idempotency)
DO NOT set PAID when the user clicks the "Pay" button. Only set it upon receiving this webhook from RideNDine.