REST API
BoxCart exposes a WordPress REST API endpoint for Stripe webhook processing, providing real-time payment status updates for your orders.
Overview
BoxCart currently exposes one REST API endpoint dedicated to Stripe webhook processing. The endpoint is registered by the BoxCart_Webhook class, which hooks into the rest_api_init action during WordPress initialisation.
The REST API is used as a callback URL for Stripe to notify your site about payment events in real time. This serves as a reliable backup to the synchronous PaymentIntent status check that runs when a customer is redirected back to your site after payment.
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
register_rest_route(
'boxcart/v1',
'/webhook/stripe',
array(
'methods' => 'POST',
'callback' => array( $this, 'handle_stripe_webhook' ),
'permission_callback' => '__return_true',
)
);
Stripe Webhook Endpoint
| Property | Value |
|---|---|
| Method | POST |
| URL | /wp-json/boxcart/v1/webhook/stripe |
| Handler | BoxCart_Webhook::handle_stripe_webhook() |
| Processing | BoxCart_Stripe::handle_webhook_event() |
| Auth | Stripe signature verification (no WordPress auth required) |
The full URL for your site will be:
https://yoursite.com/wp-json/boxcart/v1/webhook/stripe
This URL is also displayed as a read-only field in BoxCart → Settings → Payments when Stripe is enabled, so you can copy it directly from there.
Authentication
The webhook endpoint does not use WordPress authentication. Instead, it relies on Stripe signature verification using HMAC-SHA256 to ensure that incoming requests genuinely originate from Stripe.
When Stripe sends a webhook event, it includes a Stripe-Signature header containing a timestamp and one or more signatures. BoxCart verifies these by:
- Parsing the
Stripe-Signatureheader to extract the timestamp (t) and signature(s) (v1). - Checking that the timestamp is within a 5-minute tolerance window to prevent replay attacks.
- Constructing the signed payload as
{timestamp}.{raw_body}. - Computing the expected signature using
hash_hmac('sha256', $signed_payload, $webhook_secret). - Comparing the expected signature against the provided signature(s) using
hash_equals()for timing-safe comparison.
The webhook secret is a sensitive credential. Never expose it in client-side code, commit it to version control, or share it publicly. It is stored as a password-type field in BoxCart settings and should be treated with the same care as your Stripe secret key.
Supported Events
The webhook handler processes two Stripe event types. All other event types are acknowledged with a 200 response but no action is taken.
| Stripe Event | Action | Details |
|---|---|---|
payment_intent.succeeded |
Marks order as paid | Updates payment_status to paid and stores the PaymentIntent ID. If the order status is still pending, it advances to processing. |
payment_intent.payment_failed |
Marks payment as failed | Updates payment_status to failed. The order is not deleted — it remains in the database for reference. |
Both events use the order_id stored in the PaymentIntent's metadata to look up the corresponding BoxCart order. If the order_id is missing from the metadata or the order cannot be found, the event is logged and skipped.
public function handle_webhook_event( $event ) {
$type = isset( $event['type'] ) ? $event['type'] : '';
$data = isset( $event['data']['object'] ) ? $event['data']['object'] : array();
switch ( $type ) {
case 'payment_intent.succeeded':
return $this->handle_payment_succeeded( $data );
case 'payment_intent.payment_failed':
return $this->handle_payment_failed( $data );
default:
// Unhandled event type — acknowledge receipt.
return true;
}
}
Setting Up Webhooks
Follow these steps to configure Stripe webhooks for your BoxCart store.
-
Open the Stripe Dashboard
Log in to your Stripe Dashboard and navigate to Developers → Webhooks.
-
Add a new endpoint
Click Add endpoint. In the Endpoint URL field, enter your BoxCart webhook URL:
https://yoursite.com/wp-json/boxcart/v1/webhook/stripeReplace
yoursite.comwith your actual domain. -
Select events to listen for
Click Select events and choose the following two events:
payment_intent.succeededpayment_intent.payment_failed
Click Add events, then Add endpoint to save.
-
Copy the signing secret
After creating the endpoint, Stripe will display a Signing secret that begins with
whsec_. Click to reveal and copy this value. -
Paste the secret into BoxCart
In your WordPress admin, go to BoxCart → Settings → Payments. Paste the signing secret into the Webhook Secret field and save your settings.
If you are using Stripe in test mode, make sure you create the webhook endpoint in the Stripe test mode dashboard and use the corresponding test signing secret. When you switch to live mode, you will need to create a separate live webhook endpoint and update the secret in BoxCart accordingly.
Request Format
Stripe sends webhook events as POST requests with a JSON body. The request includes a Stripe-Signature header used for verification.
POST /wp-json/boxcart/v1/webhook/stripe HTTP/1.1
Host: yoursite.com
Content-Type: application/json
Stripe-Signature: t=1681000000,v1=abc123def456...
{
"id": "evt_1abc123",
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_1xyz789",
"status": "succeeded",
"amount": 2500,
"currency": "gbp",
"metadata": {
"order_id": "42"
}
}
}
}
BoxCart reads the raw request body via $request->get_body() and retrieves the signature header via $request->get_header('stripe-signature'). The JSON payload is decoded only after signature verification passes.
Response Codes
The webhook endpoint returns the following HTTP status codes.
| Status Code | Meaning | When Returned |
|---|---|---|
200 OK |
Event received and processed | The signature is valid and the event has been handled (or acknowledged for unhandled event types). Response body: {"received": true} |
400 Bad Request |
Invalid payload or signature | The Stripe-Signature header is missing, the signature verification failed, or the JSON payload could not be parsed. Response body: {"error": "..."} |
Stripe considers any 2xx response as successful delivery. If your endpoint returns a non-2xx response, Stripe will retry the delivery with exponential backoff for up to 3 days.
BoxCart always returns 200 for successfully verified events, even if the specific event type is not handled. This prevents unnecessary retries from Stripe for event types that BoxCart does not process.
Testing Webhooks
There are two primary ways to test webhook delivery during development.
Using the Stripe CLI
The Stripe CLI lets you forward webhook events to your local development environment without needing a publicly accessible URL.
stripe listen --forward-to yoursite.com/wp-json/boxcart/v1/webhook/stripe
The CLI will output a webhook signing secret (starting with whsec_). Copy this temporary secret and paste it into your BoxCart webhook secret setting for local testing.
You can then trigger test events from another terminal:
stripe trigger payment_intent.succeeded
Using the Stripe Dashboard
If your site is publicly accessible (e.g. a staging server), you can send test events directly from the Stripe Dashboard:
- Go to Developers → Webhooks in the Stripe Dashboard.
- Click on your webhook endpoint.
- Click Send test webhook.
- Select an event type (e.g.
payment_intent.succeeded) and click Send test webhook.
Check the endpoint's Recent deliveries section to see the request and response details, including status codes and any error messages.
Test webhook events from Stripe will not contain a real order_id in the metadata. The event will be acknowledged with a 200 response, but no order will be updated. To test the full flow end-to-end, place a test order using Stripe test card numbers.