Cart Service (Medusa Store API)
core/services/cart_service.dart
Overview
Dio-backed client for every cart-shaped endpoint Medusa exposes: create cart (physical vs provision), get/update cart, add/update line items, shipping options + methods, promo codes, payment collections (full and partial), and complete cart.
User Flow
- createPhysicalCart / createProvisionCart POST /store/carts with metadata.type and an optional region_id; the metadata is what lets the UI keep marketplace and booking carts separate.
- addItemToCart / updateCartItem mutate line items (qty=0 deletes server-side).
- applyPromoCode / removePromoCode use POST … /promotions with the new full list (Medusa-style overwrite semantics).
- fetchShippingOptions + addShippingMethod attach a delivery option.
- fetchPaymentProviders + createPaymentCollection + createPaymentSession build the Stripe path.
- createPartialPaymentSession splits the amount between Stripe (deposit) and 'pp_system_default' (pay-on-site rest) for booking deposits.
- completeCart converts the cart to an order_set after a successful payment.
Cases & Edge Cases
- create*Cart auto-retries without region_id on 400/422 — backend rejects unknown 'region_id' for some merchant configs; the retry lets the cart still be created.
- Booking-question helpers (getCartItemQuestions / answer / remove) treat 404 as 'no questions configured' rather than an error.
- completeCart accepts three possible shapes (order_set, data, {type:'order'}) because the Medusa response varies by version — the priority order matters.
- removePromoCode currently sends an empty promo_codes list rather than the documented DELETE endpoint; backend behavior has changed across Medusa V2 minors.
Code References
- lib/src/core/services/cart_service.dart:7 // CartService
- lib/src/core/services/cart_service.dart:11 // createProvisionCart (+retry)
- lib/src/core/services/cart_service.dart:185 // addItemToCart
- lib/src/core/services/cart_service.dart:378 // applyPromoCode
- lib/src/core/services/cart_service.dart:612 // createPartialPaymentSession
- lib/src/core/services/cart_service.dart:663 // completeCart
API Calls
- POST
/store/cartsno statusCreate a fresh Medusa cart of a given type (provision or physical).
Request{ metadata: { type: 'provision' | 'physical' }, region_id?: string }Response{ cart: { id: string } }lib/src/features/shared/checkout/views/checkout_screen.dart // initCheckoutauth: customer. Called once on checkout entry to promote the local cart to a server cart.
- GET
/store/carts/:cartIdno statusFetch the current server-side cart (lines, totals, addresses, promotions).
Response{ cart: Cart }lib/src/features/shared/cart/views/cart_screen.dart + checkout_screen.dartauth: customer.
- POST
/store/carts/:cartId/line-itemsno statusAdd a line item to the cart.
Request{ variant_id: string, quantity: number }Response{ cart: Cart }lib/src/features/shared/product_details/views/product_details_screen.dart // add-to-cartauth: customer.
- POST
/store/carts/:cartId/line-items/:lineItemIdno statusUpdate or delete (quantity=0) a line item.
Request{ quantity: number /* 0 deletes */ }Response{ cart: Cart }lib/src/features/shared/cart/views/cart_screen.dartauth: customer.
- POST
/store/carts/:cartIdno statusPatch arbitrary cart fields (shipping address, email, region…).
RequestCaller-provided map, e.g. { shipping_address, email }Response{ cart: Cart }lib/src/features/shared/checkout/views/checkout_screen.dartauth: customer.
- GET
/store/shipping-options?cart_id=:idno statusList shipping options available for the cart's destination + items.
Response{ shipping_options: ShippingOption[] }lib/src/features/shared/checkout/views/widgets/checkout_shipping_options.dartauth: customer.
- POST
/store/carts/:cartId/shipping-methodsno statusAttach a chosen shipping method to the cart.
Request{ option_id: string }Response{ cart: Cart }lib/src/features/shared/checkout/views/widgets/checkout_shipping_options.dartauth: customer.
- POST
/store/carts/:cartId/promotionsno statusApply or clear promo codes on the cart.
Request{ promo_codes: string[] /* empty array clears */ }Response{ cart: Cart }lib/src/features/shared/cart/views/cart_screen.dartauth: customer. Removal currently uses empty-array semantics, not the documented DELETE endpoint.
- GET
/store/payment-providers?region_id=:idno statusList payment providers configured for the cart's region.
Response{ payment_providers: PaymentProvider[] }lib/src/features/shared/checkout/views/widgets/checkout_payment_methods.dartauth: customer.
- POST
/store/payment-collectionsno statusCreate a payment collection for the cart.
Request{ cart_id: string }Response{ payment_collection: { id: string, amount: number } }lib/src/features/shared/checkout/views/checkout_screen.dartauth: customer.
- POST
/store/payment-collections/:idno statusResync the amount on an existing payment collection (e.g. after cart change).
Request{ amount: number }Response{ payment_collection: PaymentCollection }lib/src/features/shared/checkout/views/checkout_screen.dartauth: customer.
- POST
/store/payment-collections/:id/payment-sessionsno statusInitialize a payment session with a chosen provider; returns the Stripe client_secret.
Request{ provider_id: string }Response{ payment_collection: { payment_sessions: [{ provider_id, data: { client_secret } }] } }lib/src/features/shared/checkout/views/checkout_screen.dartauth: customer.
- POST
/store/payment-collections/:id/partial/payment-sessionno statusMulti-session partial payment for booking deposits (e.g. deposit + remainder).
Request{ providers: [{ provider_id: 'pp_partial_stripe-connect' | 'pp_system_default', amount: number }] }Response{ payment_collection: PaymentCollection }lib/src/features/shared/checkout/views/checkout_screen.dart // booking depositsauth: customer. Used only on bookings that allow a partial-payment policy.
- POST
/store/carts/:cartId/set-orderno statusComplete the cart and convert it into an order.
Response{ order_set?: OrderSet, data?: any, order?: Order } /* shape varies by Medusa version */lib/src/features/shared/checkout/views/checkout_screen.dart // Pay → completeCartauth: customer. Three response shapes accepted (order_set → data → order) — priority order matters.
- GET
/store/carts/:cartId/line-items/:lineItemId/questionsno statusFetch booking-questions for a provision line item.
Response{ questions: BookingQuestion[] }Errors- ·404 — no questions defined for this line item
lib/src/features/shared/cart/views/cart_screen.dart, checkout_booking_questions.dartauth: customer.
- POST
/store/carts/:cartId/line-items/:lineItemId/questions/:questionIdno statusPersist a customer's answer to a booking question (option / comment / number / image).
Request{ selected_option_ids?: string[], comment?: string, number?: number, image_file_id?: string }Response200 / 201 (no body)
lib/src/features/shared/checkout/views/widgets/checkout_booking_questions.dartauth: customer.
- DELETE
/store/carts/:cartId/line-items/:lineItemId/questions/:questionIdno statusClear an answer to a booking question.
Response200 (no body)
lib/src/features/shared/checkout/views/widgets/checkout_booking_questions.dartauth: customer.
Notes
Every method opts in to authScope: 'customer' via RequestTrace; never call this with a seller-only token attached.