Skip to main content

Cart Integration Guide

This guide covers how to integrate VesuvioPay's cart functionality into your e-commerce application, enabling customers to build shopping carts and proceed to checkout.

Overview​

The VesuvioPay Cart API provides:

  • Multi-store cart management
  • Add, update, and remove cart items
  • Bulk operations for better performance
  • Product availability checking
  • Back-in-stock subscription management
  • Checkout flow integration

Authentication​

Cart API endpoints support dual authentication:

  1. JWT Token (Required): Customer authentication via Bearer token
  2. API Key (Optional): Store identification via X-Api-Key header
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
X-Api-Key: your-api-key (optional)

Authentication Policies​

  • Customer with Verified Phone: Required for all cart operations
  • Public API Key: Required for availability checking without customer auth
  • Private API Key: Optional for enhanced security and store validation

Cart Lifecycle​

graph LR
A[Customer Login] --> B[Get/Create Cart]
B --> C[Add Items]
C --> D[Update Quantities]
D --> E[Remove Items]
E --> F[Finish Cart]
F --> G[Checkout & Payment]
G --> H[Order Created]

Getting Started​

Step 1: Authenticate Customer​

Before accessing the cart, authenticate your customer to get a JWT token:

const response = await fetch('https://api.vesuviopay.com/api/v1/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
phoneNumber: '+1234567890',
verificationCode: '123456'
})
});

const { token } = await response.json();

Step 2: Get Customer's Cart​

Retrieve the current cart for a specific store:

Endpoint: GET /api/v1/sdk/cart/{storeId}

const storeId = 'a3f2b1c0-1234-5678-90ab-cdef12345678';

const response = await fetch(`https://api.vesuviopay.com/api/v1/sdk/cart/${storeId}`, {
headers: {
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
}
});

const cart = await response.json();

Response:

{
"success": true,
"data": {
"storeId": "a3f2b1c0-1234-5678-90ab-cdef12345678",
"storeName": "My Store",
"items": [
{
"id": "b4e3c2d1-2345-6789-01bc-def123456789",
"productVariantId": "c5f4d3e2-3456-7890-12cd-ef1234567890",
"externalId": "shopify_12345",
"externalVariantId": "shopify_variant_001",
"productTitle": "Premium T-Shirt",
"variantTitle": "Small / Red",
"sku": "TSHIRT-S-RED",
"quantity": 2,
"originalUnitPrice": 39.99,
"unitPrice": 29.99,
"originalTotalPrice": 79.98,
"totalPrice": 59.98,
"hasDiscount": true,
"discountPercentage": 25,
"discountAmount": 19.98,
"addedAt": "2025-10-08T10:00:00Z",
"updatedAt": "2025-10-08T10:15:00Z"
}
],
"summary": {
"itemCount": 1,
"totalQuantity": 2,
"subtotal": 59.98,
"estimatedTax": 4.80,
"estimatedShipping": 5.00,
"estimatedTotal": 69.78
}
}
}

Cart Operations​

Add Item to Cart​

Endpoint: POST /api/v1/sdk/cart/store/{storeId}/item

Add a product variant to the cart. If the item already exists, the quantity will be incremented.

async function addToCart(storeId, externalProductId, externalVariantId, quantity) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/item`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
},
body: JSON.stringify({
externalProductId: externalProductId,
externalVariantId: externalVariantId,
quantity: quantity
})
}
);

return await response.json();
}

// Usage
await addToCart(
'a3f2b1c0-1234-5678-90ab-cdef12345678',
'shopify_12345',
'shopify_variant_001',
2
);

Request Body:

{
"externalProductId": "shopify_12345",
"externalVariantId": "shopify_variant_001",
"quantity": 2
}

Update Cart Item Quantity​

Endpoint: PUT /api/v1/sdk/cart/store/{storeId}/item

Update the quantity of an existing cart item.

async function updateCartItem(storeId, cartItemId, newQuantity) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/item`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
},
body: JSON.stringify({
cartItemId: cartItemId,
quantity: newQuantity
})
}
);

return await response.json();
}

// Usage
await updateCartItem(
'a3f2b1c0-1234-5678-90ab-cdef12345678',
'b4e3c2d1-2345-6789-01bc-def123456789',
5
);

