Shop Integration
This guide explains how to integrate the Stripe-powered shopping cart into your website.
Overview
The shop integration provides:
- Client-side cart — localStorage-based cart with add/remove/update functionality
- Floating cart icon — Automatically injected, shows item count badge
- Stripe Checkout — Secure payment processing via connected Stripe account
- Server-side validation — Product prices are verified by crawling your built website
Prerequisites
- Website owner must connect Stripe — See Connecting Stripe
- Include the cart script — Add cart.js to your pages
Quick Start
1. Include the Cart Script
Add this to your pages:
<script src="https://cdn.r2ware.dev/dash/static/v0.0.5/shop.min.js"></script>
This script:
- Creates a floating cart icon in the bottom-right corner
- Handles "Add to Cart" button clicks
- Manages localStorage cart state
- Shows toast notifications
2. Add "Add to Cart" Buttons
Add buttons with the required data attributes:
<button
data-add-to-cart
data-product-id="tshirt-001"
data-name="Classic T-Shirt"
data-price="2500">
Add to Cart
</button>
That's it. The cart handles the rest.
Add to Cart Button
The cart script listens for clicks on any element with the data-add-to-cart attribute.
Required Data Attributes
data-add-to-cart— Marks the element as an add-to-cart triggerdata-product-id— Unique identifier for the productdata-name— Product display namedata-price— Price in cents (e.g.,2500= $25.00)
Optional Data Attributes
data-currency— Three-letter currency code (default:usd)data-image-url— Product image URL (shown in cart)data-max-quantity— Maximum quantity per order (default:99)data-esign-template-id— E-signature template ID. When present, the cart will redirect to/checkoutfor agreement signing before Stripe payment.data-customizations— JSON-encoded array of{label, value}objects capturing per-line customizations (e.g. color picks, monogram text, engraving options). See Customizations below.
Full Example
<button
data-add-to-cart
data-product-id="tshirt-001"
data-name="Classic T-Shirt"
data-price="2500"
data-currency="usd"
data-image-url="/images/tshirt.jpg"
data-max-quantity="10">
Add to Cart
</button>
SKUs must be static. The server validates products by crawling your built HTML for
data-product-idattributes. Don't rewritedata-product-idfrom JavaScript to encode user selections — usedata-customizationsinstead. SKUs that aren't present in the built HTML fail validation with "Product not found" at checkout.
Customizations
Customizations let a single SKU carry per-line variation (colors, sizes, monograms, engraving text, etc.) without exploding your product list. The platform treats them as opaque {label, value} pairs — your website decides what they mean.
Encoding
Set data-customizations on the add-to-cart button to a JSON-encoded array of {label, value} objects. Both fields are strings.
<button
data-add-to-cart
data-product-id="star-fidget-001"
data-name="Star Fidget"
data-price="1500"
data-customizations='[{"label":"Color 1","value":"Blue"},{"label":"Color 2","value":"Red"}]'>
Add to Cart
</button>
Most websites build this JSON in JavaScript as the user makes selections, then update the button's data-customizations attribute before the customer clicks "Add to Cart".
Cart behavior
- Line identity. Two cart lines that share a
data-product-idbut differ by customizations stay separate. Two identical adds (same product, same customizations) merge into one line with quantity 2. - Display. The cart page renders each customization as a small bulleted list under the product name.
- Stripe receipt. The customization summary is appended to the line-item name on the Stripe-hosted checkout page and receipt (e.g.
Star Fidget — Color 1: Blue, Color 2: Red). - Order record. Customizations are persisted on the order's line items so website owners can see what was chosen.
Server limits
The server sanitizes customizations on every checkout request:
- Maximum 10 entries per line
- Maximum 100 characters per
labelorvalue - Entries missing either field are silently dropped
- Final Stripe line-item name is truncated to 250 characters
Anything that doesn't fit is dropped without erroring, so it's worth keeping your client-side labels short.
Button States
When localStorage is unavailable (e.g., private browsing), buttons are automatically disabled and an error alert is shown.
Cart JavaScript API
The Cart object is available globally for advanced use cases.
Methods
// Get all cart items
const items = Cart.getItems();
// Add an item (quantity defaults to 1)
Cart.addItem({
productId: 'tshirt-001',
name: 'Classic T-Shirt',
price: 2500,
currency: 'usd',
imageUrl: '/assets/images/tshirt.jpg',
customizations: [
{ label: 'Size', value: 'L' },
{ label: 'Monogram', value: 'RP' }
]
}, 2);
// Update quantity (removes item if quantity <= 0)
// Note: keyed by lineKey, not productId — see "Line identity" below
Cart.updateQuantity(lineKey, 3);
// Remove item
Cart.removeItem(lineKey);
// Clear entire cart
Cart.clear();
// Get totals
const totals = Cart.getTotals();
// { itemCount: 3, subtotal: 7500, currency: 'usd' }
// Check if localStorage is available
if (Cart.isAvailable()) {
// Cart functions will work
}
Line identity
Each cart line carries a lineKey that uniquely identifies it. For products without customizations, lineKey === productId. For products with customizations, lineKey combines the product ID and a canonical serialization of the customizations, so the same product with different selections produces distinct lines.
Cart.updateQuantity() and Cart.removeItem() take a lineKey (not a productId). If you're building a custom cart UI, read item.lineKey from the items array and pass it back to these methods.
Events
The cart dispatches a cartUpdated event on the window whenever the cart changes:
window.addEventListener('cartUpdated', function(event) {
console.log('Cart updated:', event.detail);
// event.detail contains:
// {
// items: [{ lineKey, productId, name, price, currency, quantity,
// maxQuantity, imageUrl, customizations, ... }],
// updatedAt: timestamp,
// version: 2
// }
});
Schema version: the cart schema is at
version: 2. Carts saved by older clients (version: 1) are migrated transparently on read —customizationsis backfilled to[]andlineKeytoproductId.
Cart Page
The platform provides a built-in cart page at /app/shop/cart. Users can:
- View cart contents
- Adjust quantities
- Remove items
- Proceed to checkout
The floating cart icon links to this page automatically.
Custom Cart Page (Optional)
To build a custom cart page, use the initShoppingCart function:
<div data-init="initShoppingCart">
<div id="cart-items"></div>
<div id="empty-cart" style="display: none;">
<p>Your cart is empty.</p>
</div>
<div id="cart-summary" style="display: none;">
<p>Items: <span id="summary-item-count">0</span></p>
<p>Subtotal: <span id="summary-subtotal">$0.00</span></p>
<button id="checkout-button" class="btn btn-primary">
Proceed to Checkout
</button>
</div>
</div>
Checkout Flow
- User clicks "Proceed to Checkout"
- If any cart items have an
esignTemplateId, redirect to/checkoutfor agreement signing before proceeding to Stripe - Cart items are sent to
/api/shop/checkout - Server validates products by crawling built website HTML
- Server creates Stripe Checkout Session
- User is redirected to Stripe's hosted checkout
- After payment, user returns to success/cancel page
E-Sign Before Payment
Some products may require a signed agreement before payment (e.g., a puppy purchase agreement). Add data-esign-template-id to the product's add-to-cart button:
<button
data-add-to-cart
data-product-id="puppy-ruby"
data-name="Deposit — Ruby"
data-price="50000"
data-esign-template-id="your-template-uuid">
Reserve with $500 Deposit
</button>
When the user clicks "Proceed to Checkout" from the cart page, the cart checks all items for e-sign template IDs. If any are found, it redirects to /checkout where the website's e-sign flow collects the signer's name and email, presents the agreement for signing, and then triggers Stripe checkout after the agreement is signed.
Success/Cancel Pages
Default pages are provided at:
/app/shop/checkout-success/app/shop/checkout-cancel
Custom Success/Cancel Pages
Website owners can configure custom URLs in Stripe settings. Create your own pages at any URL:
<h1>Thank you for your order!</h1>
<p>We've received your payment and will process your order shortly.</p>
<a href="/products/">Continue Shopping</a>
Server-Side Validation
The platform validates products during checkout by crawling your built website. This prevents price tampering.
How it works:
- Checkout request includes product IDs and quantities
- Server crawls your website's HTML files
- Finds elements with matching
data-product-idattributes - Extracts prices from
data-priceattributes - Validates quantities against
data-max-quantity - Creates Stripe session with server-side prices
Important: Product data is extracted from your built website, not from the cart request. This means:
- Prices must be present in your HTML via data attributes
- Products not in your built website will fail validation
- Rebuild your website after changing product data
Troubleshooting
"Cart unavailable" error
localStorage is disabled or unavailable. Common causes:
- Private/incognito browsing mode
- Browser storage settings
- Third-party cookie blocking
Checkout fails with "Product not found"
The product isn't in your built website HTML. Ensure:
- Button has
data-product-idattribute - Website has been rebuilt after adding/changing products
data-product-idisn't being rewritten by JavaScript to encode user selections (e.g.prod-blue-large). The SKU must match a staticdata-product-idin your built HTML — usedata-customizationsfor per-line variations instead.
Checkout fails with "Stripe not connected"
The website owner hasn't connected their Stripe account. See Connecting Stripe.
Prices don't match
The server uses prices from your built HTML, not from the cart. Ensure:
data-priceattribute is correct in your HTML- Website has been rebuilt after price changes
Cart badge not updating
Check browser console for JavaScript errors. Ensure:
cart.jsis loaded- No conflicting scripts
Jekyll Example
The example website in sites/example/ includes a complete Jekyll shop implementation:
_products/— Product collection files_layouts/product.html— Product detail template_pages/products.html— Product listing page_config.yml— Collection configuration
Review these files for a working reference.