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

# Quickstart

> From zero to a live test payment in about fifteen minutes.

By the end you'll have taken a real payment on testnet and received a webhook for it.

<Info>
  **What you need:** a browser wallet (MetaMask, Rabby, or similar) and a few minutes. No credit card, no sales call.
</Info>

<Steps>
  <Step title="Sign in with your wallet" icon="wallet">
    Go to [cryptocheckout.ai/admin](https://www.cryptocheckout.ai/admin) and connect.

    There is no email or password. You sign a message proving you control the wallet, and that wallet becomes your account.

    <Warning>
      Use a wallet you intend to keep. It is your login and, unless you set a separate payout address, your settlement destination.
    </Warning>
  </Step>

  <Step title="Set your payout address" icon="bullseye">
    In **Settings → Payout**, set the address that should receive your money.

    This writes an attestation on-chain from your own wallet. Nobody else can make it — not us, not an attacker with our database. It becomes the root of the verification your customers' browsers perform.

    You can point it at a different wallet from the one you sign in with. A multisig or treasury wallet is a good choice.
  </Step>

  <Step title="Deploy your pool" icon="cube">
    Go to **Pool** and pick a chain. Press **Deploy**, and sign.

    <Columns cols={2}>
      <Card title="Why you sign it" icon="key">
        Because it makes the contract yours. We hand you a prepared transaction; you're the one who broadcasts it.
      </Card>

      <Card title="What it costs" icon="gas-pump">
        A one-time network fee — cents on most chains, more on Ethereum and TRON. The dashboard shows a live estimate before you sign.
      </Card>
    </Columns>

    Repeat for each chain you want to accept on. Your pool has the **same address on all seven EVM chains**, which makes reconciliation easier.

    <Note>
      <Note>
        Making pool deployment a prerequisite for API keys and the embed snippet is coming. Today you can integrate before deploying — but a pool is still required before the wallet-payment option appears on a chain, so deploy early. [Roadmap](/reference/roadmap).
      </Note>
    </Note>
  </Step>

  <Step title="Embed the checkout" icon="code">
    Copy the snippet from **API & SDK** in the dashboard. It looks like this:

    ```html Checkout embed theme={null}
    <script src="https://www.cryptocheckout.ai/sdk.js"
            integrity="sha384-cjNZpyXGzEF9lSh5wnh8mtsi3YU56F0n0bToTaUBqdDCVZ2AFw4GYqnxlSU3I+ob"
            crossorigin="anonymous"></script>

    <script>
      const checkout = CryptoCheckout.init({
        merchantId: "your-merchant-id",
        settlementAnchor: "0xYourPayoutWalletAddress",
        onPaymentConfirmed: (tx) => console.log("Paid", tx),
      });

      checkout.updateCart({ total: 49.00, currency: "EUR" });
      checkout.open();
    </script>
    ```

    <Warning>
      Keep both `integrity` and `settlementAnchor`.

      `integrity` means a tampered script fails to run rather than running altered. `settlementAnchor` is what the checkout verifies against the blockchain. Strip either and you lose a protection that exists specifically for you. [Why](/concepts/verification).
    </Warning>

    Copy the snippet from the dashboard rather than this page — yours has your real merchant ID and payout address filled in.
  </Step>

  <Step title="Take a test payment" icon="flask">
    Open your page and click through. Pick a testnet chain, and pay with testnet tokens from any public faucet.

    Watch for:

    * The checkout showing a **verified** badge before payment options appear
    * Your `onPaymentConfirmed` callback firing
    * The payment appearing in **Payments** in your dashboard

    [Full testing guide](/integration/testing).
  </Step>

  <Step title="Receive a webhook" icon="webhook">
    Browser callbacks are convenient but not trustworthy — a customer can close the tab. **Fulfil orders from webhooks.**

    In **Settings → Webhooks**, add your endpoint URL and copy the signing secret. Then verify every delivery:

    <CodeGroup>
      ```javascript Node.js theme={null}
      import crypto from "crypto";

      app.post("/webhooks/cryptocheckout",
        express.raw({ type: "application/json" }),
        (req, res) => {
          const header = req.headers["x-webhook-signature"]; // "t=…,v1=…"
          const [tPart, vPart] = header.split(",");
          const timestamp = tPart.split("=")[1];
          const signature = vPart.split("=")[1];

          const expected = crypto
            .createHmac("sha256", process.env.CC_WEBHOOK_SECRET)
            .update(`${timestamp}.${req.body}`)
            .digest("hex");

          if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
            return res.status(400).send("bad signature");
          }

          const event = JSON.parse(req.body);
          if (event.event === "payment_confirmed") {
            fulfilOrder(event.data);
          }
          res.sendStatus(200);
        });
      ```

      ```python Python theme={null}
      import hmac, hashlib, os
      from flask import request, abort

      @app.post("/webhooks/cryptocheckout")
      def webhook():
          header = request.headers["X-Webhook-Signature"]  # "t=…,v1=…"
          parts = dict(p.split("=", 1) for p in header.split(","))
          signed = f"{parts['t']}.{request.get_data(as_text=True)}"

          expected = hmac.new(
              os.environ["CC_WEBHOOK_SECRET"].encode(),
              signed.encode(),
              hashlib.sha256,
          ).hexdigest()

          if not hmac.compare_digest(parts["v1"], expected):
              abort(400)

          event = request.get_json()
          if event["event"] == "payment_confirmed":
              fulfil_order(event["data"])
          return "", 200
      ```
    </CodeGroup>

    <Warning>
      Sign the **raw request body**. Parsing and re-serialising JSON changes the bytes and the signature will never match.
    </Warning>

    [Webhook reference](/webhooks/overview).
  </Step>

  <Step title="Claim your money" icon="hand-holding-dollar">
    Go to **Pool** and press **Claim**. One signature moves your balance to your payout address.

    There's no minimum and no schedule. Batching a few payments into one claim keeps network fees down. [Claiming](/money/claiming).
  </Step>
</Steps>

## Then what

<Columns cols={3}>
  <Card title="Go-live checklist" icon="list-check" href="/get-started/go-live">
    What to confirm before real money.
  </Card>

  <Card title="Webhook events" icon="bolt" href="/webhooks/events">
    Every event and payload.
  </Card>

  <Card title="Edge cases" icon="triangle-exclamation" href="/money/edge-cases">
    Underpayments, wrong token, late arrivals.
  </Card>
</Columns>

## No-code option

Don't want to touch your site? Generate a **payment link** in the dashboard — a hosted checkout page you send to a customer. Same settlement, no integration. [Payment links](/integration/payment-links).
