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

# Accept Payments

> Add the SaturnShift crypto Pay button to your checkout or paylink.

Add a "Pay with crypto" button to your checkout alongside your other payment
methods. When a customer chooses it, they are taken to the SaturnShift hosted
checkout to pay on-chain, and you receive a webhook when it settles.

This is the model for a platform that renders the button on behalf of its
merchants: you integrate once and pass the correct merchant per checkout.

## 1. Get the merchant's publishable key

Each merchant has a publishable key (`pk_live_...`) that is safe to use in the
browser. Fetch just the merchant you need, either by the reference you set when
you onboarded them (`psp_reference_id`) or by the SaturnShift merchant id. Store
the `public_key` against your merchant record once and reuse it on every
checkout for that merchant.

```bash theme={null}
# By your own reference (returns a list with the matching merchant)
curl "https://api.saturnshift.io/v1/merchants?psp_reference_id=YOUR-MERCHANT-REF" \
  -H "Authorization: Bearer <access_token>"

# Or directly by SaturnShift id (returns the merchant object)
curl https://api.saturnshift.io/v1/merchants/116 \
  -H "Authorization: Bearer <access_token>"
```

```json theme={null}
{
  "object": "merchant",
  "id": 116,
  "name": "Green Leaf Coffee",
  "public_key": "pk_live_29935fed5bc9878299588621772e0bc9",
  "psp_reference_id": "YOUR-MERCHANT-REF",
  "status": "ACTIVE"
}
```

## 2. Render the button

Load the checkout script and initialize it with the merchant's `publicKey`, the
order amount, and your own order id. Use a stable order id for both
`externalReference` and `idempotencyKey` so a retry of the same order dedupes
instead of creating a duplicate payment.

```html theme={null}
<button id="payWithCryptoBtn">Pay with crypto</button>

<script src="https://api.saturnshift.io/checkout.js"></script>
<script>
  const orderId = "ORDER-123"; // your stable order id

  SaturnShift.checkout({
    publicKey: "pk_live_29935fed5bc9878299588621772e0bc9", // the merchant's key
    amount: 1.00,
    currency: "USD",
    title: "Order #123",
    externalReference: orderId,
    idempotencyKey: orderId,
    redirectUrl: "https://your-portal.com/paylink/success",
    openInNewTab: true,   // opens the hosted checkout in a new page
    allowCrypto: true,
  }, "#payWithCryptoBtn");
</script>
```

The customer is taken to the SaturnShift hosted checkout to connect a wallet and
pay, then returned to your `redirectUrl`.

## 3. Verify the amount before you fulfill

<Warning>
  The amount is set in the browser, so you must confirm it server-side. When the
  `payment.paid` webhook arrives, check that the amount actually paid is at least
  your order amount, matched by `external_reference`, before you mark the order
  paid or fulfill it. Never fulfill on the event type alone.
</Warning>

```js theme={null}
// In your webhook handler, after verifying the signature (see Webhooks):
const event = JSON.parse(rawBody);

if (event.type === "payment.paid") {
  const order = await getOrder(event.data.external_reference);
  const paid = Number(event.data.amount.gross); // what the customer paid

  if (!order || paid < order.amount) {
    // Underpaid or unknown order. Do not fulfill; flag for review.
    return;
  }

  await markOrderPaid(order.id, event.data.id);
}
```

`data.amount.gross` is the amount the customer actually paid. `data.amount_status`
(`EXACT` / `UNDERPAID` / `OVERPAID`) tells you how it compared to what was
requested, but always compare against your own order amount as well, since the
requested amount originates in the browser.

See [Webhooks](/webhooks) for signature verification and the full event payload.
