> ## Documentation Index
> Fetch the complete documentation index at: https://docs-test.rye.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Build an Ecommerce App with Lovable

> Build a full ecommerce app using Lovable and Rye's Universal Checkout API with Supabase Edge Functions.

export const OpenWithLLM = ({prompt, tools}) => {
  const handleClick = (tool, e) => {
    const btn = e.currentTarget;
    const labelSpan = btn.querySelector('[data-label]');
    const copiedLabel = btn.getAttribute('data-copied');
    if (tool === 'claude') {
      window.open(`https://claude.ai/new?q=${encodeURIComponent(prompt)}`, '_blank');
      return;
    }
    if (tool === 'lovable') {
      window.open(`https://lovable.dev/?autosubmit=true#prompt=${encodeURIComponent(prompt)}`, '_blank');
      return;
    }
    navigator.clipboard.writeText(prompt);
    if (labelSpan && copiedLabel) {
      const original = labelSpan.textContent;
      labelSpan.textContent = copiedLabel;
      setTimeout(() => {
        labelSpan.textContent = original;
      }, 2500);
    }
    if (tool === 'chatgpt') {
      window.open('https://chatgpt.com/', '_blank');
    }
  };
  const show = tool => !tools || tools.includes(tool);
  const buttonBase = "inline-flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium transition-all duration-200 border cursor-pointer";
  return <div className="my-6 rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 p-5">
      <div className="flex items-center gap-2 mb-3">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-gray-500 dark:text-gray-400">
          <path d="M12 2L2 7l10 5 10-5-10-5z" />
          <path d="M2 17l10 5 10-5" />
          <path d="M2 12l10 5 10-5" />
        </svg>
        <span className="text-sm font-semibold text-gray-700 dark:text-gray-200">Build this with AI</span>
      </div>
      <p className="text-xs text-gray-500 dark:text-gray-400 mb-4">
        Open a pre-built prompt in your preferred AI tool to generate a complete working integration.
      </p>
      <div className="flex flex-wrap gap-2">
        {show('claude') && <button onClick={e => handleClick('claude', e)} data-copied="Opening..." className={`${buttonBase}`}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
              <path d="M4.709 15.955l4.72-2.756.08-.046 2.698-1.575c.092-.054.153-.063.2-.063.046 0 .107.01.199.063l2.698 1.575 4.8 2.802c.026.014.04.022.064.037l-7.552 4.41a.46.46 0 01-.232.063.46.46 0 01-.232-.063L4.709 15.955zm7.897-12.42l7.585 4.428a.471.471 0 01.233.408v8.747c0 .03-.003.057-.01.094l-4.812-2.81-2.698-1.574a.384.384 0 01-.199-.327V3.535zm-1.212 0v8.966a.384.384 0 01-.2.327L8.498 14.4l-4.812 2.81a.519.519 0 01-.01-.095V8.37c0-.17.09-.326.233-.408l7.485-4.428z" />
            </svg>
            <span data-label>Open in Claude</span>
          </button>}

        {show('lovable') && <button onClick={e => handleClick('lovable', e)} data-copied="Opening..." className={`${buttonBase}`}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
              <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
            </svg>
            <span data-label>Open in Lovable</span>
          </button>}

        {show('chatgpt') && <button onClick={e => handleClick('chatgpt', e)} data-copied="Copied! Paste in ChatGPT" className={`${buttonBase}`}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
              <path d="M22.282 9.821a5.985 5.985 0 00-.516-4.91 6.046 6.046 0 00-6.51-2.9A6.065 6.065 0 0011.708.414a6.116 6.116 0 00-5.89 4.412 6.005 6.005 0 00-4.014 2.91 6.048 6.048 0 00.749 7.084 5.98 5.98 0 00.516 4.911 6.051 6.051 0 006.511 2.9 6.056 6.056 0 004.548 1.956 6.116 6.116 0 005.889-4.411 6.006 6.006 0 004.014-2.912 6.043 6.043 0 00-.749-7.084zM13.109 21.95a4.582 4.582 0 01-2.94-1.063c.037-.02.102-.056.144-.081l4.884-2.822a.794.794 0 00.4-.688v-6.892l2.064 1.192a.073.073 0 01.04.056v5.707a4.601 4.601 0 01-4.592 4.592zM3.6 17.938a4.556 4.556 0 01-.546-3.072c.036.021.099.06.144.084l4.884 2.822a.796.796 0 00.79 0l5.964-3.444v2.384a.074.074 0 01-.03.063L9.88 19.615a4.6 4.6 0 01-6.28-1.677zM2.338 7.875a4.56 4.56 0 012.394-2.007c0 .042-.003.114-.003.164v5.645a.788.788 0 00.4.688l5.964 3.443-2.064 1.192a.075.075 0 01-.071.006L4.032 14.16a4.6 4.6 0 01-1.694-6.284zM18.93 11.99l-5.965-3.443 2.065-1.192a.074.074 0 01.07-.006l4.927 2.844a4.588 4.588 0 01-.71 8.282v-5.797a.793.793 0 00-.387-.688zm2.056-3.08c-.037-.022-.1-.06-.144-.084L15.96 6.004a.795.795 0 00-.79 0L9.205 9.448V7.064a.073.073 0 01.03-.063l4.925-2.843a4.591 4.591 0 016.826 4.752zM8.114 12.544L6.05 11.352a.073.073 0 01-.04-.057V5.59a4.59 4.59 0 017.533-3.523c-.037.02-.102.055-.144.08L8.514 4.97a.794.794 0 00-.4.688v6.886zm1.12-2.418l2.657-1.534 2.656 1.534v3.068l-2.656 1.534-2.657-1.534V10.126z" />
            </svg>
            <span data-label>Open in ChatGPT</span>
          </button>}

        {show('cursor') && <button onClick={e => handleClick('cursor', e)} data-copied="Copied! Paste in Composer" className={`${buttonBase}`}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
              <path d="M3.147 2.245a.6.6 0 01.652-.054l17.4 9.6a.6.6 0 01.014 1.046l-6.9 4.027-4.182 5.178a.6.6 0 01-1.075-.232L3.013 2.858a.6.6 0 01.134-.613zM10.17 15.2l3.563-2.08L5.34 4.44l4.83 10.76zm.894 1.09l-1.1 1.362 1.357 4.58 2.83-3.504-3.087-2.438z" />
            </svg>
            <span data-label>Copy for Cursor</span>
          </button>}

        {show('vscode') && <button onClick={e => handleClick('vscode', e)} data-copied="Copied! Paste in Copilot Chat" className={`${buttonBase}`}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
              <path d="M17.583 2.603l-5.402 4.914L6.968 3.26 4.2 4.86v14.28l2.768 1.6 5.213-4.257 5.402 4.914L21.6 19.2V4.8l-4.017-2.197zM17.4 17.063l-4.392-3.994 4.392-3.994v7.988zM6.6 15.674V8.326l3.695 3.674L6.6 15.674z" />
            </svg>
            <span data-label>Copy for Copilot</span>
          </button>}

        {show('claude-code') && <button onClick={e => handleClick('claude-code', e)} data-copied="Copied! Paste in Claude Code" className={`${buttonBase}`}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <polyline points="4 17 10 11 4 5" />
              <line x1="12" y1="19" x2="20" y2="19" />
            </svg>
            <span data-label>Copy for Claude Code</span>
          </button>}
      </div>
    </div>;
};

