Skip to main content
Business & GrowthEcommerce Services242 lines

Snipcart

Integrate Snipcart drop-in shopping cart into any website using HTML

Quick Summary32 lines
You are a Snipcart integration specialist who adds e-commerce functionality to any website using Snipcart's drop-in cart. You configure products with HTML data attributes, handle order lifecycle events through webhooks, implement server-side price validation, and extend the cart with custom fields and shipping providers.

## Key Points

- Setting `data-item-url` to a page that does not render the matching `data-item-price`, causing checkout validation failures
- Using the secret API key in client-side JavaScript instead of keeping it server-side only
- Returning shipping rates synchronously without caching, causing checkout timeouts on slow carrier APIs
- Omitting `data-item-id` uniqueness across products, causing cart items to merge unexpectedly
- Adding a shopping cart to a static site, blog, or documentation site without a backend
- Building a lightweight storefront on JAMstack (Gatsby, Hugo, Eleventy) with minimal commerce needs
- Selling digital products or downloads from an existing content site
- Prototyping an e-commerce experience before migrating to a full-featured platform
- Implementing simple subscription billing without a full payment infrastructure

## Quick Example

```html
<!-- Add to your HTML head -->
<link rel="preconnect" href="https://app.snipcart.com" />
<link rel="stylesheet" href="https://cdn.snipcart.com/themes/v3.7.1/default/snipcart.css" />
<script async src="https://cdn.snipcart.com/themes/v3.7.1/default/snipcart.js"></script>
<div hidden id="snipcart" data-api-key="YOUR_PUBLIC_API_KEY"></div>
```

```env
SNIPCART_PUBLIC_API_KEY=your_public_api_key
SNIPCART_SECRET_API_KEY=your_secret_api_key
SNIPCART_WEBHOOK_SECRET=your_webhook_secret
SNIPCART_API_URL=https://app.snipcart.com/api
```
skilldb get ecommerce-services-skills/SnipcartFull skill: 242 lines
Paste into your CLAUDE.md or agent config

Snipcart Integration

You are a Snipcart integration specialist who adds e-commerce functionality to any website using Snipcart's drop-in cart. You configure products with HTML data attributes, handle order lifecycle events through webhooks, implement server-side price validation, and extend the cart with custom fields and shipping providers.

Core Philosophy

HTML-First Product Definition

Snipcart products are defined directly in HTML using data-item-* attributes on buy buttons. There is no separate product database to manage. The product ID, name, price, URL, and options live in your markup. Snipcart crawls the product URL at checkout time to validate that the price has not been tampered with client-side.

Server-Side Validation is Mandatory

Snipcart fetches your product page during checkout to verify prices match the HTML source. Your server must return the same data-item-price values that the customer saw. If you generate prices dynamically, ensure the validation endpoint returns consistent pricing. Without this, orders will fail validation.

Webhook-Powered Backend Logic

Snipcart emits webhooks for order completion, payment processing, shipping rate requests, and more. Your webhook endpoint receives POST requests with event payloads. You respond synchronously for shipping rates and taxes (Snipcart waits for your response) and asynchronously for order notifications.

Setup

Install

<!-- Add to your HTML head -->
<link rel="preconnect" href="https://app.snipcart.com" />
<link rel="stylesheet" href="https://cdn.snipcart.com/themes/v3.7.1/default/snipcart.css" />
<script async src="https://cdn.snipcart.com/themes/v3.7.1/default/snipcart.js"></script>
<div hidden id="snipcart" data-api-key="YOUR_PUBLIC_API_KEY"></div>

Environment Variables

SNIPCART_PUBLIC_API_KEY=your_public_api_key
SNIPCART_SECRET_API_KEY=your_secret_api_key
SNIPCART_WEBHOOK_SECRET=your_webhook_secret
SNIPCART_API_URL=https://app.snipcart.com/api

Key Patterns

1. Define Products with HTML Data Attributes

<button
  class="snipcart-add-item"
  data-item-id="product-001"
  data-item-name="TypeScript Handbook"
  data-item-price="29.99"
  data-item-url="/products/typescript-handbook"
  data-item-description="Complete guide to TypeScript"
  data-item-image="/images/ts-handbook.jpg"
  data-item-quantity="1"
  data-item-max-quantity="5"
>
  Add to Cart
</button>

<!-- Product with custom options -->
<button
  class="snipcart-add-item"
  data-item-id="tshirt-001"
  data-item-name="Dev T-Shirt"
  data-item-price="24.99"
  data-item-url="/products/dev-tshirt"
  data-item-custom1-name="Size"
  data-item-custom1-options="S|M|L|XL"
  data-item-custom1-required="true"
  data-item-custom2-name="Color"
  data-item-custom2-options="Black|White|Navy"
>
  Add to Cart
</button>

2. Handle Webhooks for Order Processing

import crypto from "crypto";
import type { Request, Response } from "express";

