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

# Set up

> Add a crypto payment button to your website in minutes

<Steps>
  <Step title="Configure payment notifications" titleSize="h2">
    Choose how you want to be notified when payments are received.

    ### Option A: Webhooks (Recommended)

    Configure a webhook to receive real-time updates on payment status. This is ideal for automated systems and immediate order fulfillment.

    1. Go to your [Webhook Configuration](https://dashboard.chipipay.com/configure/webhooks) page
    2. Add your endpoint URL
    3. Select the events you want to receive
    4. Save your configuration

    ### Option B: Email notifications

    Set up email notifications for manual order processing and simple setups.

    1. Visit the [Notification Settings](https://dashboard.chipipay.com/configure/notifications) page
    2. Enter your email address
    3. Save your settings
  </Step>

  <Step title="Get your merchant wallet address" titleSize="h2">
    Your merchant wallet address is where all payments will be sent.

    1. Navigate to the [Merchant Configuration](https://dashboard.chipipay.com/configure/merchant) page in your dashboard and copy your Merchant Wallet Address

    <Warning>
      Keep your wallet address secure and only share it through your payment button implementation.
    </Warning>
  </Step>

  <Step title="Add the payment button to your website" titleSize="h2">
    Choose your preferred implementation method:

    <Tabs>
      <Tab title="Next.js">
        Perfect for Next.js applications.

        ```typescript theme={null}

        "use client";

        import { useState, useCallback } from "react";
        import { Button } from "@/components/ui/button";
        import { Input } from "@/components/ui/input";
        import { toast } from "sonner";
        import { useAuth } from "@clerk/nextjs";
        import { useWalletWithAuth } from "@/hooks/useWalletWithAuth";
        import { useTransfer } from "@/hooks/useTransfer";
        import { useRecordSendTransaction } from "@/hooks/useRecordSendTransaction";
        import type { WalletData, Chain, ChainToken } from "@chipi-stack/types";

        export function PayCryptoButton({
        costumerWallet,
        amountUsd,
        onSuccess,
        label,
        }: {
        costumerWallet: WalletData;
        amountUsd: number;
        onSuccess?: (txHash: string) => void;
        label: string;
        }) {
        const { getToken } = useAuth();
        const { wallet: senderWallet } = useWalletWithAuth();
        const { transferAsync } = useTransfer();
        const { recordSendTransactionAsync } = useRecordSendTransaction();

        const [pin, setPin] = useState("");
        const [busy, setBusy] = useState(false);

        const runPayment = useCallback(
          async (pin: string) => {
            const jwtToken = await getToken();
            if (!jwtToken) {
              toast.error("No token found");
              return;
            }

            const merchant = process.env.NEXT_PUBLIC_MERCHANT_WALLET || "";
            if (!merchant) {
              toast.error("Merchant wallet is not configured");
              return;
            }

            try {
              setBusy(true);
              const transactionHash = await transferAsync({
                params: {
                  amount: amountUsd.toString(),
                  encryptKey: pin,
                  wallet: costumerWallet,
                  token: ChainToken.USDC,
                  recipient: merchant,
                },
                bearerToken: jwtToken,
              });

              const recordSendTransactionParams = {
                transactionHash,
                chain: Chain.STARKNET,
                expectedSender: senderWallet?.publicKey || "",
                expectedRecipient: merchant,
                expectedToken: ChainToken.USDC,
                expectedAmount: amountUsd.toString(),
              };

              const sendTx = await recordSendTransactionAsync({
                params: recordSendTransactionParams,
                bearerToken: jwtToken,
              });

              console.log("successful record send transaction:", sendTx);
              toast.success("Pago completado ✨", { position: "bottom-center" });
              onSuccess?.(transactionHash);
            } catch (err: unknown) {
              console.error("Error in pay with chipi button:", err);
              const msg =
                (typeof err === "string" && err) ||
                (err instanceof Error ? err.message : "Something went wrong. Please try again.");
              toast.error(msg, { position: "bottom-center" });
            } finally {
              setBusy(false);
            }
          },
          [
            amountUsd,
            costumerWallet,
            getToken,
            recordSendTransactionAsync,
            senderWallet?.publicKey,
            transferAsync,
            onSuccess,
          ]
        );

        return (
          <div className="flex flex-col gap-3 w-full max-w-sm">
            <label className="font-medium">{label}</label>
            <Input
              type="password"
              placeholder="Enter PIN"
              value={pin}
              onChange={(e) => setPin(e.target.value)}
            />
            <Button
              className="w-full"
              disabled={busy}
              onClick={() => runPayment(pin)}
            >
              {busy ? "Processing..." : "Pay Now"}
            </Button>
          </div>
        );
        }

        ```

        <Tip>
          The `usdAmount` parameter is optional. If you don't include it, customers can choose how much they want to pay, making it perfect for donations or flexible pricing.
        </Tip>
      </Tab>
    </Tabs>
  </Step>

  <Step title="Handle webhook notifications" titleSize="h2">
    When a payment is successful, you'll receive a webhook notification with the transaction details.

    ### Webhook payload schema

    <ParamField path="event" type="string" required>
      The event type. Currently only `"transaction.sent"` is supported.
    </ParamField>

    <ParamField path="data" type="object" required>
      Container object for the transaction data.

      <Expandable title="data properties">
        <ParamField path="transaction" type="object" required>
          The transaction details.

          <Expandable title="transaction properties">
            <ParamField path="id" type="string" required>
              Unique transaction identifier (e.g., `"transaction-1ad902fe-fb48-434f-af9c-c23c170b47b4"`)
            </ParamField>

            <ParamField path="chain" type="string" required>
              The blockchain network. Always `"STARKNET"` for ChipiPay.
            </ParamField>

            <ParamField path="apiPublicKey" type="string" required>
              Your ChipiPay API public key (e.g., `"pk_prod_7726eff1cf6ce2e0291fa61e0c08b8c2"`)
            </ParamField>

            <ParamField path="transactionHash" type="string" required>
              The Starknet transaction hash for verification on-chain
            </ParamField>

            <ParamField path="blockNumber" type="number" required>
              Block number where the transaction was included. `-1` if pending.
            </ParamField>

            <ParamField path="senderAddress" type="string" required>
              The wallet address that sent the payment
            </ParamField>

            <ParamField path="destinationAddress" type="string" required>
              Your merchant wallet address that received the payment
            </ParamField>

            <ParamField path="amount" type="string" required>
              Payment amount in the smallest token unit (e.g., `"100000"` = 0.1 USDC)
            </ParamField>

            <ParamField path="token" type="string" required>
              Token type. Currently always `"USDC"`.
            </ParamField>

            <ParamField path="calledFunction" type="string">
              The contract function called. Usually `"send"`.
            </ParamField>

            <ParamField path="amountInUSD" type="number" required>
              USD equivalent of the payment amount
            </ParamField>

            <ParamField path="status" type="string" required>
              Transaction status. `"SUCCESS"` indicates a completed payment.
            </ParamField>

            <ParamField path="isChipiWallet" type="boolean" required>
              Whether the sender used a ChipiPay managed wallet
            </ParamField>

            <ParamField path="createdAt" type="string" required>
              ISO timestamp when the transaction was first detected
            </ParamField>

            <ParamField path="updatedAt" type="string" required>
              ISO timestamp when the transaction was last updated
            </ParamField>

            <ParamField path="senderWalletId" type="string | null">
              ChipiPay wallet ID if sender used a managed wallet
            </ParamField>

            <ParamField path="destinationWalletId" type="string | null">
              ChipiPay wallet ID if destination is a managed wallet
            </ParamField>

            <ParamField path="chipiWalletId" type="string | null">
              Legacy field for ChipiPay wallet identification
            </ParamField>
          </Expandable>
        </ParamField>
      </Expandable>
    </ParamField>

    ### Webhook signature verification

    <Warning>
      For security, all webhooks include an HMAC signature in the `chipi-signature` header. You should verify this signature to ensure the webhook is from ChipiPay.
    </Warning>

    1. Get your webhook signing secret from your [webhook configuration](https://dashboard.chipipay.com/configure/webhooks) page (it looks like `whsec_****`)
    2. Create a webhook handler in your Next.js app:

    ```typescript theme={null}
    // app/api/webhooks/route.ts
    import { createHmac, timingSafeEqual } from 'crypto';
    import { NextRequest, NextResponse } from 'next/server';

    function verifyWebhookSignature(
      payload: string,
      receivedSignature: string,
      secretKey: string
    ): boolean {
      try {
        const expectedSignature = createHmac("sha256", secretKey)
          .update(payload)
          .digest("hex");

        // Use timing-safe comparison to prevent timing attacks
        return timingSafeEqual(
          Buffer.from(expectedSignature, "hex"),
          Buffer.from(receivedSignature, "hex")
        );
      } catch (error) {
        return false;
      }
    }

    export async function POST(request: NextRequest) {
      const payload = await request.text();
      const signature = request.headers.get('chipi-signature');
      const webhookSecret = process.env.CHIPI_WEBHOOK_SECRET!; // Your whsec_**** key

      if (!signature || !verifyWebhookSignature(payload, signature, webhookSecret)) {
        return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
      }

      const event = JSON.parse(payload);

      if (event.event === 'transaction.sent' && event.data.transaction.status === 'SUCCESS') {
        // Handle successful payment
        const transaction = event.data.transaction;
        
        // Update your database, send confirmation emails, fulfill orders, etc.
        console.log(`Payment received: ${transaction.amount} USDC from ${transaction.senderAddress}`);
      }

      return NextResponse.json({ received: true });
    }
    ```
  </Step>

  <Step title="Celebrate" titleSize="h2">
    Your integration is now ready for production!
  </Step>
</Steps>