export const llmPrompt = `Build a complete ecommerce application using Rye's Universal Checkout API with Supabase Edge Functions as the backend. Use the official Rye TypeScript SDK (checkout-intents) for all Rye API calls. The app should let users browse products and place orders through any merchant (Amazon, Shopify, etc.).

## What Rye Does
Rye is a commerce API that lets you sell products from any merchant (Amazon, Shopify, etc.) in your app. You provide a product URL and buyer details, Rye fetches pricing/shipping from the merchant, and you confirm with a payment token to place the order.

## Architecture
IMPORTANT: All Rye SDK calls MUST happen server-side. Never import or use the checkout-intents SDK in frontend code — the API key must stay on the server.
- Frontend: React app (Vite or Next.js) with TypeScript and Tailwind CSS. Calls Supabase Edge Functions via fetch — never calls Rye directly.
- Backend: Supabase Edge Functions (Deno) that use the checkout-intents SDK to call the Rye API. The CHECKOUT_INTENTS_API_KEY stays server-side as a Supabase secret.

## Rye SDK Setup
In Supabase Edge Functions (Deno), import the SDK via npm: specifier:
import CheckoutIntents from 'npm:checkout-intents';

For Node.js projects: npm install checkout-intents
import CheckoutIntents from 'checkout-intents';

Environment variable: CHECKOUT_INTENTS_API_KEY (the SDK reads this by default)
For staging, also set: CHECKOUT_INTENTS_BASE_URL=https://staging.api.rye.com

Initialize:
const client = new CheckoutIntents(); // reads CHECKOUT_INTENTS_API_KEY from env

## Rye SDK Usage

### Create checkout intent and poll until offer is ready
const intent = await client.checkoutIntents.createAndPoll({
  buyer: { firstName, lastName, email, phone, address1, city, province, postalCode, country: 'US' },
  productUrl: 'https://flybyjing.com/collections/shop/products/sichuan-chili-crisp',
  quantity: 1,
});
// intent.state === 'awaiting_confirmation'
// intent.offer.cost: { subtotal, tax, total } (each with amountSubunits, currencyCode)
// intent.offer.shipping: { availableOptions, selectedOptionId }

### Confirm with payment and poll until completed
const completed = await client.checkoutIntents.confirmAndPoll(intent.id, {
  paymentMethod: { stripeToken: 'tok_visa', type: 'stripe_token' },
});
// completed.state === 'completed' or 'failed'

### Other useful SDK methods
- client.checkoutIntents.create(params) — create without polling
- client.checkoutIntents.retrieve(id) — get current state
- client.checkoutIntents.confirm(id, params) — confirm without polling
- client.checkoutIntents.pollUntilCompleted(id) — poll existing intent
- client.products.lookup({ url }) — look up product details by URL

### Error handling
import CheckoutIntents, { PollTimeoutError } from 'checkout-intents';
If intent.state === 'failed', check intent.failureReason.code and intent.failureReason.message.

## Important Notes
- Test payment token: tok_visa (Stripe test token, works in staging)
- US shipping addresses only

## App Requirements
1. Product browsing page with sample product URLs from various merchants
2. Product detail view showing item info
3. Checkout flow: buyer info form → create intent via Supabase function (using SDK) → poll for offer → display pricing (subtotal, shipping, tax, total) → confirm payment → show order result
4. Supabase Edge Function that uses the checkout-intents SDK (keeps CHECKOUT_INTENTS_API_KEY server-side)
5. Error handling for failed orders with failureReason display
6. Clean, modern UI with Tailwind CSS
7. Environment variables: SUPABASE_URL, SUPABASE_ANON_KEY (frontend), CHECKOUT_INTENTS_API_KEY and CHECKOUT_INTENTS_BASE_URL (Supabase Edge Function secrets)

## Supabase Edge Function Example
The edge function receives requests from the frontend and uses the SDK:
- POST /rye-checkout with action: "create" | "confirm" | "get" and relevant payload
- Uses the checkout-intents SDK initialized with CHECKOUT_INTENTS_API_KEY from Deno.env
- Returns Rye API response to frontend

Generate the complete application with all files. Include a README with setup instructions for connecting to Supabase and configuring the Rye API key. Make sure the .gitignore includes .env and .env.* so secrets are never committed to version control. After generating the code, ask the user for their Rye API key so you can update the .env or .env.local file with the actual CHECKOUT_INTENTS_API_KEY value. Also ask for their Supabase URL and anon key to fill in SUPABASE_URL and SUPABASE_ANON_KEY. Confirm which environment they want to use (staging or production) so you can set CHECKOUT_INTENTS_BASE_URL accordingly.`;

