Orders
Lifecycle, statuses, and the refund flow that actually calls the payment provider.
An order is created from a cart at checkout. From that point on it lives independently — variants can be deleted, prices can change, and the order remembers what was sold and for how much via a JSON snapshot of every line.
Statuses
pending → paid → fulfilled → delivered
↓
refunded- pending — order exists, payment intent issued, money not received. Stock is reserved.
- paid — payment provider confirmed (webhook or admin override). Inventory moves from reserved to consumed.
- fulfilled — shipping label generated / pickup ready.
- delivered — terminal happy state.
- canceled — terminal failure state. Reservations released.
- refunded — payment reversed at the provider. See below.
Endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /stores/{id}/orders | List, paginated. |
GET | /stores/{id}/orders/{order_id} | Single order with lines + payment + shipping. |
PATCH | /stores/{id}/orders/{order_id} | Update status (e.g. mark fulfilled). |
POST | /stores/{id}/orders/{order_id}/refund | Issue a refund through the payment provider. |
The refund flow
The Reembolsar button in the admin (and POST .../refund in the
API) doesn't just flip a database column — it actually calls the
payment provider. Today that's Mercado Pago. The handler:
- Loads the latest approved payment intent for this order.
- Calls Mercado Pago's
/v1/payments/{id}/refundswith an idempotency key. Empty body = full refund;{"amount": 12.34}= partial. - Only on a successful provider response does it mark the order
refunded, release inventory reservations, kill any pending build jobs, and fire theorder_refundedworkflow event.
If the provider call fails (network, declined, already refunded), the local state is left alone and a 5xx propagates back. Retrying is safe because the idempotency key is regenerated on every call — partial refunds will accumulate, not duplicate.
# Full refund:
curl -X POST \
"https://bp.mobicms.com.br/stores/$STORE_ID/orders/$ORDER_ID/refund" \
-H "Authorization: Bearer $BP_TOKEN" \
-d '{}'
# Partial refund of R$ 30,00:
curl -X POST \
"https://bp.mobicms.com.br/stores/$STORE_ID/orders/$ORDER_ID/refund" \
-H "Authorization: Bearer $BP_TOKEN" \
-d '{"amount_cents": 3000}'Pickup orders
If the buyer chose Retirar pedido na loja at checkout instead of
shipping, the order carries a pickup_location_snapshot_json and the
freight section is hidden. The snapshot is intentionally a copy of the
location at the time of purchase — you can later edit or delete the
physical location without changing the order's pickup address.
fulfillment_method on the order is either "shipping" or "pickup"
and tells the admin UI which card to render.
Permission cheatsheet
| Action | Permission bit |
|---|---|
| List, get | orders.read |
| Create (admin-created) | orders.write |
| Update status, fulfill | orders.edit / orders.fulfill |
| Cancel | orders.cancel |
| Refund | orders.refund |