Skip to content

Webhooks API

Stripe and MobiMatter webhook endpoints with signature verification for asynchronous payment and eSIM events.

This endpoint does not require JWT authentication. Security is provided by Stripe's signature verification using the STRIPE_WEBHOOK_SECRET environment variable.

MobiMatter callbacks are also public and use MobiMatter's documented RSA-SHA256 signature plus a comparison against the encrypted configured merchant ID.

POST /webhooks/mobimatter

Accepts delayed/KYC order completion callbacks. The handler validates the payload, verifies the signature over orderId.merchantId.providerName, stores a minimal idempotency receipt, and enqueues GET-only reconciliation. It never persists the callback's QR, LPA, or other activation credentials.

Duplicate callbacks return 200. An unknown but valid signed order is acknowledged and retained as a minimal receipt. If the deterministic queue handoff fails, the endpoint returns 503 so MobiMatter can redeliver within its documented retry window.

See MobiMatter eSIM Provider Operations for deployment and recovery details.


POST /webhooks/stripe

Handle Stripe webhook events. Verifies the Stripe-Signature header before processing.

Signature Verification Flow

  1. Read raw request body as text (required for signature verification — parsed JSON cannot be verified)
  2. Extract Stripe-Signature header
  3. Call stripe.webhooks.constructEvent(payload, signature, STRIPE_WEBHOOK_SECRET) — throws on invalid signature
  4. Route the event by type; return 200 immediately per T-05-11 (quick acknowledgement)
  5. Payment processing happens asynchronously via PaymentService

Request Headers

HeaderRequiredDescription
Stripe-SignatureYesStripe webhook signature for verification

Request Body

The raw Stripe event payload (JSON). See Stripe Webhook Events for full schema.

Handled Event Types

Event TypeAction
payment_intent.succeededMark payment as completed, generate receipt number, enqueue settlement job
payment_intent.payment_failedMark payment as failed with reason from last_payment_error.message
payment_intent.canceledMark payment as failed with reason payment_intent.canceled
OtherAcknowledge and ignore

Settlement verification (payment_intent.succeeded)

The service verifies the provider-reported status, currency, amount, amount_received, and metadata (payment_id, order_id, user_id) against the stored payment row. On mismatch the payment is NOT completed. The payment row is marked failed with reason PAYMENT_PROVIDER_AMOUNT_MISMATCH (only if it is still processing — a completed payment is never downgraded by a late duplicate). A reconciliation record is written to the payment_webhook_events table with the mismatch detail. Both writes share one transaction. The endpoint still returns 200 so the provider stops retrying. If the recording fails, the endpoint returns 500 with code PAYMENT_MISMATCH_RECORD_FAILED so the provider redelivers the event. The mock gateway bypasses verification.

Response

200 OK — Handled event:

json
{
  "received": true
}

200 OK — Unhandled event type (ignored):

json
{
  "received": true,
  "ignored": true
}

Error Responses

CodeStatusCondition
STRIPE_WEBHOOK_SIGNATURE_MISSING400Stripe-Signature header not provided
STRIPE_WEBHOOK_SIGNATURE_INVALID400Signature verification failed

Example Webhook Payload

Stripe sends events as JSON with the following structure:

json
{
  "id": "evt_1Rabc2DEF3ghi4JKL",
  "object": "event",
  "type": "payment_intent.succeeded",
  "created": 1746057600,
  "data": {
    "object": {
      "id": "pi_1Mabc2DEF3ghi4JKL",
      "object": "payment_intent",
      "amount": 4999,
      "currency": "sar",
      "status": "succeeded",
      "metadata": {
        "payment_id": "c4d5e6f7-a8b9-0123-cdef-234567890123",
        "order_id": "b3c4d5e6-f7a8-9012-bcde-f12345678901",
        "user_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
      }
    }
  },
  "livemode": false,
  "pending_webhooks": 1,
  "request": {
    "id": "req_abc123",
    "idempotency_key": null
  }
}

Payment Failed Example

json
{
  "id": "evt_1Rabc2DEF3ghi4JKL",
  "object": "event",
  "type": "payment_intent.payment_failed",
  "created": 1746057600,
  "data": {
    "object": {
      "id": "pi_1Mabc2DEF3ghi4JKL",
      "object": "payment_intent",
      "amount": 4999,
      "currency": "sar",
      "status": "requires_payment_method",
      "last_payment_error": {
        "message": "Your card was declined.",
        "type": "card_error",
        "code": "card_declined"
      },
      "metadata": {
        "payment_id": "c4d5e6f7-a8b9-0123-cdef-234567890123",
        "order_id": "b3c4d5e6-f7a8-9012-bcde-f12345678901",
        "user_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
      }
    }
  },
  "livemode": false,
  "pending_webhooks": 1
}

Payment Canceled Example

json
{
  "id": "evt_1Rabc2DEF3ghi4JKL",
  "object": "event",
  "type": "payment_intent.canceled",
  "created": 1746057600,
  "data": {
    "object": {
      "id": "pi_1Mabc2DEF3ghi4JKL",
      "object": "payment_intent",
      "amount": 4999,
      "currency": "sar",
      "status": "canceled",
      "metadata": {
        "payment_id": "c4d5e6f7-a8b9-0123-cdef-234567890123",
        "order_id": "b3c4d5e6-f7a8-9012-bcde-f12345678901"
      }
    }
  },
  "livemode": false,
  "pending_webhooks": 1
}

Webhook Configuration

Environment Variables

VariableRequiredDescription
STRIPE_WEBHOOK_SECRETIf PAYMENT_PROVIDER=stripeStripe webhook signing secret (whsec_...)
STRIPE_SECRET_KEYIf PAYMENT_PROVIDER=stripeStripe API secret key (sk_test_... or sk_live_...)

Stripe Dashboard Setup

  1. Go to Developers → Webhooks in the Stripe Dashboard
  2. Click Add endpoint
  3. Set the endpoint URL to https://<api-production-origin>/webhooks/stripe
  4. Select events: payment_intent.succeeded, payment_intent.payment_failed, payment_intent.canceled
  5. Copy the Signing secret to STRIPE_WEBHOOK_SECRET

Local Testing with Stripe CLI

bash
# Install Stripe CLI, then:
stripe listen --forward-to http://localhost:3001/webhooks/stripe

# Trigger a test event:
stripe trigger payment_intent.succeeded

Internal documentation - Activation System