Shop It Docs
Developer Resourcescart

Cart Module API & Integration Guide

API contracts for customer/mobile cart operations, manual delivery address flow, quantity operations, and admin cart read.

Cart Module - API & Integration Guide

1. Base Routes

  • Customer API: /api/cart
  • Mobile-composed customer API: /api/mobile/cart
  • Admin API: /api/admin/cart

All successful responses use ResponseDto<T>.

2. Customer/Mobile Endpoints

MethodPathAuthRequestResponse
GET/api/cartJWTnoneCartResponseDto
GET/api/mobile/cartJWTnoneCartResponseDto
POST/api/cart/itemsJWTAddToCartDtoCartResponseDto
POST/api/mobile/cart/itemsJWTAddToCartDtoCartResponseDto
POST/api/cart/syncJWTSyncCartDtoCartResponseDto
POST/api/mobile/cart/syncJWTSyncCartDtoCartResponseDto
DELETE/api/cart/items/:itemIdJWTpath itemIdCartResponseDto
DELETE/api/mobile/cart/items/:itemIdJWTpath itemIdCartResponseDto
POST/api/cart/items/:itemId/increaseJWTpath itemIdCartResponseDto
POST/api/mobile/cart/items/:itemId/increaseJWTpath itemIdCartResponseDto
POST/api/cart/items/:itemId/decreaseJWTpath itemIdCartResponseDto
POST/api/mobile/cart/items/:itemId/decreaseJWTpath itemIdCartResponseDto
PUT/api/cart/items/:itemId/quantityJWTUpdateCartItemDtoCartResponseDto
PUT/api/mobile/cart/items/:itemId/quantityJWTUpdateCartItemDtoCartResponseDto
GET/api/cart/locationJWTnoneCartLocationResponseDto
GET/api/mobile/cart/locationJWTnoneCartLocationResponseDto
GET/api/cart/location/districtsPublicnoneCartShippingDistrictResponseDto[]
GET/api/mobile/cart/location/districtsPublicnoneCartShippingDistrictResponseDto[]
POST/api/cart/guest/quotePublicGuestCartQuoteDtoGuestCartQuoteResponseDto
POST/api/mobile/cart/guest/quotePublicGuestCartQuoteDtoGuestCartQuoteResponseDto
POST/api/cart/locationJWTSetCartLocationDtoCartLocationResponseDto
POST/api/mobile/cart/locationJWTSetCartLocationDtoCartLocationResponseDto
POST/api/cart/location/autoJWTGeocodeLocationDtoDisabled by default (410)
POST/api/mobile/cart/location/autoJWTGeocodeLocationDtoDisabled by default (410)
DELETE/api/cart/locationJWTnoneCartClearResponseDto
DELETE/api/mobile/cart/locationJWTnoneCartClearResponseDto
DELETE/api/cartJWTnoneCartClearResponseDto
DELETE/api/mobile/cartJWTnoneCartClearResponseDto

3. Admin Endpoint

MethodPathAuthPermissionResponse
GET/api/admin/cart/users/:userIdJWT + RBACCart_READCartAdminUserCartResponseDto | null

4. Care-Package Cart Endpoints

These endpoints are mounted under /api/cart/items/:cartItemId/care-package (and /api/mobile/cart/items/:cartItemId/care-package via MobileModule routing).

The direct add/remove endpoints are authenticated only. Guest care-package pricing is supported through POST /api/mobile/cart/guest/quote; persistence happens after login through POST /api/mobile/cart/sync.

MethodPathAuthRequestResponse
POST/api/cart/items/:cartItemId/care-packageJWTAddCarePackageToCartDtoCartCarePackageItemResponseDto
POST/api/mobile/cart/items/:cartItemId/care-packageJWTAddCarePackageToCartDtoCartCarePackageItemResponseDto
DELETE/api/cart/items/:cartItemId/care-packageJWTpath cartItemIdRemoveCarePackageFromCartResponseDto
DELETE/api/mobile/cart/items/:cartItemId/care-packageJWTpath cartItemIdRemoveCarePackageFromCartResponseDto

Add request DTO:

POST /api/mobile/cart/items/:cartItemId/care-package
{
  "carePackageId": 1,
  "pricingTierId": 3
}

Response note: DELETE returns HTTP 200 (not 204) with a RemoveCarePackageFromCartResponseDto confirming the removed item details.

5. Request DTOs

4.1 Add item

{
  "productId": 101,
  "quantity": 2
}
  • quantity is optional, defaults to 1.
  • allowed range: 1..99.

4.2 Set quantity

{
  "operation": "set",
  "quantity": 3
}
  • for this endpoint, operation must be set.

