# x402-shopify API > Shopify checkout with USDC payments via the x402 protocol ## Overview This API enables purchasing products from Shopify stores using USDC stablecoin on the Base network. It implements the x402 HTTP payment protocol where: 1. First request (without payment) returns HTTP 402 with price 2. Second request (with payment header) processes the order ## Base URL Production: https://your-domain.com Local: http://localhost:3000 ## Authentication The checkout endpoint uses x402 protocol authentication via the `Payment-Signature` header. No API key required for checkout - payment is the authentication. --- ## Discovery ### GET /.well-known/x402 Returns x402 protocol discovery information for this API. #### Response (HTTP 200) ```json { "x402Version": 2, "endpoints": [ { "path": "/api/shopify/checkout", "method": "POST", "description": "Purchase products from Shopify stores with USDC", "accepts": [ { "scheme": "exact", "network": "eip155:8453", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x...", "maxTimeoutSeconds": 300 } ], "pricing": "dynamic", "pricingNote": "Price determined by cart contents, shipping, and tax" } ], "documentation": "https://api.example.com/llms.txt", "network": { "name": "Base", "chainId": 8453, "caip2": "eip155:8453" }, "asset": { "name": "USD Coin", "symbol": "USDC", "decimals": 6, "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" } } ``` --- ## Endpoints ### POST /api/shopify/checkout Purchase products with USDC payment. #### Request Body ```json { "shop": "store-name.myshopify.com", "line_items": [ { "variant_id": "gid://shopify/ProductVariant/123456789", "quantity": 1 } ], "shipping_address": { "first_name": "John", "last_name": "Doe", "address1": "123 Main St", "address2": "Apt 4", "city": "San Francisco", "province": "CA", "country": "US", "zip": "94102", "phone": "+1234567890" }, "shipping_line": { "title": "Standard Shipping", "price": "5.99" }, "email": "customer@example.com", "idempotency_key": "unique-order-key-123" } ``` #### Required Fields | Field | Type | Description | |-------|------|-------------| | shop | string | Shopify store domain (e.g., "store.myshopify.com") | | line_items | array | Products to purchase | | line_items[].variant_id | string | Shopify variant GID | | line_items[].quantity | number | Quantity (positive integer) | | shipping_address.first_name | string | Customer first name | | shipping_address.last_name | string | Customer last name | | shipping_address.address1 | string | Street address | | shipping_address.city | string | City | | shipping_address.province | string | State/province code | | shipping_address.country | string | Country code (e.g., "US") | | shipping_address.zip | string | Postal code | | idempotency_key | string | Unique key to prevent duplicate orders | #### Optional Fields | Field | Type | Description | |-------|------|-------------| | shipping_address.address2 | string | Apartment, suite, etc. | | shipping_address.phone | string | Phone number | | shipping_line | object | Selected shipping method | | shipping_line.title | string | Shipping method name | | shipping_line.price | string | Shipping cost (e.g., "5.99") | | email | string | Customer email for order confirmation | #### Response (No Payment - HTTP 402) When called without payment header, returns payment requirements: ```json { "x402Version": 2, "error": "Payment required", "resource": { "url": "https://api.example.com/api/shopify/checkout", "description": "Checkout with USDC payment", "mimeType": "application/json" }, "accepts": [ { "scheme": "exact", "network": "eip155:8453", "amount": "1050000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0x...", "maxTimeoutSeconds": 300, "extra": { "name": "USD Coin", "version": "2" } } ] } ``` Headers returned: - `Payment-Required`: Base64-encoded payment requirements #### Response (With Payment - HTTP 201) When called with valid payment: ```json { "order_id": "gid://shopify/Order/123456789", "order_number": "#1001", "tx_hash": "0xabc123...", "status": "created" } ``` #### Error Responses | Status | Error | Description | |--------|-------|-------------| | 400 | validation_error | Invalid request body | | 400 | invalid_payment_header | Malformed payment header | | 402 | payment_verification_failed | Payment signature invalid | | 402 | payment_settlement_failed | Payment transaction failed | | 404 | merchant_not_found | Shop not registered or disabled | | 500 | order_creation_failed | Shopify order creation failed (payment was taken) | --- ### POST /api/shopify/search-products Semantic product search using AI-powered vector search and LLM ranking. Searches across all enabled stores with x402 feature. #### Request Body ```json { "product_type": "snowboard", "product_domain": "sports", "attributes": [ { "name": "color", "value": "red" }, { "name": "size", "value": "medium" } ] } ``` | Field | Type | Description | |-------|------|-------------| | product_type | string | Required. Specific product type (e.g., "snowboard", "t-shirt", "laptop") | | product_domain | string | Required. Broad category (e.g., "sports", "fashion", "electronics") | | attributes | array | Optional. Array of attribute filters | | attributes[].name | string | Attribute name (e.g., "color", "size", "material") | | attributes[].value | string | Attribute value (e.g., "red", "large", "cotton") | #### Response (HTTP 200) Returns top 3 products ranked by AI: ```json { "products": [ { "id": "gid://shopify/Product/123", "title": "Red Snowboard Pro", "handle": "red-snowboard-pro", "description": "High-performance red snowboard for all terrain...", "productType": "Snowboard", "tags": ["winter", "sports"], "merchantId": "...", "shop": "store.myshopify.com", "createdAt": "2024-01-01T00:00:00.000Z", "updatedAt": "2024-01-01T00:00:00.000Z", "syncedAt": "2024-01-01T00:00:00.000Z", "variants": [ { "id": "variant-id-1", "productId": "gid://shopify/Product/123", "title": "Red / Medium", "available": true, "price": "299.99", "selectedOptions": { "Color": "Red", "Size": "Medium" } } ] } ] } ``` #### Response Fields | Field | Type | Description | |-------|------|-------------| | products | array | Top 3 ranked products (ordered by best match) | | products[].variants | array | Available product variants with pricing and options | #### How It Works 1. **Vector Search**: Generates semantic embedding from search query and finds ~30 candidate products using vector similarity 2. **Individual Grading**: Each candidate is analyzed individually against the search criteria, creating a detailed match report 3. **LLM Ranking**: Top 3 products are selected based on the detailed reports, considering: - Type and domain match - Attribute matching (especially colors, sizes, materials) - Overall relevance score 4. **Response**: Returns products with variants #### Error Responses | Status | Error | Description | |--------|-------|-------------| | 400 | Invalid request | Missing required fields or invalid attribute format | | 500 | Failed to search products | Internal error during search or ranking | #### Example ```typescript const results = await fetch("/api/shopify/search-products", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ product_type: "snowboard", product_domain: "sports", attributes: [ { name: "color", value: "red" }, { name: "size", value: "medium" } ] }) }).then(r => r.json()); console.log(`Found ${results.products.length} matches`); results.products.forEach(product => { console.log(`${product.title}`); }); ``` **Note**: This endpoint searches across all stores that have the x402 feature enabled. Results are ranked by AI to find the best semantic matches, not just keyword matches. --- ## x402 Payment Flow ### Step 1: Get Price ```bash curl -X POST https://api.example.com/api/shopify/checkout \ -H "Content-Type: application/json" \ -d '{ "shop": "store.myshopify.com", "line_items": [{"variant_id": "gid://shopify/ProductVariant/123", "quantity": 1}], "shipping_address": {...}, "idempotency_key": "order-123" }' # Returns: HTTP 402 with payment requirements ``` ### Step 2: Create Payment Using @x402/fetch or compatible client: ```typescript import { wrapFetchWithPayment, x402Client } from "@x402/fetch"; import { ExactEvmScheme } from "@x402/evm/exact/client"; const client = new x402Client().register("eip155:8453", new ExactEvmScheme(signer)); const x402Fetch = wrapFetchWithPayment(fetch, client); const response = await x402Fetch("https://api.example.com/api/shopify/checkout", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ shop: "store.myshopify.com", line_items: [{ variant_id: "gid://shopify/ProductVariant/123", quantity: 1 }], shipping_address: { ... }, idempotency_key: "order-123" }) }); ``` ### Payment Details - **Network**: Base (eip155:8453) - **Asset**: USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) - **Protocol**: x402 v2 with "exact" scheme - **Amount**: In USDC base units (6 decimals, e.g., 1000000 = $1.00) --- ## Variant ID Format Shopify variant IDs must be in Global ID (GID) format: ``` gid://shopify/ProductVariant/51534946238744 ``` You can get variant IDs from the /api/shopify/search-products endpoint. --- ## Idempotency The `idempotency_key` prevents duplicate orders. If you retry a request with the same key after a successful order, you'll receive the existing order details instead of creating a new one. Recommended format: `{user-id}-{cart-hash}-{timestamp}` --- ## Error Handling If payment succeeds but order creation fails, the response includes: ```json { "error": "order_creation_failed", "message": "Payment settled (tx: 0x...) but order creation failed. Contact support for refund.", "tx_hash": "0xabc123..." } ``` The transaction hash can be used for refund processing. --- ## Example: Complete Checkout Flow ```typescript // 1. Search for products const searchResults = await fetch("/api/shopify/search-products", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ product_type: "snowboard", product_domain: "sports" }) }).then(r => r.json()); // 2. Checkout with payment const order = await x402Fetch("/api/shopify/checkout", { method: "POST", body: JSON.stringify({ shop: searchResults.products[0].shop, line_items: [{ variant_id: searchResults.products[0].variants[0].id, quantity: 1 }], shipping_address: { first_name: "John", last_name: "Doe", address1: "123 Main St", city: "San Francisco", province: "CA", country: "US", zip: "94102" }, email: "customer@example.com", idempotency_key: `order-${Date.now()}` }) }).then(r => r.json()); console.log(`Order ${order.order_number} created! TX: ${order.tx_hash}`); ```