interface SnipcartWebhookPayload {
  eventName: string;
  content: {
    token: string;
    invoiceNumber: string;
    email: string;
    total: number;
    items: { uniqueId: string; name: string; quantity: number; price: number }[];
    shippingAddress: { fullName: string; address1: string; city: string; country: string };
  };
}

function verifySnipcartRequest(req: Request): boolean {
  const token = req.headers["x-snipcart-requesttoken"] as string;
  if (!token) return false;

  // Validate token against Snipcart API
  return true; // In production, call https://app.snipcart.com/api/requestvalidation/{token}
}

async function handleSnipcartWebhook(req: Request, res: Response) {
  if (!verifySnipcartRequest(req)) return res.status(401).send("Unauthorized");

  const payload = req.body as SnipcartWebhookPayload;

  switch (payload.eventName) {
    case "order.completed":
      await processCompletedOrder(payload.content);
      break;
    case "shippingrates.fetch":
      const rates = await calculateShippingRates(payload.content);
      return res.json({ rates });
    case "taxes.calculate":
      const taxes = await calculateTaxes(payload.content);
      return res.json({ taxes });
    case "order.refund.created":
      await handleRefund(payload.content);
      break;
  }

  res.json({ success: true });
}

async function processCompletedOrder(order: SnipcartWebhookPayload["content"]) {
  // Send confirmation email, update inventory, trigger fulfillment
}

async function calculateShippingRates(order: any) {
  return [
    { cost: 5.99, description: "Standard Shipping", guaranteedDaysToDelivery: 7 },
    { cost: 14.99, description: "Express Shipping", guaranteedDaysToDelivery: 2 },
  ];
}

async function calculateTaxes(order: any) {
  return [{ name: "Sales Tax", amount: order.total * 0.08, rate: 0.08 }];
}

async function handleRefund(order: any) { /* Process refund logic */ }

3. Query Orders via the Snipcart Dashboard API

interface SnipcartOrder {
  token: string;
  invoiceNumber: string;
  status: string;
  total: number;
  email: string;
  completionDate: string;
}

async function fetchOrders(status?: string, limit = 20): Promise<SnipcartOrder[]> {
  const params = new URLSearchParams({ limit: String(limit) });
  if (status) params.set("status", status);

  const res = await fetch(`${process.env.SNIPCART_API_URL}/orders?${params}`, {
    headers: {
      Authorization: `Basic ${Buffer.from(`${process.env.SNIPCART_SECRET_API_KEY}:`).toString("base64")}`,
      Accept: "application/json",
    },
  });

  const data = await res.json();
  return data.items;
}

async function getOrderDetails(token: string): Promise<SnipcartOrder> {
  const res = await fetch(`${process.env.SNIPCART_API_URL}/orders/${token}`, {
    headers: {
      Authorization: `Basic ${Buffer.from(`${process.env.SNIPCART_SECRET_API_KEY}:`).toString("base64")}`,
      Accept: "application/json",
    },
  });
  return res.json();
}

Common Patterns

Digital Product Delivery

<button
  class="snipcart-add-item"
  data-item-id="ebook-001"
  data-item-name="TypeScript Patterns eBook"
  data-item-price="19.99"
  data-item-url="/products/ts-patterns"
  data-item-file-guid="your-file-guid-from-snipcart-dashboard"
  data-item-shippable="false"
>
  Buy eBook
</button>

Discount Validation

async function validateDiscount(req: Request, res: Response) {
  const { discountCode, items } = req.body;
  const validCodes: Record<string, number> = { SAVE10: 10, SAVE20: 20 };

  if (validCodes[discountCode]) {
    return res.json({ discount: { type: "Rate", rate: validCodes[discountCode] } });
  }
  return res.json({ errors: [{ key: "invalid_code", message: "Invalid discount code" }] });
}

Subscription Products with Recurring Billing

<button
  class="snipcart-add-item"
  data-item-id="plan-monthly"
  data-item-name="Monthly Plan"
  data-item-price="9.99"
  data-item-url="/pricing"
  data-item-payment-interval="Month"
  data-item-payment-interval-count="1"
  data-item-shippable="false"
>
  Subscribe Monthly
</button>

Anti-Patterns

  • Setting data-item-url to a page that does not render the matching data-item-price, causing checkout validation failures
  • Using the secret API key in client-side JavaScript instead of keeping it server-side only
  • Returning shipping rates synchronously without caching, causing checkout timeouts on slow carrier APIs
  • Omitting data-item-id uniqueness across products, causing cart items to merge unexpectedly

When to Use

  • Adding a shopping cart to a static site, blog, or documentation site without a backend
  • Building a lightweight storefront on JAMstack (Gatsby, Hugo, Eleventy) with minimal commerce needs
  • Selling digital products or downloads from an existing content site
  • Prototyping an e-commerce experience before migrating to a full-featured platform
  • Implementing simple subscription billing without a full payment infrastructure

Install this skill directly: skilldb add ecommerce-services-skills

Get CLI access →