4.3 Sync cart (replace active cart, no merge)

{
  "items": [
    {
      "productId": 101,
      "quantity": 2,
      "carePackage": {
        "carePackageId": 2,
        "pricingTierId": 3
      }
    },
    { "productId": 202, "quantity": 1 }
  ]
}

Rules:

  • If items is empty, backend returns current authenticated cart unchanged.
  • If items has data, backend replaces existing active cart content with this payload.
  • Existing active cart is not merged; it is replaced.
  • Optional carePackage persists one care package add-on for that synced product line.
  • Duplicate entries for the same productId must carry the same care package selection.

4.4 Set location (manual canonical)

{
  "districtName": "Kathmandu",
  "addressLine1": "Boudha Stupa Road",
  "addressLine2": "House 12, Floor 2",
  "city": "Kathmandu",
  "state": "Bagmati",
  "postalCode": "44600",
  "landmark": "Near the main gate",
  "mobileNum": "+977-9800000000",
  "instructions": "Leave at reception if not available.",
  "latitude": 27.7172,
  "longitude": 85.324
}

Required fields:

  • districtName
  • addressLine1
  • city
  • postalCode

Optional fields:

  • addressLine2
  • state
  • landmark
  • mobileNum
  • instructions
  • latitude
  • longitude

4.5 Guest quote (public, read-only)

{
  "districtName": "Kathmandu",
  "items": [
    {
      "productId": 101,
      "quantity": 2,
      "carePackage": {
        "carePackageId": 2,
        "pricingTierId": 3
      }
    },
    { "productId": 202, "quantity": 1 }
  ]
}

Rules:

  • backend recalculates product and care package prices from records; client price inputs are ignored.
  • optional carePackage is allowed only for physical products when the selected care package is active, the pricing tier is active, and the package eligibility rules match the product.
  • care package price is counted once per selected product line, not multiplied by product quantity.
  • max 50 unique products per quote request.
  • per-item quantity must be within 1..99.
  • no cart write, no order creation, no stock reservation.

5. Cart Response Contract

{
  "id": 1,
  "status": "active",
  "items": [
    {
      "id": 10,
      "productId": 101,
      "productTitle": "Dell XPS 15 Laptop",
      "productSlug": "dell-xps-15-laptop",
      "productThumbnail": "https://cdn.example.com/thumb.jpg",
      "productType": "laptop",
      "unitMrp": 15000,
      "unitSp": 12000,
      "quantity": 2,
      "discount": 3000
    }
  ],
  "carePackageItems": [
    {
      "id": 1,
      "cartItemId": 10,
      "carePackageId": 2,
      "carePackageName": "Extended Warranty",
      "pricingTierId": 3,
      "durationMonths": 12,
      "unitPrice": 149900
    }
  ],
  "totalMrp": 179900,
  "totalSp": 173900,
  "totalDiscount": 6000,
  "shippingDistrict": "Kathmandu",
  "shippingMobileNum": "+977-9800000000",
  "shippingInstructions": "Leave at reception if not available.",
  "shippingFee": 50000,
  "shippingAddressLine1": "Boudha Stupa Road",
  "shippingAddressLine2": "House 12, Floor 2",
  "shippingCity": "Kathmandu",
  "shippingState": "Bagmati",
  "shippingPostalCode": "44600",
  "shippingLandmark": "Near the main gate",
  "shippingLatitude": 27.7172,
  "shippingLongitude": 85.324,
  "appliedCoupon": {
    "promotionId": 1,
    "code": "SUMMER2024",
    "discount": 1000
  },
  "promoNotice": {
    "status": "promo_removed",
    "reason": "min_cart_value_not_met",
    "message": "This promo is no longer valid for your current cart."
  },
  "finalPayable": 222900
}

Promo revalidation on cart mutations

After a coupon is successfully applied to the cart, the backend owns promo maintenance for all cart-changing operations:

  • POST /cart/items
  • POST /cart/sync
  • DELETE /cart/items/:itemId
  • POST /cart/items/:itemId/increase
  • POST /cart/items/:itemId/decrease
  • PUT /cart/items/:itemId/quantity

Flow:

  • backend applies the cart mutation first
  • backend revalidates the already-applied coupon against the updated cart
  • if still valid, backend recalculates and returns the updated cart totals
  • if no longer valid, backend removes the coupon from active pricing and returns the updated cart with promoNotice

promoNotice contract:

{
  "status": "promo_removed",
  "reason": "min_cart_value_not_met",
  "message": "This promo is no longer valid for your current cart."
}

