Supercommerce API Docs
Store API

Payment PhonePe Module — Storefront

The customer-side verify endpoint called after PhonePe returns the shopper, from web and mobile alike. Takes no body — PhonePe's client callback carries no payment status, so the server reads the gateway itself.

The customer-side verify endpoint, called after PhonePe hands the shopper back — from web checkout and from the mobile SDK alike. It takes no body, because PhonePe's client-side callback carries no payment status: the server reads the Order Status API itself and reconciles.

Source: api-modules/payment-phonepe/src/controllers/store-phonepe-verify.controller.ts.

PhonePe is one of several payment providers. Configuration, the place-order integration and refunds live in common/payment-phonepe.md; the webhook ingest surface is in webhooks/payment-phonepe.md. This file documents the customer-callable endpoint only.


Conventions

Authentication

EndpointAuth
POST /store/orders/:id/phonepe/verifyrequired (customer session)

The order must belong to the caller — cross-customer ids return 404, never 403, so another shopper's orders cannot be probed for existence. The endpoint also pins to the order_payment row written at place-order, so a gateway response for a different order cannot move this one.

Response envelope

{ "data": <OrderResponse>, "message": "Success", "statusCode": 200 }

Endpoint

POST /store/orders/:id/phonepe/verify — Settle a PhonePe payment

Call this when PhonePe returns the customer:

  • web — after the redirect back to your return URL, or when the checkout script's callback fires with CONCLUDED;
  • mobile — when the SDK's activity result arrives.

Neither of those tells you whether the payment succeeded, which is why this endpoint exists. It:

  1. Looks up the order and rejects (404) if it isn't the caller's.
  2. Rejects (400) if the order wasn't placed via PhonePe.
  3. Returns the current order view if it is already paid — the webhook may have won the race.
  4. Otherwise reads GET /checkout/v2/order/{merchantOrderId}/status, requires the root state to be COMPLETED with an amount matching the order total, then transitions the order to confirmed / paid, commits the inventory reservation, stamps paidAt and emits order.paid.

Path params

NameNotes
idInternal order id (not the PhonePe OMO… id)

Body — none.

Response 200OrderResponse (see order.md). Reflects the post-reconciliation state: paymentStatus: "paid", paidAt set, pendingClientAction: null.

Errors

StatusCodeWhen
400BAD_REQUESTOrder was not placed via the PhonePe provider
400BAD_REQUESTGateway order id doesn't match the place-time row, or the amount doesn't match the order total
404NOT_FOUNDOrder does not exist or belongs to another customer
409PAYMENT_NOT_YET_CAPTUREDPhonePe still reports PENDING — the customer may not have finished. Retry shortly rather than treating checkout as failed

Idempotency — safe to retry. If the webhook reconciled first, this returns the same view without re-running the transition.

If it is never called — because the browser closed or the app crashed — the webhook reconciles independently, and if that is also lost the stale-pending sweep asks PhonePe directly before cancelling. A paid order is not auto-cancelled by a missed webhook.


  • orderpendingClientAction on the place-order response is the checkout bootstrap data (a redirectUrl on web, SDK order id + token on mobile); this endpoint is the back end of that flow. See order.md.
  • webhooks/payment-phonepe — the asynchronous path that may reconcile before or after this call.

On this page