@vouch.finance/sdk
v0.2.2
Published
TypeScript SDK for the Vouch Purpose-Bound Voucher platform — UPI QR redemption, payments, open-network merchant support
Maintainers
Readme
@vouch.finance/sdk
TypeScript SDK for the Vouch Purpose-Bound Voucher platform. Supports UPI QR voucher redemption, payment intents, voucher balance checks, and verification.
Two entry points:
@vouch.finance/sdk— core client, UPI parsing, framework-agnostic scanner@vouch.finance/sdk/react— React QR scanner component
Install
npm install @vouch.finance/sdkIf using the QR scanner, also install the optional peer dependencies:
# React apps using <QRScanner />
npm install react react-dom html5-qrcode
# Non-React apps using UPIScanner
npm install html5-qrcodeCDN (no bundler):
<script src="https://cdn.jsdelivr.net/npm/@vouch.finance/sdk/dist/index.global.js"></script>
<script>
const vouch = new Vouch.Vouch({
apiKey: "your-key",
baseUrl: "https://api.your-domain.com/api/v1",
});
</script>Configuration
Always set baseUrl explicitly in production. The SDK only falls back to a local dev default when omitted.
| Source | Used by | Example |
| --- | --- | --- |
| VouchConfig.baseUrl | All runtimes | https://sandbox.lk.vouch.finance/api/v1 |
| VOUCH_BASE_URL env | Node.js / tests | http://localhost:9393/api/v1 |
| DEFAULT_DEV_BASE_URL | Dev fallback only | http://localhost:9393/api/v1 |
import { Vouch, DEFAULT_DEV_BASE_URL } from "@vouch.finance/sdk";
const vouch = new Vouch({
apiKey: process.env.VOUCH_API_KEY!,
baseUrl: process.env.VOUCH_BASE_URL ?? DEFAULT_DEV_BASE_URL,
showVoucherOnlyWithBalance: true, // optional: gate UI on spendable balance
});Browser apps (Vite / React)
Avoid CORS and hardcoded hosts by proxying /api to your backend and using a relative SDK base URL:
# .env.local
VITE_BACKEND_URL=http://localhost:9393
VITE_VOUCH_BASE_URL=/api/v1
VITE_VOUCH_API_KEY=sk_test_...See examples/partner-test-ui/ for a full working setup.
E2E tests
cd sdk
VOUCH_API_KEY=sk_test_... \
VOUCH_BASE_URL=http://localhost:9393/api/v1 \
VOUCH_PROGRAM_ID=cmpo31oud0000vzmopcrfb9kx \
VOUCH_BENEFICIARY_PHONE=9953531809 \
npm run test:e2eQuick Start
import { Vouch } from "@vouch.finance/sdk";
const vouch = new Vouch({
apiKey: "sk_test_...",
baseUrl: "https://api.your-domain.com/api/v1",
});Voucher balance gate (partner apps)
Before showing voucher UI, check whether the beneficiary has spendable balance:
const balance = await vouch.vouchers.getBalance({
beneficiaryPhone: "9953531809",
programId: "cmpo31oud0000vzmopcrfb9kx",
});
// balance.hasVoucher, balance.hasBalance, balance.remainingBalanceMinor, balance.tokenId
const showUi = await vouch.vouchers.shouldShowUI({
beneficiaryPhone: "9953531809",
programId: "cmpo31oud0000vzmopcrfb9kx",
});
// true only when showVoucherOnlyWithBalance is set and hasBalance is trueUPI Voucher Payment Flow
The primary integration for partners building beneficiary-facing apps. The partner identifies the beneficiary by phone number, scans a merchant UPI QR, and completes payment using the voucher.
Step 1 — Scan merchant UPI QR
import { parseUPIScheme } from "@vouch.finance/sdk";
const upi = parseUPIScheme("upi://pay?pa=pharmacy@upi&pn=City+Pharmacy&am=250.00");
if (!upi) throw new Error("Not a valid UPI QR");
// upi = {
// type: "upi",
// upiId: "pharmacy@upi",
// merchantName: "City Pharmacy",
// amount: "250.00",
// mcc: "5912",
// raw: "upi://pay?..."
// }Or use the scanner components (see QR Scanner section below).
Step 2 — Create intent and get quote
const intent = await vouch.payments.initiateUPI({
upiData: upi,
beneficiaryPhone: "+919876543210",
programId: "cmpo31oud0000vzmopcrfb9kx",
});Step 3 — Show payment review
Display the quote to the beneficiary. Proceed only if status === "QUOTED" and eligibleAmount > 0.
Step 4 — Authorize payment
const auth = await vouch.payments.authorize({ intentId: intent.intentId });Step 5 — Poll for settlement
const status = await vouch.payments.getIntent(intent.intentId);
// status.status — "AUTHORIZED" | "SETTLED" | "FAILED"Open-Network UPI (Unregistered Merchants)
Payments work to any UPI merchant — registered or not. Unregistered merchants return merchantResolution: "open_network" and settle via fiat UPI (settlementRail: "fiat").
const intent = await vouch.payments.initiateUPI({
upiData: upi,
beneficiaryPhone: "+919876543210",
programId: "cmpo31oud0000vzmopcrfb9kx",
});
if (intent.merchantResolution === "open_network") {
console.log("Paying via UPI to:", intent.upiId);
}QR Scanner
React
import { QRScanner } from "@vouch.finance/sdk/react";
<QRScanner
onScan={(upi) => console.log(upi.upiId, upi.amount)}
onError={(err) => console.error(err)}
onClose={() => setOpen(false)}
width={320}
height={320}
/>Vanilla JS (any framework)
import { UPIScanner } from "@vouch.finance/sdk";
const scanner = new UPIScanner({
container: "#scanner",
onScan: (upi) => console.log(upi.upiId),
onError: (err) => console.error(err),
});
await scanner.start();API Reference
Client
new Vouch({
apiKey,
baseUrl?, // required in production
version?, // default "2026-04-06"
showVoucherOnlyWithBalance?, // default false
})All requests include x-api-key and Vouch-Version headers.
Programs
| Method | Return |
|--------|--------|
| vouch.programs.list() | { programs: Program[] } |
| vouch.programs.get(slug) | { program: Program } |
| vouch.programs.create(params) | { data: { program }, meta } |
| vouch.programs.stats(slug) | { data: { stats }, meta } |
| vouch.programs.enrol(slug, { phone, fields? }) | { data, meta } |
Payments
| Method | Return |
|--------|--------|
| vouch.payments.initiateUPI({ upiData, programId, beneficiaryPhone?, beneficiaryId?, tokenId? }) | UPIIntentResponse |
| vouch.payments.createIntent({ merchantId, items, programId? }) | { data: PaymentIntent, meta } |
| vouch.payments.quote({ intentId, tokenId }) | { data, meta } |
| vouch.payments.authorize({ intentId }) | AuthorizeResponse |
| vouch.payments.getIntent(intentId) | IntentStatusResponse |
Vouchers & Merchants
| Method | Return |
|--------|--------|
| vouch.vouchers.verify(token, programId?) | { data: Voucher, meta } |
| vouch.vouchers.getBalance({ beneficiaryPhone, programId }) | VoucherBalanceStatus |
| vouch.vouchers.shouldShowUI({ beneficiaryPhone, programId }) | boolean |
| vouch.merchants.list(programId?) | { data: Merchant[], meta, pagination } |
Constants
| Export | Description |
|--------|-------------|
| DEFAULT_DEV_BASE_URL | http://localhost:9393/api/v1 — local dev fallback |
| DEFAULT_DEV_BACKEND_ORIGIN | http://localhost:9393 — for Vite proxy config |
| resolveBaseUrl(explicit?) | Config → VOUCH_BASE_URL → dev default |
Types
| Type | Description |
|------|-------------|
| VouchConfig | apiKey, baseUrl?, version?, showVoucherOnlyWithBalance? |
| VoucherBalanceStatus | Response from getBalance — hasVoucher, hasBalance, remainingBalanceMinor, tokenId, currency, etc. |
| UPIData | Parsed UPI QR (upiId, merchantName?, amount?, mcc?) |
| UPIIntentResponse | Response from initiateUPI |
| AuthorizeResponse | Response from authorize |
| IntentStatusResponse | Response from getIntent |
| VouchError | code, statusCode, requestId, message |
Error Handling
import { VouchError } from "@vouch.finance/sdk";
try {
await vouch.payments.authorize({ intentId: "bad" });
} catch (err) {
if (err instanceof VouchError) {
console.log(err.code, err.statusCode, err.requestId, err.message);
}
}Examples
| App | Port | Description |
| --- | --- | --- |
| examples/partner-test-ui/ | 5182 | Dvara flow: balance gate + UPI scan (recommended) |
| examples/react-app/ | 5180 | Minimal React + <QRScanner /> |
| examples/vanilla/ | — | CDN HTML, no build step |
cd sdk && npm run build
# Partner test UI
cd examples/partner-test-ui
cp .env.local.example .env.local
npm install && npm run dev
# React example
cd examples/react-app
cp .env.local.example .env.local
npm install && npm run devPublishing
cd sdk
npm install
npm run build
NPM_TOKEN=... ./scripts/publish.shBuild outputs: dist/index.js (CJS), dist/index.mjs (ESM), dist/index.global.js (IIFE/CDN), dist/react/ (React entry).
