Stripe Payments
Base path: /api/v1/stripe
How Stripe Is Used in Booki
Stripe handles two types of payments:
| Flow | Who | Endpoint |
|---|---|---|
| Booking payment | Customer paying for a booking | POST /stripe/bookings/:id/payment-intent |
| Subscription payment | Owner registering and subscribing | POST /stripe/create-subscription-intent |
The frontend uses the returned clientSecret with Stripe.js to complete payment on the client side, then calls the verify endpoint to confirm.
POST /api/v1/stripe/bookings/:id/payment-intent — Create Booking Payment Intent
Auth: Protected (Authorization: Bearer <accessToken>)
Used by: customer-booki-web-app · Role: customer
organizationId: From JWT token
Description: Create a Stripe PaymentIntent for a specific booking. Returns a clientSecret used by Stripe.js on the frontend to collect and confirm the payment.
Request
POST /api/v1/stripe/bookings/507f1f77bcf86cd799439041/payment-intent
No request body required. The booking ID is taken from the path parameter.
Response (201 Created)
{
"clientSecret": "pi_3NxABC2eZvKYlo2C1ABC1234_secret_...",
"paymentIntentId": "pi_3NxABC2eZvKYlo2C1ABC1234"
}
Error Responses
401 Unauthorized — No valid token:
{
"statusCode": 401,
"message": "Access token is required to proceed."
}
404 Not Found — Booking not found:
{
"statusCode": 404,
"message": "Booking not found."
}
cURL Example
curl -X POST "http://localhost:4001/api/v1/stripe/bookings/507f1f77bcf86cd799439041/payment-intent" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGc..."
POST /api/v1/stripe/bookings/:id/verify-payment — Verify Booking Payment
Auth: Protected (Authorization: Bearer <accessToken>)
Used by: customer-booki-web-app · Role: customer
organizationId: From JWT token
Description: After the customer completes payment via Stripe.js, call this endpoint to confirm the payment succeeded and update the booking status.
Request
{
"paymentIntentId": "pi_3NxABC2eZvKYlo2C1ABC1234"
}
Fields:
paymentIntentId(string, required): The PaymentIntent ID returned from the payment intent step
Response (200 OK)
{
"message": "Payment verified successfully.",
"bookingId": "507f1f77bcf86cd799439041"
}
Error Responses
400 Bad Request — Payment not completed:
{
"statusCode": 400,
"message": "Payment has not been completed yet."
}
cURL Example
curl -X POST "http://localhost:4001/api/v1/stripe/bookings/507f1f77bcf86cd799439041/verify-payment" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGc..." \
-d '{"paymentIntentId": "pi_3NxABC2eZvKYlo2C1ABC1234"}'
POST /api/v1/stripe/create-subscription-intent — Create Subscription Payment Intent
Auth: Public (no authentication required)
Used by: cms-booki-web-app · Role: guest/owner (during onboarding)
Description: Create a Stripe PaymentIntent for an organization subscription. Called during owner registration before the account is created. Returns a clientSecret for Stripe.js.
Request
{
"email": "owner@example.com",
"pricePerBranch": 999,
"branchCount": 1,
"interval": "monthly",
"trialDays": 90,
"registrationData": {}
}
Fields:
email(string, required): Owner email addresspricePerBranch(number, required): Price per branch in PHP (min 0)branchCount(number, required): Number of branches (min 1)interval(string, optional, default:monthly):monthly|annuallytrialDays(number, optional, default: 90): Trial period in daysregistrationData(object, optional): Additional data to carry through the registration flow
Response (201 Created)
{
"clientSecret": "pi_3NxABC2eZvKYlo2C1ABC1234_secret_...",
"paymentIntentId": "pi_3NxABC2eZvKYlo2C1ABC1234"
}
cURL Example
curl -X POST "http://localhost:4001/api/v1/stripe/create-subscription-intent" \
-H "Content-Type: application/json" \
-d '{
"email": "owner@example.com",
"pricePerBranch": 999,
"branchCount": 1,
"interval": "monthly"
}'
POST /api/v1/stripe/verify-subscription — Verify Subscription Payment
Auth: Public (no authentication required)
Used by: cms-booki-web-app · Role: guest/owner (during onboarding)
Description: After the owner completes subscription payment via Stripe.js, call this to confirm the payment and finalize the subscription setup.
Request
{
"paymentIntentId": "pi_3NxABC2eZvKYlo2C1ABC1234"
}
Fields:
paymentIntentId(string, required): The PaymentIntent ID from the subscription intent step
Response (200 OK)
{
"message": "Subscription payment verified successfully."
}
cURL Example
curl -X POST "http://localhost:4001/api/v1/stripe/verify-subscription" \
-H "Content-Type: application/json" \
-d '{"paymentIntentId": "pi_3NxABC2eZvKYlo2C1ABC1234"}'
POST /api/v1/stripe/webhook — Stripe Webhook Receiver
Auth: Public (Stripe signature verification)
Used by: Stripe payment gateway (server-to-server)
Description: Receives webhook events from Stripe (e.g., payment_intent.succeeded, payment_intent.payment_failed). This endpoint is called by Stripe's servers, not by the frontend.
Important: Stripe sends raw request bodies. This route is registered before
express.json()inapp.tsso the raw body is preserved for signature verification.
Request
POST /api/v1/stripe/webhook
Headers:
stripe-signature: t=1700000000,v1=abc123...
{
"id": "evt_3NxABC2eZvKYlo2C0001",
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_3NxABC2eZvKYlo2C1ABC1234",
"amount": 45000,
"status": "succeeded"
}
}
}
Response (200 OK)
{
"received": true
}
Note: This endpoint is for Stripe's internal use. Do not call it from the frontend.