Supported promoNotice.reason values:

  • promotion_inactive
  • promotion_expired
  • promotion_not_started
  • usage_limit_reached
  • per_user_limit_reached
  • min_cart_value_not_met
  • scope_mismatch
  • cart_type_mismatch

Frontend integration rule:

  • call only the normal cart mutation endpoint
  • render the returned CartResponseDto
  • if promoNotice exists, show its message to the user

Monetary contract (important)

  • All money fields in cart APIs are integer minor units (USD cents in current Stripe flow).
  • unitSp fallback is backend-authoritative: when unitSp <= 0, effective sell price becomes unitMrp.
  • Response item discount is per-unit:
    • discount = unitMrp - effectiveUnitSp
  • totalDiscount is quantity-aware across product lines.
  • Clients must not divide/multiply these values for API/payment calls; convert only for display formatting.

Final payable formula

For physical products:

finalPayable = totalSp - appliedDiscount + shippingFee

Where:

  • appliedDiscount = appliedCoupon.discount if coupon exists, else 0
  • shippingFee = shippingFee if location is set, else 0

Checkout safety guard

Cart mutation responses keep pricing and coupon state current, but checkout still performs final promotion validation as a safety guard.

Payment-method mismatch is still enforced at checkout/order creation, not in cart mutation promoNotice.

6. Location Response Contract

{
  "districtName": "Kathmandu",
  "shippingFee": 50000,
  "addressLine1": "Boudha Stupa Road",
  "addressLine2": "House 12, Floor 2",
  "city": "Kathmandu",
  "state": "Bagmati",
  "postalCode": "44600",
  "landmark": "Near the main gate",
  "mobileNum": "+977-9800000000",
  "instructions": "Leave at reception if not available.",
  "latitude": 27.7172,
  "longitude": 85.324,
  "isAutoDetected": false
}

GET /location returns {} when no location is set.

6.1 Guest Quote Response Contract

{
  "items": [
    {
      "productId": 101,
      "productTitle": "Dell XPS 15 Laptop",
      "productSlug": "dell-xps-15-laptop",
      "productThumbnail": "https://cdn.example.com/thumb.jpg",
      "productType": "laptop",
      "unitMrp": 15000,
      "unitSp": 12000,
      "quantity": 2,
      "discount": 3000,
      "carePackage": {
        "carePackageId": 2,
        "carePackageName": "IT Mart Care Plus",
        "pricingTierId": 3,
        "durationMonths": 12,
        "unitPrice": 149900
      }
    }
  ],
  "totalMrp": 179900,
  "totalSp": 173900,
  "totalDiscount": 6000,
  "shippingDistrict": "Kathmandu",
  "shippingFee": 180000,
  "finalPayable": 353900
}

Guest quote final payable formula:

finalPayable = product totalSp + selected care package unitPrice values + shippingFee

7. Available Shipping Districts Response Contract

GET /location/districts returns active shipping destinations:

[
  {
    "districtName": "Kathmandu",
    "shippingFee": 50000,
    "currency": "NPR"
  },
  {
    "districtName": "Pokhara",
    "shippingFee": 75000,
    "currency": "NPR"
  }
]

8. Clear Response Contract

{
  "cleared": true
}

9. Error Codes (Common)

HTTPerrorCodeMeaning
400CART_PRODUCT_NOT_ELIGIBLEProduct type cannot be added to cart
400CART_INVALID_ITEMInvalid quantity operation/payload
400CART_LOCATION_SHIPPING_NOT_AVAILABLEShipping not configured for selected destination (legacy)
400CART_LOCATION_DISTRICT_NOT_AVAILABLEShipping fee not configured for the selected district
400CART_PRODUCT_NOT_SELLABLEProduct is not currently purchasable
404CART_PRODUCT_NOT_FOUNDProduct does not exist
404CART_ITEM_NOT_FOUNDCart item does not exist
404CART_NOT_FOUNDCart not found
410CART_LOCATION_AUTO_DISABLEDAuto-locate endpoint is disabled
429RATE_LIMIT_EXCEEDEDCart operation rate limit exceeded

10. Notes for Frontend

  • Use /api/mobile/cart/* routes in mobile/web app integration.
  • Item discount is per-unit: unitMrp - unitSp; use totalDiscount for quantity-aware discount totals.
  • Always read quantity from each item; do not infer from totals.
  • For guest users, call POST /api/mobile/cart/guest/quote with optional items[].carePackage to render backend-trusted totals before login.
  • After login, send the same guest cart shape to POST /api/mobile/cart/sync so selected care packages are persisted.
  • Fetch available districts via GET /location/districts before showing district selector.
  • Use manual location flow (POST /location) as canonical.
  • Do not depend on /location/auto unless backend explicitly re-enables it.