Contents
On this page

Stripe Webhook - Plugixa Chauffeur

POST /plugixa-chauffeur/v1/public/stripe-webhook

The endpoint Stripe calls, server to server, to report payments, refunds and disputes. You register it in Stripe; you do not call it yourself.

Public: no login and no capability. It is authenticated by Stripe’s signature instead of a login.

Note. The endpoint always answers 200, even to a request it rejects: Stripe would otherwise retry for days, and a retried bad signature is still bad. Read received to see whether the event was accepted.

Request

POST https://your-site.example/wp-json/plugixa-chauffeur/v1/public/stripe-webhook

Request body

Content type: application/json

The body is the Stripe event, sent by Stripe as is. Shown here shortened.

{
  "id": "evt_1QaBcD2eZvKYlo2C",
  "type": "checkout.session.completed",
  "data": {
    "object": {
      "id": "cs_live_a1B2c3D4e5F6",
      "payment_status": "paid",
      "payment_intent": "pi_3QaBcD2eZvKYlo2C1x9yAbCd",
      "client_reference_id": "42",
      "metadata": {
        "booking_id": "42",
        "booking_number": "PXC-20261112-0815"
      }
    }
  }
}

Setting it up

Register this URL in your Stripe Dashboard (Developers -> Webhooks), subscribe it to the events below, and paste its signing secret (whsec_...) into Webhook signing secret in Settings.

https://your-site.example/wp-json/plugixa-chauffeur/v1/public/stripe-webhook

The signature check

Before anything in the body is read, the Stripe-Signature header is checked against the raw body and the signing secret: it must hold a timestamp t and at least one v1 signature, the timestamp must be within 300 seconds of the server clock, and the HMAC-SHA256 of t.body, keyed with the secret, must equal one of the v1 signatures. With no signing secret saved, every call is rejected.

Events

Event What it changes
checkout.session.completed A paid session marks the booking paid and moves it from pending to processing. An unpaid one (a bank debit still processing) sets the payment status to pending
checkout.session.async_payment_succeeded The delayed payment arrived: paid, as above
checkout.session.async_payment_failed Payment status failed
checkout.session.expired Nothing changes; the plugixa_chauffeur_stripe_notice action fires
refund.created, refund.updated partially_refunded or refunded; a full refund also sets an open booking to refunded
refund.failed Nothing changes; the notice action fires
charge.dispute.created Payment status disputed
charge.dispute.closed Back to paid when the dispute was won or closed as an inquiry, else stays disputed

The booking is found from the booking_id the plugin puts in the session and payment metadata, so refunds and disputes made in the Stripe Dashboard are matched too. A paid booking is never moved back to pending or failed by a late or replayed event, and the same event twice changes nothing.

Responses

200 Always. received says whether the event was acted on.

This response is not wrapped in the success envelope.

{
  "received": true
}
Field Type Meaning
received boolean true when the signature is valid and the event is one listed above; false otherwise, including for an event type the plugin ignores

Errors

This endpoint never answers with an error status.

Plus the errors every endpoint can return.

Examples

curl -s -X POST "https://your-site.example/wp-json/plugixa-chauffeur/v1/public/stripe-webhook" \
  -H "Stripe-Signature: $STRIPE_SIGNATURE" \
  -H "Content-Type: application/json" \
  -d '{"id":"evt_1QaBcD2eZvKYlo2C","type":"checkout.session.completed","data":{"object":{"id":"cs_live_a1B2c3D4e5F6","payment_status":"paid","payment_intent":"pi_3QaBcD2eZvKYlo2C1x9yAbCd","client_reference_id":"42","metadata":{"booking_id":"42","booking_number":"PXC-20261112-0815"}}}}'
<?php
$ch = curl_init('https://your-site.example/wp-json/plugixa-chauffeur/v1/public/stripe-webhook');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Stripe-Signature: ' . getenv('STRIPE_SIGNATURE'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'id'   => 'evt_1QaBcD2eZvKYlo2C',
        'type' => 'checkout.session.completed',
        'data' => [
            'object' => [
                'id'                  => 'cs_live_a1B2c3D4e5F6',
                'payment_status'      => 'paid',
                'payment_intent'      => 'pi_3QaBcD2eZvKYlo2C1x9yAbCd',
                'client_reference_id' => '42',
                'metadata'            => [
                    'booking_id'     => '42',
                    'booking_number' => 'PXC-20261112-0815',
                ],
            ],
        ],
    ]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
const response = await fetch('https://your-site.example/wp-json/plugixa-chauffeur/v1/public/stripe-webhook', {
  method: 'POST',
  headers: {
    'Stripe-Signature': process.env.STRIPE_SIGNATURE,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    id: 'evt_1QaBcD2eZvKYlo2C',
    type: 'checkout.session.completed',
    data: {
      object: {
        id: 'cs_live_a1B2c3D4e5F6',
        payment_status: 'paid',
        payment_intent: 'pi_3QaBcD2eZvKYlo2C1x9yAbCd',
        client_reference_id: '42',
        metadata: { booking_id: '42', booking_number: 'PXC-20261112-0815' },
      },
    },
  }),
});
const { received } = await response.json();

Quick Links

Share