Remove Item from Cart​

Endpoint: DELETE /api/v1/sdk/cart/store/{storeId}/item/{cartItemId}

async function removeFromCart(storeId, cartItemId) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/item/${cartItemId}`,
{
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
}
}
);

return await response.json();
}

Clear Entire Cart​

Endpoint: DELETE /api/v1/sdk/cart/store/{storeId}

Remove all items from the cart for a specific store.

async function clearCart(storeId) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}`,
{
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
}
}
);

return await response.json();
}

Bulk Operations​

For better performance when adding or updating multiple items, use bulk endpoints.

Bulk Add Items​

Endpoint: POST /api/v1/sdk/cart/store/{storeId}/items/bulk

async function bulkAddToCart(storeId, items) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/items/bulk`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
},
body: JSON.stringify({ items })
}
);

return await response.json();
}

// Usage
await bulkAddToCart('a3f2b1c0-1234-5678-90ab-cdef12345678', [
{
externalProductId: 'shopify_12345',
externalVariantId: 'shopify_variant_001',
quantity: 2
},
{
externalProductId: 'shopify_67890',
externalVariantId: 'shopify_variant_002',
quantity: 1
}
]);

Bulk Update Items​

Endpoint: PUT /api/v1/sdk/cart/store/{storeId}/items/bulk

async function bulkUpdateCartItems(storeId, items) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/items/bulk`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
},
body: JSON.stringify({ items })
}
);

return await response.json();
}

// Usage
await bulkUpdateCartItems('a3f2b1c0-1234-5678-90ab-cdef12345678', [
{ cartItemId: 'item-id-1', quantity: 5 },
{ cartItemId: 'item-id-2', quantity: 3 }
]);

Bulk Remove Items​

Endpoint: DELETE /api/v1/sdk/cart/store/{storeId}/items/bulk

async function bulkRemoveFromCart(storeId, cartItemIds) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/items/bulk`,
{
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
},
body: JSON.stringify({ cartItemIds })
}
);

return await response.json();
}

// Usage
await bulkRemoveFromCart('a3f2b1c0-1234-5678-90ab-cdef12345678', [
'item-id-1',
'item-id-2',
'item-id-3'
]);

Product Availability Checking​

Before adding items to cart or during checkout, check product availability.

Check Availability (API Key Only)​

Endpoint: POST /api/v1/sdk/cart/store/{storeId}/availability

Authentication: Public API Key (no customer authentication required)

async function checkAvailability(storeId, externalProductId, externalVariantId) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/availability`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'your-public-api-key'
},
body: JSON.stringify({
externalProductId,
externalVariantId
})
}
);

return await response.json();
}

// Usage
const availability = await checkAvailability(
'a3f2b1c0-1234-5678-90ab-cdef12345678',
'shopify_12345',
'shopify_variant_001'
);

Response:

{
"success": true,
"data": {
"available": true,
"inventoryQuantity": 100,
"inventoryTracking": true,
"productTitle": "Premium T-Shirt",
"variantTitle": "Small / Red",
"price": 29.99,
"compareAtPrice": 39.99
}
}

Check Availability with Subscription Status​

Endpoint: POST /api/v1/sdk/cart/store/{storeId}/availability-with-subscription

Authentication: Customer JWT + Optional API Key

This endpoint also returns whether the customer is subscribed to back-in-stock notifications.

async function checkAvailabilityWithSubscription(storeId, externalProductId, externalVariantId) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/availability-with-subscription`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
},
body: JSON.stringify({
externalProductId,
externalVariantId
})
}
);

return await response.json();
}

Response:

{
"success": true,
"data": {
"available": false,
"inventoryQuantity": 0,
"inventoryTracking": true,
"productTitle": "Premium T-Shirt",
"variantTitle": "Small / Red",
"price": 29.99,
"compareAtPrice": 39.99,
"isSubscribedToBackInStock": true
}
}

Back-in-Stock Subscriptions​

Allow customers to subscribe to notifications when out-of-stock products become available.

Subscribe to Back-in-Stock Notifications​

Endpoint: POST /api/v1/sdk/cart/store/{storeId}/back-in-stock-subscription

async function subscribeToBackInStock(storeId, externalProductId, externalVariantId) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/back-in-stock-subscription`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
},
body: JSON.stringify({
externalProductId,
externalVariantId
})
}
);

