Fragfrag
Quickstart

Ship Frag in 10 minutes

Copy every block in order. When you finish, you're accepting any token on any chain and settling in one.

STEP 0· 30 sec

Prerequisites

  • Node 18+ (or Bun / Deno / Cloudflare Workers)
  • A Frag account — sign in
  • A settlement wallet address (EVM or Solana) you control
STEP 1· 2 min

Get your keys

  1. Open Dashboard → API Keys
  2. Copy pk_test_… (publishable) and sk_test_… (secret)
  3. Create a webhook endpoint and copy its signing secret
STEP 2· 1 min

Set environment variables

snippet.tsx
# .env (server)
FRAG_SECRET_KEY=sk_test_xxx
FRAG_WEBHOOK_SECRET=whsec_xxx
FRAG_API_URL=https://frag.cash   # optional

# .env (client)
VITE_FRAG_PUBLIC_KEY=pk_test_xxx
NameScopePurpose
FRAG_SECRET_KEYserverAuth for REST calls
FRAG_WEBHOOK_SECRETserverVerify inbound webhooks
VITE_FRAG_PUBLIC_KEYclientMount <FragmentPayCheckout>

Never expose FRAG_SECRET_KEY to the browser.

STEP 3· 30 sec

Install the SDKs

snippet.tsx
npm install @fragmentpay/server @fragmentpay/react
# or: bun add / pnpm add / yarn add
STEP 4· 2 min

Create a payment intent (server)

snippet.tsx
import { FragmentPay } from "@fragmentpay/server";

const frag = new FragmentPay({ apiKey: process.env.FRAG_SECRET_KEY! });

export async function createCheckout(orderId: string, amountUsd: number) {
  return frag.paymentIntents.create(
    {
      amount: amountUsd,
      currency: "USD",
      settlement: {
        chain: "robinhood",
        token: "USDC",
        address: process.env.MERCHANT_WALLET!,
      },
      // Vending-machine by default — any supported token on any supported
      // chain is accepted and auto-swapped to your settlement token.
      metadata: { orderId },
    },
    { idempotencyKey: `order:${orderId}` }     // safe to retry
  );
}
STEP 5· 2 min

Mount the checkout widget (client)

snippet.tsx
import {
  FragmentPayProvider,
  FragmentPayCheckout,
} from "@fragmentpay/react";

export default function CheckoutPage({ intentId }: { intentId: string }) {
  return (
    <FragmentPayProvider publicKey={import.meta.env.VITE_FRAG_PUBLIC_KEY}>
      <FragmentPayCheckout
        intentId={intentId}
        onSettled={(i) => {
          window.location.href = `/thanks/${i.metadata.orderId}`;
        }}
        onError={(err) => console.error(err)}
      />
    </FragmentPayProvider>
  );
}

Handles Reown/WalletConnect, multi-token contributions, quotes, retries, and confirmations automatically.

STEP 6· 2 min

Verify webhooks (server)

snippet.tsx
import { verifyWebhook } from "@fragmentpay/server";

export async function POST(req: Request) {
  const body = await req.text();                     // RAW body, not JSON.stringify
  const sig = req.headers.get("frag-signature")!;

  const event = await verifyWebhook({
    payload: body,
    signature: sig,
    secret: process.env.FRAG_WEBHOOK_SECRET!,
  });

  switch (event.type) {
    case "payment_intent.settled":
      await fulfillOrder(event.data.metadata.orderId);
      break;
    case "payment_intent.expired":
    case "payment_intent.failed":
      await releaseInventory(event.data.metadata.orderId);
      break;
  }
  return new Response("ok");
}

Test with Developers → Webhooks → Send test event.

STEP 7· 30 sec

Go live

  1. Swap pk_test_ / sk_test_pk_live_ / sk_live_
  2. Update FRAG_WEBHOOK_SECRET to the live endpoint's secret
  3. Toggle Live mode in the dashboard header

Troubleshooting

SymptomFix
401 UnauthorizedWrong key, or test-vs-live mismatch
Widget stuck loadingpublicKey missing or mismatched env
Bad signatureUse raw body, verify secret matches endpoint
429SDK auto-retries with Retry-After
Read the full docs →API reference →Going live checklist →