Skip to content

Payments

LaraCommerce uses Stripe as its payment processor. The flow uses Payment Intents with full support for 3D Secure (SCA) authentication.

All payment endpoints require authentication.


Payment Flow

1. Place order   → POST /orders          → order_number
2. Create intent → POST /payments/intent → stripe_client_secret
3. Confirm       → Stripe.js / SDK       → payment confirmed on client
4. Verify        → POST /payments/confirm → server verifies status
5. Webhook       → Stripe notifies API  → order marked paid

Create Payment Intent

http
POST /api/v1/payments/intent

Creates a Stripe PaymentIntent for a pending order. Returns the stripe_client_secret needed by the frontend to complete payment.

Request:

json
{
  "order_number": "ORD-20260305-0001",
  "payment_method_id": "pm_1234abcd"
}
FieldRequiredDescription
order_numberYesOrder to pay for (must have payment_status: pending)
payment_method_idNoStripe Payment Method ID from a saved card. Leave empty to let Stripe.js collect card details

Response 200:

json
{
  "status": "success",
  "data": {
    "id": 1,
    "order_number": "ORD-20260305-0001",
    "amount": "141.97",
    "currency": "EUR",
    "status": "pending",
    "stripe_payment_intent_id": "pi_3Oxxxxxx",
    "stripe_client_secret": "pi_3Oxxxxxx_secret_xxxxxxx",
    "requires_action": false
  }
}

3D Secure

If requires_action: true, the customer must complete 3DS verification in the browser/app. Use the stripe_client_secret with stripe.handleNextAction() or stripe.confirmCardPayment().


Confirm Payment

http
POST /api/v1/payments/confirm

Verifies payment status on the server after the client completes the Stripe flow.

Request:

json
{
  "payment_intent_id": "pi_3Oxxxxxx"
}

Response 200 — Success:

json
{
  "status": "success",
  "message": "Payment confirmed successfully.",
  "data": { ...payment details... }
}

Response 200 — Failed:

json
{
  "status": "success",
  "message": "Payment failed.",
  "data": { "status": "failed", ... }
}

Webhooks

Payment confirmation also happens automatically via Stripe webhooks (payment_intent.succeeded). The confirm endpoint is a secondary check for the frontend.


Retry a Failed Payment

http
POST /api/v1/payments/retry

When a payment fails (card declined, 3DS timeout, etc.), use this endpoint to try again with the same or a different card. The old failed PaymentIntent is cancelled and a new one is created.

Request:

json
{
  "order_number": "ORD-20260305-0001",
  "payment_method_id": "pm_newcard123"
}

The order must have payment_status: failed to use this endpoint.


Saved Payment Methods

List Saved Cards

http
GET /api/v1/user/payment-methods

Returns the user's saved Stripe cards, ordered by default first.

Response 200:

json
{
  "status": "success",
  "data": [
    {
      "id": 1,
      "brand": "visa",
      "last_four": "4242",
      "exp_month": 12,
      "exp_year": 2027,
      "cardholder_name": "John Doe",
      "is_default": true
    }
  ]
}

Save a Card

http
POST /api/v1/user/payment-methods

Saves a Stripe Payment Method to the user's account. The card is attached to the user's Stripe Customer profile for future one-click payments.

Request:

json
{
  "stripe_payment_method_id": "pm_1234abcd",
  "cardholder_name": "John Doe",
  "set_default": true
}

How to get a stripe_payment_method_id: Use stripe.createPaymentMethod() in your frontend before calling this endpoint.

Delete a Card

http
DELETE /api/v1/user/payment-methods/{id}

Removes the card locally and detaches it from Stripe. If it was the default card, the next most recent card becomes the default automatically.

Response 204 — No content.


Stripe Webhooks

http
POST /api/v1/webhooks/stripe

Stripe sends events to this endpoint automatically. Supported events:

EventAction
payment_intent.succeededMarks order as paid, confirms stock deduction
payment_intent.payment_failedMarks payment as failed
payment_intent.requires_actionSends 3DS notification to customer
charge.refundedUpdates refund status
charge.dispute.createdCreates dispute record
refund.updatedSyncs refund status

Webhook Security

Webhook requests are verified using the Stripe signature header (STRIPE_WEBHOOK_SECRET). Invalid signatures return 400. Events are deduplicated using a 48-hour cache to prevent double processing.


Refunds

Refund requests are handled by the admin panel. Customers can request a return/refund via:

http
POST /api/v1/returns

See the Returns & Refunds section for details.

Licensed for single or extended use.