Developers

Integration Guide

Take your first UPI payment, then wire up the webhook that makes it trustworthy.

Last updated: 31 October 2026

How a payment flows

There are three ways to accept a payment, in increasing order of control:

ApproachCode neededUse when
Payment linkNoneYou want a URL to share on WhatsApp, Instagram or an invoice
Browser SDKOne script tagYou have a website and want a checkout button fast
Server APIOne POST, one webhookProduction. Your server sets the amount, so a customer cannot change it.

The server flow, end to end:

  1. Your server calls POST /api/v1/payments with the order reference and amount.
  2. Cashvya returns a paymentId and a checkoutUrl.
  3. You send the customer to that URL, or open it in the SDK modal.
  4. The customer pays with any UPI app.
  5. Cashvya matches the bank credit notification to the payment and marks it successful.
  6. Cashvya POSTs a signed webhook to your endpoint. Your server fulfils the order.
  7. The money reaches your bank account in the next day's settlement.

Step 1 — Get your keys

Sign in to the dashboard and open Developers. You need two values:

ValueLooks likeWhere it goes
Secret API keycashvya_live_…Server only. Never in a web page or app.
Webhook signing secretwhsec_…Server only. Used to verify webhooks.
The key is shown once

Generating a key displays it a single time and immediately replaces any previous key. After that the dashboard shows only a masked form, and revealing it again asks for your account password. Store it in your server's environment, not in your repository.

Step 2 — Create a payment

Send the amount in rupees, as a string, with at most two decimals. The orderId is your own reference and must be unique per order.

curl -X POST https://cashvya.com/api/v1/payments \
  -H "Authorization: Bearer cashvya_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "ORDER-1042",
    "amount": "499.00",
    "currency": "INR",
    "customer": {
      "fullName": "Asha Kumar",
      "email": "[email protected]",
      "phoneNumber": "9876543210"
    },
    "returnUrl": "https://yourshop.com/thank-you",
    "cancelUrl": "https://yourshop.com/cart"
  }'

A 201 response:

{
  "paymentId": "7f3c1e62-...",
  "orderId": "ORDER-1042",
  "amount": "499.00",
  "amountPaise": "49900",
  "currency": "INR",
  "status": "INITIATED",
  "queuePosition": 0,
  "checkoutUrl": "https://cashvya.com/checkout/7f3c1e62-..."
}

Node example:

const res = await fetch('https://cashvya.com/api/v1/payments', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.CASHVYA_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ orderId: order.id, amount: order.total.toFixed(2) })
});
const payment = await res.json();
if (!res.ok) throw new Error(payment.error);

// Save the mapping now, so the webhook can find this order later.
await db.orders.update(order.id, { cashvyaPaymentId: payment.paymentId });
redirect(payment.checkoutUrl);

Step 3 — Send the customer to checkout

Either redirect to the checkoutUrl, or keep the customer on your page and open it in a modal with the SDK:

<script src="https://cashvya.com/cashvya.js"></script>
<script>
  Cashvya.pay({
    paymentId: '7f3c1e62-...',        // from your server
    onSuccess: r => { window.location = '/thank-you?order=' + r.order_id; },
    onCancelled: () => console.log('customer closed the checkout')
  });
</script>

See SDKs for every option.

Step 4 — Handle the webhook (the important step)

Never trust the browser

A success event in the browser is a user-interface hint. Anyone can fake it. Treat a payment as paid only after your server has verified a signed webhook, or has read the payment back from the API with your secret key.

Set your webhook URL in Dashboard → Developers. It must be a public HTTPS URL. Cashvya POSTs JSON and expects a 2xx within 10 seconds.

import express from 'express';
import crypto from 'node:crypto';

const app = express();

// The signature covers the exact bytes sent, so capture the RAW body.
app.post('/webhooks/cashvya', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = crypto
    .createHmac('sha256', process.env.CASHVYA_WEBHOOK_SECRET)
    .update(req.body)              // Buffer, untouched
    .digest('hex');

  const received = String(req.get('X-Gateway-Signature') || '').replace(/^sha256=/, '');

  const a = Buffer.from(expected, 'hex');
  const b = Buffer.from(received, 'hex');
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(400).send('bad signature');
  }

  const event = JSON.parse(req.body.toString('utf8'));

  if (event.event === 'SUCCESS') {
    // Idempotent: this may arrive more than once.
    fulfilOrder(event.orderId, { amount: event.amount, utr: event.utr });
  }

  res.sendStatus(200);   // acknowledge fast; do slow work in the background
});

Two rules that prevent almost every integration bug:

  • Be idempotent. A webhook can be delivered more than once. Key your fulfilment on orderId and do nothing if it is already fulfilled.
  • Check the amount. Compare event.amountPaise against what the order should cost before shipping anything.

Step 5 — Test it

Cashvya verifies payments against real bank notifications, so the honest way to test is a real payment of a small amount to yourself.

  1. Create a payment for "1.00".
  2. Pay it from your own UPI app.
  3. Watch the dashboard move the payment to SUCCESS, then confirm your endpoint received the webhook.
  4. Check Developers → Webhook deliveries for the response your server gave. A non-2xx is retried.
Going live checklist

Secret key in environment variables · webhook signature verified · fulfilment idempotent · amount checked · bank account and UPI ID saved in the dashboard so settlements are not held · two-factor authentication on your dashboard login.

Next