return await response.json();
}

Unsubscribe from Back-in-Stock Notifications​

Endpoint: DELETE /api/v1/sdk/cart/store/{storeId}/back-in-stock-subscription

async function unsubscribeFromBackInStock(storeId, externalProductId, externalVariantId) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/back-in-stock-subscription`,
{
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
},
body: JSON.stringify({
externalProductId,
externalVariantId
})
}
);

return await response.json();
}

Checkout Flow​

Endpoint: POST /api/v1/sdk/cart/store/{storeId}/finish

This endpoint completes the cart flow and sends the customer a checkout link via SMS.

async function finishCart(storeId) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/sdk/cart/store/${storeId}/finish`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'X-Api-Key': 'your-api-key' // optional
}
}
);

return await response.json();
}

// Usage
const result = await finishCart('a3f2b1c0-1234-5678-90ab-cdef12345678');
console.log('Checkout URL:', result.data); // Returns the checkout URL

Response:

{
"success": true,
"data": "https://checkout.vesuviopay.com/c/abc123xyz"
}

The customer will receive an SMS with the checkout link and can complete the purchase on VesuvioPay's secure checkout page.

Complete Cart Integration Example​

Here's a complete React example with cart state management:

import { useState, useEffect } from 'react';

const VESUVIO_API_BASE = 'https://api.vesuviopay.com/api/v1';
const STORE_ID = 'a3f2b1c0-1234-5678-90ab-cdef12345678';
const API_KEY = process.env.VESUVIO_API_KEY;

class VesuvioCartService {
constructor(token, apiKey) {
this.token = token;
this.apiKey = apiKey;
}

async getCart(storeId) {
const response = await fetch(`${VESUVIO_API_BASE}/sdk/cart/${storeId}`, {
headers: {
'Authorization': `Bearer ${this.token}`,
'X-Api-Key': this.apiKey
}
});

if (!response.ok) throw new Error('Failed to fetch cart');
return await response.json();
}

async addItem(storeId, externalProductId, externalVariantId, quantity) {
const response = await fetch(`${VESUVIO_API_BASE}/sdk/cart/store/${storeId}/item`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`,
'X-Api-Key': this.apiKey
},
body: JSON.stringify({
externalProductId,
externalVariantId,
quantity
})
});

if (!response.ok) throw new Error('Failed to add item');
return await response.json();
}

async updateItem(storeId, cartItemId, quantity) {
const response = await fetch(`${VESUVIO_API_BASE}/sdk/cart/store/${storeId}/item`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`,
'X-Api-Key': this.apiKey
},
body: JSON.stringify({ cartItemId, quantity })
});

if (!response.ok) throw new Error('Failed to update item');
return await response.json();
}

async removeItem(storeId, cartItemId) {
const response = await fetch(
`${VESUVIO_API_BASE}/sdk/cart/store/${storeId}/item/${cartItemId}`,
{
method: 'DELETE',
headers: {
'Authorization': `Bearer ${this.token}`,
'X-Api-Key': this.apiKey
}
}
);

if (!response.ok) throw new Error('Failed to remove item');
return await response.json();
}

