> ## 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

## 1. Configure payment notifications

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

## 2. Get your merchant wallet address

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.

## 3. Add the payment button to your website

Choose your preferred implementation method:

**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.

## 4. Handle webhook notifications

When a payment is successful, you'll receive a webhook notification with the transaction details.

### Webhook payload schema

**event** `string` *required*

The event type. Currently only `"transaction.sent"` is supported.

**data** `object` *required*

Container object for the transaction data.

**data properties**

**transaction** `object` *required*

The transaction details.

**transaction properties**

**id** `string` *required*

Unique transaction identifier (e.g., `"transaction-1ad902fe-fb48-434f-af9c-c23c170b47b4"`)

**chain** `string` *required*

The blockchain network. Always `"STARKNET"` for ChipiPay.

**apiPublicKey** `string` *required*

Your ChipiPay API public key (e.g., `"pk_prod_7726eff1cf6ce2e0291fa61e0c08b8c2"`)

**transactionHash** `string` *required*

The Starknet transaction hash for verification on-chain

**blockNumber** `number` *required*

Block number where the transaction was included. `-1` if pending.

**senderAddress** `string` *required*

The wallet address that sent the payment

**destinationAddress** `string` *required*

Your merchant wallet address that received the payment

**amount** `string` *required*

Payment amount in the smallest token unit (e.g., `"100000"` = 0.1 USDC)

**token** `string` *required*

Token type. Currently always `"USDC"`.

**calledFunction** `string`

The contract function called. Usually `"send"`.

**amountInUSD** `number` *required*

USD equivalent of the payment amount

**status** `string` *required*

Transaction status. `"SUCCESS"` indicates a completed payment.

**isChipiWallet** `boolean` *required*

Whether the sender used a ChipiPay managed wallet

**createdAt** `string` *required*

ISO timestamp when the transaction was first detected

**updatedAt** `string` *required*

ISO timestamp when the transaction was last updated

**senderWalletId** `string | null`

ChipiPay wallet ID if sender used a managed wallet

**destinationWalletId** `string | null`

ChipiPay wallet ID if destination is a managed wallet

**chipiWalletId** `string | null`

Legacy field for ChipiPay wallet identification

### 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.

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 });
}
```

## 5. Celebrate

Your integration is now ready for production!


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.