# Payment buttons and products

> Sell a fixed-price product with a "Pay with crypto" button, a layerhop.com/buy link or a QR code, on any website and without a backend. Customers pay with any token on any chain; you receive USDC or USDT.

Source: https://docs.layerhop.com/guides/payment-buttons

A **product** is a price you set once in the [dashboard](https://dashboard.layerhop.com) (**Payments → Products**) or with the API: a name, an amount of USDC or USDT, the chain you are paid on and your payout address. Every product gets a buy link and a button you can paste into any website. When a customer clicks it, LayerHop starts a [checkout session](/guides/checkout) for that exact price and opens the payment, on top of your page or on layerhop.com.

You need no backend: the price, token and payout address live on the product, so a visitor cannot change what they pay or where it goes. You still get the signed `checkout.session.completed` [webhook](/guides/webhooks) for every payment.

## 1. Create a product

In the dashboard: **Payments → Products → New product**. Or with the API:

```bash
curl -X POST https://api.layerhop.com/v1/checkout/products \
  -H "x-api-key: $LAYERHOP_API_KEY" -H "content-type: application/json" \
  -d '{
    "name": "Pro plan",
    "description": "1 month, unlimited projects",
    "amount": "49000000",
    "settleChain": "base",
    "settleToken": "USDC",
    "recipient": "0xYourPayoutAddress",
    "successUrl": "https://shop.example.com/thanks",
    "allowedOrigins": ["https://shop.example.com"]
  }'
```

```ts
const product = await lh.checkout.products.create({
  name: "Pro plan", amount: "49000000", settleChain: "base", settleToken: "USDC",
  recipient: "0xYourPayoutAddress", successUrl: "https://shop.example.com/thanks",
});
// product.id = "prod_…", product.url = "https://layerhop.com/buy/prod_…"
```

| Field | Notes |
|---|---|
| `amount`, `settleChain`, `settleToken`, `recipient` | What you are paid and where, exactly as for a [checkout session](/guides/checkout#1-create-a-session). |
| `successUrl`, `cancelUrl` | Where the payer goes afterwards (redirect checkouts). |
| `expiresInMinutes` | Lifetime of each session started from the product (default 60). |
| `allowedOrigins` | Optional list of `https://` origins allowed to start checkouts from a browser, for example your shop. Empty means any site. |
| `active` | Set `false` to stop selling; existing sessions are unaffected. |

Products created with an `lh_test_` key (or in the dashboard's test mode) are [test-mode](/guides/test-mode) products: their payments are simulated.

## 2. Add the button to your site

Paste this anywhere in your HTML:

```html
<script src="https://layerhop.com/checkout.js" async></script>
<button data-layerhop-product="prod_…">Pay with crypto</button>
```

Clicking the button starts a session and opens the payment in a popup on your page; the customer never leaves your site. Add `data-layerhop-reference="order_1042"` to tag the payment with your own order id (it comes back in the webhooks and when you fetch the session with your key), or `data-layerhop-mode="inline"` on a `<div>` to show the payment inside your page instead of a popup.

The button fires DOM events you can listen to:

```js
document.addEventListener("layerhop:paid", (e) => {
  console.log("paid", e.detail.session.id); // show a thank-you message
});
document.addEventListener("layerhop:cancel", () => console.log("closed without paying"));
```

> [!IMPORTANT]
> Use the browser events for the user interface only. Fulfil orders on the signed [`checkout.session.completed` webhook](/guides/webhooks), which LayerHop sends only after it has verified the payment on chain.

## Other ways to sell a product

- **Link or QR code:** share `https://layerhop.com/buy/prod_…` in emails, social posts or invoices. Add `?reference=…` to tag the payment. The dashboard shows a QR code for every product.
- **JavaScript:** `LayerHopCheckout.open({ productId: "prod_…", reference: "order_1042", onPaid: (session) => { … } })`. See [Embedded checkout](/guides/embedded-checkout).
- **React:** `<LayerHopCheckout productId="prod_…" onPaid={…} />` from `@layerhop/widget/react`.
- **WordPress:** the shortcode `[layerhop_button product="prod_…"]` from the [LayerHop WordPress plugin](/guides/wordpress).
- **Your own code:** `POST /v1/checkout/products/{id}/sessions` (no key needed) returns a session; send the customer to its `url`.

## How starting a checkout works

[`POST /v1/checkout/products/{id}/sessions`](/api-reference#start-a-checkout-for-a-product) is public and open to any website (CORS). It creates a session with the product's amount, token, payout address and URLs; the caller can only add a `reference` and `metadata`. If the product lists `allowedOrigins`, browser requests from other sites are refused (`FORBIDDEN`). Inactive products return `CONFLICT`.

Sessions started from a product carry `productId`, so you can tell in webhooks and in the dashboard which product was bought.