<OpenWithLLM prompt={llmPrompt} tools={['lovable']} />

# Overview

This tutorial shows you how to build an ecommerce app using Lovable, Supabase, and Rye. You can see a [live version of it here](https://rye-demo.lovable.app/) and [view the source code here](https://github.com/rye-com/rye-lovable-demo).

Lovable is an AI-powered app builder that can generate applications from natural language prompts. Supabase is a backend-as-a-service platform, including serverless functions to enable calling third-party APIs. Rye is an ecommerce API that lets you surface products from multiple merchants in your app and place orders programmatically.

By the end of this tutorial, you will have a Lovable app connected to Supabase and Rye, capable of letting users browse products and place orders. From there, you can customize it, including the look and feel, and extend the app’s functionality to build any type of ecommerce experience.

<img src="https://mintcdn.com/rye-35/NgqGsU8bKa84ml3P/images/demo-app-1.png?fit=max&auto=format&n=NgqGsU8bKa84ml3P&q=85&s=93a38e9dc3284c70756fa6177d99d276" alt="Demo App Screenshot" width="2414" height="1934" data-path="images/demo-app-1.png" />

# Tutorial

<Warning>
  This app is for demo purposes only. Before using any code generated from this repo in production, thoroughly review and test it for security and proper functionality.
</Warning>

## Step 1: Set Up Accounts

Lovable: [Sign up for a Lovable account](https://lovable.dev/signup) to generate and manage your app.

Supabase: [Create a Supabase account](https://supabase.com/dashboard/sign-up) and [set up a new project](https://supabase.com/docs/guides/getting-started) in your account for use with this tutorial.

Rye: [Sign up for a Rye staging account](https://docs.rye.com/api-v2/account-registration) to checkout orders. [Get your API key](https://docs.rye.com/api-v2/authorization) and store it in a secure place.

## Step 2: Connect Lovable with Supabase

Connect Supabase to Lovable by [following these steps](https://docs.lovable.dev/integrations/supabase).

## Step 3: Create App in Lovable

Create a new app in Lovable with this prompt.

> Copy an existing GitHub repo

<img src="https://mintcdn.com/rye-35/8I_GNfHB5aVdNFZL/images/lovable-1.png?fit=max&auto=format&n=8I_GNfHB5aVdNFZL&q=85&s=088932ad56a82f6b50dc7d3b74ae5c20" alt="Lovable 1 Pn" width="2124" height="1158" data-path="images/lovable-1.png" />

This will create a blank Lovable project. Then ask Lovable to use this repo.

> [https://github.com/rye-com/rye-lovable-demo](https://github.com/rye-com/rye-lovable-demo)

Lovable will generate the app now. Afterwards run this prompt to confirm the setup.

> 1. Check for any required Supabase functions used by the code.
> 2. If the Supabase functions already exist in Supabase, connect to them.
> 3. If the Supabase functions do not exist, create them in Supabase.
> 4. If the Rye API key doesn’t exist in Supabase, add a placeholder Rye API key with the value of “AddAPIKeyHere”

## Step 4: Add API Key in Supabase

Log in to Supabase and navigate to the new function created within your Supabase project from step 1. Find the environmental variable with the value `AddAPIKeyHere` and replace it with your Rye API key from the staging environment.

## Step 5: Test & Secure the App

After running the prompts in step 3, Lovable will display a preview of your app. Test your app in that section.

Run [Lovable’s built-in security test](https://docs.lovable.dev/features/security) and address any issues it identifies. Lovable generates code with AI, so it is important to verify that all security measures, such as input validation, authentication, and access controls are correctly implemented before deploying the app.

# Extend Your App

Now you can customize your app, including the UI and functionality, to build any type of ecommerce experience.

Using Lovable prompts, you can add features like an admin dashboard to manage products. For a more AI-focused experience, you could integrate a chatbot to answer common questions or guide users through checkout.

Remember, this app is for demo purposes, so review and test it thoroughly before deploying to production.