async finishCart(storeId) {
const response = await fetch(`${VESUVIO_API_BASE}/sdk/cart/store/${storeId}/finish`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`,
'X-Api-Key': this.apiKey
}
});

if (!response.ok) throw new Error('Failed to finish cart');
return await response.json();
}
}

// React Hook for Cart Management
function useVesuvioCart(token) {
const [cart, setCart] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

const cartService = new VesuvioCartService(token, API_KEY);

const loadCart = async () => {
setLoading(true);
try {
const result = await cartService.getCart(STORE_ID);
setCart(result.data);
setError(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

const addToCart = async (externalProductId, externalVariantId, quantity = 1) => {
setLoading(true);
try {
await cartService.addItem(STORE_ID, externalProductId, externalVariantId, quantity);
await loadCart(); // Refresh cart
setError(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

const updateQuantity = async (cartItemId, quantity) => {
setLoading(true);
try {
await cartService.updateItem(STORE_ID, cartItemId, quantity);
await loadCart(); // Refresh cart
setError(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

const removeItem = async (cartItemId) => {
setLoading(true);
try {
await cartService.removeItem(STORE_ID, cartItemId);
await loadCart(); // Refresh cart
setError(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

const checkout = async () => {
setLoading(true);
try {
const result = await cartService.finishCart(STORE_ID);
// Redirect to checkout URL
window.location.href = result.data;
} catch (err) {
setError(err.message);
setLoading(false);
}
};

useEffect(() => {
if (token) loadCart();
}, [token]);

return {
cart,
loading,
error,
addToCart,
updateQuantity,
removeItem,
checkout,
refresh: loadCart
};
}

// Cart Component
function CartPage({ token }) {
const { cart, loading, addToCart, updateQuantity, removeItem, checkout } = useVesuvioCart(token);

if (loading && !cart) return <div>Loading cart...</div>;
if (!cart) return <div>Your cart is empty</div>;

return (
<div className="cart-page">
<h1>Shopping Cart</h1>

<div className="cart-items">
{cart.items.map(item => (
<div key={item.id} className="cart-item">
<h3>{item.productTitle}</h3>
{item.variantTitle && <p>{item.variantTitle}</p>}

<div className="quantity-controls">
<button onClick={() => updateQuantity(item.id, item.quantity - 1)}>-</button>
<span>{item.quantity}</span>
<button onClick={() => updateQuantity(item.id, item.quantity + 1)}>+</button>
</div>

<div className="price">
{item.hasDiscount && (
<span className="original-price">${item.originalUnitPrice}</span>
)}
<span className="current-price">${item.unitPrice}</span>
</div>

<button onClick={() => removeItem(item.id)}>Remove</button>
</div>
))}
</div>

<div className="cart-summary">
<p>Subtotal: ${cart.summary.subtotal.toFixed(2)}</p>
<p>Tax (estimated): ${cart.summary.estimatedTax.toFixed(2)}</p>
<p>Shipping (estimated): ${cart.summary.estimatedShipping.toFixed(2)}</p>
<h2>Total: ${cart.summary.estimatedTotal.toFixed(2)}</h2>

<button onClick={checkout} disabled={loading}>
Proceed to Checkout
</button>
</div>
</div>
);
}

Error Handling​

Common Error Responses​

400 Bad Request - Invalid Item​

{
"success": false,
"message": "Product variant not found or unavailable",
"errorCode": "PRODUCT_NOT_FOUND"
}

400 Bad Request - Insufficient Inventory​

{
"success": false,
"message": "Insufficient inventory. Only 5 items available",
"errorCode": "INSUFFICIENT_INVENTORY"
}

401 Unauthorized​

{
"success": false,
"message": "Unauthorized. Valid JWT token required"
}

403 Forbidden - API Key Mismatch​

{
"success": false,
"message": "API key does not have access to this store",
"errorCode": "NO_STORE_ACCESS"
}

Best Practices​

1. Handle Inventory Validation​

Always check availability before adding items:

const availability = await checkAvailability(storeId, productId, variantId);
if (availability.data.available) {
await addToCart(storeId, productId, variantId, quantity);
} else {
alert('Product is out of stock');
}

2. Use Bulk Operations​

For better performance when modifying multiple items:

// Instead of multiple individual calls
for (const item of items) {
await addToCart(storeId, item.productId, item.variantId, item.quantity);
}

// Use bulk operation
await bulkAddToCart(storeId, items);

3. Implement Optimistic Updates​

Update UI immediately, then sync with server:

function optimisticUpdateQuantity(cartItemId, newQuantity) {
// Update local state immediately
setCart(prev => ({
...prev,
items: prev.items.map(item =>
item.id === cartItemId ? { ...item, quantity: newQuantity } : item
)
}));

// Then sync with server
updateQuantity(cartItemId, newQuantity).catch(() => {
// Revert on error
loadCart();
});
}

4. Monitor Cart State​

Listen for cart-related webhook events to keep cart in sync:

  • cart.item_added
  • cart.item_updated
  • cart.item_removed
  • cart.cleared

See Webhook Integration Guide for details.

Next Steps​