# Your first integration

## 1. Prepare your account

Retrieve your API key from the [merchant dashboard](https://dashboard.niripay.com). Add your backend server’s public egress IP to **API Key & IP Whitelist**. Your merchant account must be approved.

Keep the key on your server. Never include it in frontend bundles or mobile applications.

## 2. Create a payment

Send a unique merchant `transaction_id`, an INR `amount`, and your `webhook_url` to the [create deposit endpoint](/create-deposit/). Persist your request and the returned identifiers before redirecting your customer.

Use the interactive request example generator on the website, or copy the [create deposit examples](/create-deposit/). No request is sent by the generator.

## 3. Open hosted checkout

Redirect the customer to `data.payment_page` from the create response. Checkout handles the available payment options and any required evidence submission.

A return to your `return_url` is navigation, not proof that a payment succeeded.

## 4. Verify the notification

Receive the webhook on your server. Verify its HMAC-SHA256 signature against the exact raw body, check the timestamp, and store the event idempotently. See [webhook verification](/webhooks/) for a complete Node.js example.

## 5. Reconcile the payment

Query [deposit status](/deposit-status/) with your original transaction ID. Only fulfill the order when its payment status is `success`. Apply each financial effect once, including when a callback repeats.

```js
const response = await fetch(
  "https://api.niripay.com/api/v1/merchant/deposits/status",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NIRIPAY_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ transaction_id: "ORDER-2026-001" })
  }
);
const result = await response.json();
if (!response.ok || result.status !== "success") {
  throw new Error(result.message || "Status lookup failed");
}
// An accepted status lookup may still describe a pending payment.
if (result.data.status === "success") {
  // Persist payment and fulfillment idempotently in your database.
}
```

## If a response is delayed

Keep the original transaction ID and check its status. Do not blindly create a replacement payment or payout after a timeout. Use capped backoff for polling and escalate unresolved records with sanitized details.
