@babuperumana/upipg-new
v1.0.0
Published
Full-featured UPI payment gateway with automated BharatPe verification, SQLite persistence, branded QR codes, and payer VPA capture
Maintainers
Readme
@babuperumana/upipg-new
Full-featured UPI payment gateway for Node.js with automated BharatPe verification, SQLite persistence, branded QR codes, and payer VPA capture. No webhooks or public servers needed.
Features
- Automated payment verification — Background polls BharatPe every 5 seconds; no webhooks or public IP required
- Payer VPA capture — Automatically fetches the payer's UPI ID (
person@okaxis) via BharatPe's transaction detail endpoint - Micro-amount collision avoidance — Unique fractional amounts (₹100.00 → ₹100.03) so multiple customers can pay simultaneously
- Branded QR codes — Generates high-resolution PNG QR cards via the
upi://paydeep-link URI - SQLite persistence — All merchants, payments, events, and polling logs stored locally
- Event-driven architecture — Listen for
success,expired, andcredentials_expiredevents - REST API + Dashboard — Full Express API with a built-in dark-mode dashboard and mobile-friendly payment status pages
- SSE live events — Real-time payment status stream via Server-Sent Events
- Credential hot-reload — Update BharatPe API token/cookie without restarting
- Multi-merchant — Register multiple merchants; each polls independently
How It Works
Customer opens payment link
│
▼
Server creates order + generates QR / UPI link
│
▼
Customer pays via any UPI app (GPay, PhonePe, BHIM, PayTM...)
│
▼
BharatPe records the transaction on merchant's account
│
▼
Background poller (every 5s) fetches recent QR transactions
│
▼
Matches by amount + time window
│
▼
Fetches transaction detail → captures payer VPA, UTR, payer name, handle
│
▼
Updates SQLite DB → emits `success` event → payment page auto-refreshesInstallation
npm install @babuperumana/upipg-newPrerequisites
- A BharatPe merchant account with QR payments enabled
- Your BharatPe API token and cookie (obtain via browser DevTools → Network tab on the BharatPe merchant dashboard)
Quick Start
1. Set up environment
Copy .env.example to .env (optional — defaults work for local use):
PORT=3000
HOST=0.0.0.0
DB_PATH=./data/upi-gateway.db2. Register a merchant
node scripts/setup.jsOr with command-line flags:
node scripts/setup.js \
--name "Kaippulli Temple" \
--upi "BHARATPE.8S0S0J3C6E51057@fbpe" \
--mid "49354135" \
--token "4dac28e239f3467fa3e96305f1adeb60" \
--cookie "eyJpdiI6..." \
--poll 5000 \
--timeout 3003. Start the server
npm startServer runs at http://localhost:3000
4. Create a payment
curl -X POST http://localhost:3000/payments \
-H "Content-Type: application/json" \
-d '{"merchant_id": 1, "amount": 100}'Response:
{
"order_id": "ORD_1786177314232_886",
"session_amount": 100,
"status": "PENDING",
"qr_code_url": "http://localhost:3000/qr/ORD_1786177314232_886",
"upi_uri": "upi://pay?pa=...&am=100.00&tn=ORD_1786177314232_886",
"status_url": "http://localhost:3000/pay/ORD_1786177314232_886"
}5. Share with payer
Share any of these with the payer:
- UPI deep-link:
upi://pay?pa=...(opens their UPI app directly) - QR image:
http://localhost:3000/qr/:orderId - Status page:
http://localhost:3000/pay/:orderId(mobile-friendly, auto-refreshes)
The poller will auto-detect the payment within 5–10 seconds and capture VPA + UTR.
API Reference
Health
GET /healthReturns server status, active merchant count, and payment counts by status.
Merchants
GET /merchants List all active merchants
GET /merchants/:id Get merchant details (credentials excluded)
POST /merchants Register new merchant
PUT /merchants/:id Update merchant
DELETE /merchants/:id Deactivate merchant
GET /merchants/:id/credentials/check Verify BharatPe credentials
POST /merchants/:id/credentials/update Update credentials live
POST /merchants/:id/poll Run one verification pass manually
GET /merchants/:id/poll-logs Recent polling logsPayments
POST /payments Create payment order
GET /payments/:orderId Get payment details + events
GET /payments List all payments (filterable)
GET /merchants/:merchantId/payments Merchant payments (paginated)
POST /payments/:orderId/enrich Backfill payer VPA for old paymentsQR & UPI
GET /qr/:orderId QR code PNG image
GET /payments/:orderId/qr QR code PNG (download)
GET /payments/:orderId/qr/uri UPI deep-link URIPayment Status Page
GET /pay/:orderId Mobile-friendly payment page (auto-refreshes)Events
GET /events/:orderId Event timeline for a payment
GET /events/stream SSE live event streamGateway
GET /gateway/status All registered gateway statusesPayment Object
{
"order_id": "ORD_1786177314232_886",
"base_amount": 100,
"session_amount": 100,
"status": "SUCCESS",
"utr": "658078651930",
"payer_vpa": "perumanababu-1@okaxis",
"payer_name": "Babu Perumana",
"payer_handle": "Google Pay",
"metadata": {"purpose": "donation"},
"created_at": "2026-08-08 08:21:54",
"completed_at": "2026-08-08 08:25:12",
"merchant": {
"name": "Kaippulli Temple",
"upi_id": "BHARATPE.8S0S0J3C6E51057@fbpe"
}
}Payment Status Values
| Status | Description |
|---|---|
| PENDING | Awaiting payment |
| SUCCESS | Payment verified by BharatPe |
| FAILURE | Payment failed or expired |
| EXPIRED | Session timed out (default 5 minutes) |
Events
Every state change is logged:
[
{
"event_type": "created",
"payload": {"orderId": "...", "baseAmount": 100, "sessionAmount": 100},
"created_at": "2026-08-08 08:21:54"
},
{
"event_type": "success",
"payload": {"utr": "658078651930", "payerVpa": "perumanababu-1@okaxis", "payerName": "Babu Perumana", "payerHandle": "Google Pay"},
"created_at": "2026-08-08 08:25:12"
}
]Programmatic Usage
const { paymentService, qr, db } = require('@babuperumana/upipg-new');
// Create a payment
const session = await paymentService.createPayment(1, {
amount: 100,
metadata: { customerId: 'CUST_001' }
});
// Listen for events
paymentService.on('payment:success', ({ orderId, payerVpa }) => {
console.log(`Payment ${orderId} from ${payerVpa}`);
});
paymentService.on('payment:expired', ({ orderId }) => {
console.log(`Payment ${orderId} expired`);
});
paymentService.on('credentials:expired', ({ merchantId }) => {
console.log(`Merchant ${merchantId} credentials expired`);
});
// Generate QR
const buffer = await qr.generateQR('merchant@bank', 'My Store', 100, 'ORD_123');
const uri = qr.buildUpiUri('merchant@bank', 'My Store', 100, 'ORD_123');
// Direct DB access
const payment = db.getPaymentByOrderId('ORD_123');Configuration
Merchant Config
| Field | Required | Description |
|---|---|---|
| name | Yes | Merchant display name |
| upi_id | Yes | Merchant UPI ID |
| merchant_id | Yes | BharatPe merchant ID |
| api_token | Yes | BharatPe API session token |
| api_cookie | Yes | BharatPe cookie string |
| bharatpe_api | No | Custom BharatPe API endpoint |
| user_agent | No | Custom User-Agent header |
| poll_interval | No | Polling interval in ms (default: 5000) |
| timeout | No | Session timeout in seconds (default: 300) |
| min_amount | No | Minimum payment amount in INR (default: 1.0) |
| max_amount | No | Maximum payment amount in INR (default: 50000.0) |
Environment Variables
| Variable | Default | Description |
|---|---|---|
| PORT | 3000 | Server port |
| HOST | 0.0.0.0 | Server bind address |
| DB_PATH | ./data/upi-gateway.db | SQLite database path |
Database Schema
merchants
├── id, name, upi_id, merchant_id
├── api_token, api_cookie, bharatpe_api
├── poll_interval, timeout, min_amount, max_amount
├── is_active, created_at, updated_at
payments
├── id, order_id (unique), merchant_id
├── amount, session_amount, status
├── utr, payer_vpa, payer_name, payer_handle
├── metadata, created_at, completed_at
events
├── id, payment_id, event_type, payload, created_at
polling_log
├── id, merchant_id, status
├── transactions_checked, matched, error, created_atDeployment
With PM2 (recommended for production)
npm install -g pm2
pm2 start src/server.js --name upi-gateway
pm2 save
pm2 startupWith Docker
FROM node:20-alpine
RUN apk add --no-cache python3 make g++
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY src/ ./src/
COPY scripts/ ./scripts/
COPY .env.example ./.env
EXPOSE 3000
CMD ["npm", "start"]Environment notes
- Set
HOST=0.0.0.0to accept external connections - Set
PORTto your desired port - The SQLite DB file is created automatically at
DB_PATH - For production, use a process manager like PM2 or systemd
How BharatPe Credentials Are Obtained
- Log into your BharatPe merchant dashboard
- Open Browser DevTools → Network
- Navigate to the transactions page
- Find any API request to
payments-tesseract.bharatpe.in - Copy the
tokenandCookieheaders
These credentials are stored in the SQLite database and used for all polling.
Architecture
src/
index.js → Public package entry point
server.js → Express REST API + dashboard + SSE
paymentService.js → UpiPG wrapper, event handlers, VPA enrichment
database.js → SQLite layer (better-sqlite3)
qrHelper.js → QR code & UPI deep-link generation
scripts/
setup.js → Interactive merchant registration CLILicense
MIT
