For Website Developers

Shop Integration

Add shopping cart and checkout

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
  • Flat-rate shipping — Optional per-order shipping fee and address collection, configured in your HTML
  • Discount codes — Promo codes managed by the website owner in the dashboard, entered by the shopper on the cart page

Prerequisites

  1. Website owner must connect Stripe — See Connecting Stripe
  2. 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 trigger
  • data-product-id — Unique identifier for the product
  • data-name — Product display name
  • data-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-shippable — Set to "false" for digital products that should never trigger a shipping fee or address collection (default: true). See Shipping below.
  • data-esign-template-id — E-signature template ID. When present, the cart will redirect to /checkout for 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-id attributes. Don't rewrite data-product-id from JavaScript to encode user selections — use data-customizations instead. SKUs that aren't present in the built HTML fail validation with "Product not found" at checkout.

Button States

When localStorage is unavailable (e.g., private browsing), buttons are automatically disabled and an error alert is shown.


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-id but 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 label or value
  • 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.


Shipping

Charge a flat shipping fee per order and collect a shipping address at checkout. Like product prices, shipping is configured entirely in your HTML and read server-side from your built website — there is nothing to set up in the dashboard.

Enable shipping

Add a data-shop-config element once, in a shared layout so it appears on every page:

<div data-shop-config data-shipping-flat="500" data-shipping-countries="US"></div>
  • data-shipping-flat — Flat shipping fee in cents (e.g., 500 = $5.00), charged once per order regardless of item count. Required; a value of 0 (or removing the element) disables shipping.
  • data-shipping-countries — Comma-separated ISO country codes the shopper can ship to (default: US).

The element renders nothing — it only carries configuration for the server-side crawl. Rebuild your website after adding or changing it.

Digital products

Products ship by default. Mark digital products with data-shippable="false" so they never trigger shipping:

<button
  data-add-to-cart
  data-product-id="ebook-001"
  data-name="Recipe E-Book"
  data-price="900"
  data-shippable="false">
  Add to Cart
</button>

A cart containing only non-shippable items skips the shipping fee and address collection entirely. A mixed cart (some physical, some digital) is charged the flat fee once.

What the shopper sees

The cart page shows the item subtotal with a "Shipping calculated at checkout" note. The shipping fee, address form, and grand total appear on Stripe's hosted checkout page — Stripe shows a "Standard shipping" line, collects the address, and adds the fee to the total.

The shipping fee and the ship-to address are recorded on the order, where the website owner can see them in the dashboard's order detail view.

Current limitations

  • One flat rate per website — no weight tiers, free-shipping thresholds, or multiple speeds (e.g., Standard vs. Express) yet.
  • The rate doesn't vary by destination; data-shipping-countries only controls which countries the shopper may ship to.
  • No sales tax calculation.

Discount Codes

Discount codes are created and managed by the website owner in the dashboard (see Managing Discount Codes). There is nothing to add to your HTML — the cart page at /app/shop/cart includes a "Promo code" box automatically.

What the shopper sees

  1. The shopper enters a code on the cart page and clicks Apply
  2. The applied code survives page reloads, the e-sign redirect, and a cancelled Stripe Checkout
  3. At checkout, the platform re-validates the code and reduces each line item's unit price proportionally, so Stripe's hosted page shows discounted line prices rather than a separate discount line. The cart page notes this for the shopper: "Discount applied to your order. Final total shown on the secure checkout page."

The discount applies to the item subtotal only — a shipping fee is added unchanged. Codes match case-insensitively (SAVE10 == save10).


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.


Checkout Flow

  1. User clicks "Proceed to Checkout"
  2. If any cart items have an esignTemplateId, redirect to /checkout for agreement signing before proceeding to Stripe
  3. Cart items (and any applied discount code) are sent to /api/shop/checkout
  4. Server validates products by crawling built website HTML
  5. If a discount code was applied, the server re-validates it and reduces line prices accordingly
  6. If shipping is configured and the cart has a shippable item, the server attaches the flat shipping rate and enables address collection
  7. Server creates Stripe Checkout Session
  8. User is redirected to Stripe's hosted checkout (shipping fee and address form appear here)
  9. 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:

  1. Checkout request includes product IDs and quantities
  2. Server crawls your website's HTML files
  3. Finds elements with matching data-product-id attributes
  4. Extracts prices from data-price attributes
  5. Validates quantities against data-max-quantity
  6. 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

The same applies to the shipping configuration: the flat rate is read from the data-shop-config element in your built HTML, never from the browser.


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-id attribute
  • Website has been rebuilt after adding/changing products
  • data-product-id isn't being rewritten by JavaScript to encode user selections (e.g. prod-blue-large). The SKU must match a static data-product-id in your built HTML — use data-customizations for 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-price attribute is correct in your HTML
  • Website has been rebuilt after price changes

Shipping fee not showing

The cart page total never includes shipping — the fee and address form appear on Stripe's hosted checkout page. If they don't appear there either, ensure:

  • The data-shop-config element with a positive data-shipping-flat is present in your built HTML (rebuild after adding it)
  • At least one cart item is shippable (items with data-shippable="false" never trigger shipping)

Discount code rejected at checkout but valid on the cart page

The code is re-validated at checkout time against the live dashboard state. Between Apply and checkout the owner may have deactivated the code, it may have expired or hit its maximum redemptions, or the cart may have changed so it fell below the code's minimum subtotal or minimum item quantity. The shopper is shown a message with the specific reason. Code settings (active status, dates, redemption limits, minimums) are managed by the website owner in the dashboard — see Managing Discount Codes.

Cart badge not updating

Check browser console for JavaScript errors. Ensure:

  • cart.js is 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
  • _layouts/base.html — Shared layout carrying the data-shop-config shipping element
  • _pages/products.html — Product listing page
  • _config.yml — Collection configuration

Review these files for a working reference.