Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vyapar Hub · Merchant Console for Outgoing Business UPI Payments

A desktop web console that gives a merchant one place to run every payment they send out: suppliers, rent, utilities, logistics, fuel and salaries. Saved payees drive one-approval bulk payouts, every payment is auto-tagged into a GST-ready ledger, and a working-capital line sized from the merchant's own QR collections covers the gap when payables land before receivables.

Built as a submission for the Paytm Innovation Challenge 2026 (UPI Growth Challenge, Track B: Grow Paytm Usage Among Merchants as UPI Payers).


The Problem

Merchants already receive on Paytm. The soundbox is on the counter and the QR is on the wall. But when the same merchant pays their wholesaler, their landlord or their delivery staff, the money usually leaves through PhonePe, Google Pay or a bank app.

That outgoing side is a large, largely uncontested pool of UPI GMV, and the reasons it goes elsewhere are rarely about Paytm failing. They are about habit, about the supplier's printed QR deciding the rail, and about the fact that nobody has given the merchant a reason to consolidate.

Vyapar Hub is that reason. The wedge is that the credit limit and the reconciliation suggestions are both derived from inflow data Paytm already holds. A UPI app without the merchant's collection history cannot offer either, which makes the differentiation compounding rather than cashback-led.


Setup and Run Instructions

Prerequisites

  • Node.js 20+
  • npm 10+

No backend, no database, no API keys, no network calls. Everything runs client-side.

1. Install and run

npm install
npm run dev

Console UI: http://localhost:5173

2. Other commands

npm run build     # production bundle into dist/
npm run preview   # serve the production build
npm test          # 54 unit tests over the computed logic and state machine
npm run voc       # regenerate the synthetic VOC workbook in voc/
npm run lint

State lives in React Context and persists to localStorage. Reset demo data on the Profile screen restores the seeded state at any point.


Approach & Architecture

High-Level Data Flow

Deterministic Seed Generator  (data/seed.js)
  │   two merchant profiles, 60 days of vendors,
  │   transactions and QR collections, PRNG-seeded
  ▼
State Machine  (state/reducer.js)
  │   PAY_VENDORS · ADD/UPDATE/DELETE_VENDOR · RETAG_TRANSACTION
  │   APPLY_AUTO_MATCH · SIMULATE_INFLOW · REPAY_CREDIT
  │   RECORD_SWITCH_REASON · SWITCH_MERCHANT · RESET_DEMO
  ▼
Derivation Layer  (utils/compute.js)
  │   every figure on screen is computed here, nothing is hardcoded
  │   netPositionTrend · computeCreditProfile · duePayments
  │   proposeMatches · appSplit · categoryBreakdown · repaymentSchedule
  ▼
Console UI  (pages/ + components/)
  ├── Home        net position, due-this-week table, GMV split by app
  ├── Vendors     saved payee roster, bulk settlement
  ├── Ledger      date-grouped transactions, auto-tag, CSV export
  ├── Credit      limit gauge, the formula shown openly, repayment plan
  └── Profile     opportunity sizing, switch-reason ranking, VOC mapping

Key Engineering Decisions & Trade-Offs

Every number is derived, none are hardcoded. The demo would be trivial to fake with static figures. Instead the seed generator produces 60 days of realistic vendor payments and QR collections against a fixed PRNG, and utils/compute.js derives every figure the UI shows. Pay a vendor and the net position, the credit utilisation, the reconciliation rate and the app-split chart all move together, because they are all reading the same state.

A local-timezone date layer. toISOString() converts to UTC before formatting, which in IST stamps anything before 05:30 with the previous day. That is enough to make a payment made today look a day old, and to push a vendor's next due date out by one. Every calendar date in the app goes through utils/date.js, which formats and parses in local time. This was a real bug the tests caught, not a theoretical one.

Deterministic reconciliation before anything clever. proposeMatches resolves an untagged payment to a saved vendor by UPI ID first, then by saved vendor reference, then by normalised fuzzy name, and inherits that vendor's expense head. A confidence value is attached per match basis. No model call, no heuristic soup, and the whole backlog clears in one click.

Indian currency formatting as a first-class concern. Amounts use Indian digit grouping (₹1,92,930, never ₹192,930), tabular figures so headline numbers do not jitter as values update, and the minus sign outside the symbol (-₹17,120, never ₹-17,120). All three are covered by tests, because all three were wrong at some point.

Hand-rolled SVG charts. The donut and the credit gauge are roughly 40 lines of SVG each rather than a charting library. Dropping the dependency halved the production bundle, from 636 KB to 320 KB, for two graphics.

Feedback capture as a research instrument. When a merchant pays a vendor they previously paid elsewhere, the success screen asks why in one click. The options mirror the factors the brief asks teams to investigate. The Profile screen then ranks the answers by count and by the GMV sitting behind each, because five small vendors kept elsewhere out of habit is a weaker signal than one large vendor lost to transaction limits.

Stack

Layer Choice
Framework React 19 + Vite 8
Styling Tailwind CSS v4, design tokens as CSS custom properties
Routing React Router 7 (HashRouter, so static hosts need no rewrite rules)
Icons Material Symbols
Charts Hand-rolled inline SVG
Tests Vitest, 54 tests
State React Context + useReducer, persisted to localStorage

The Three Features, and the Pain Each Answers

Feature Merchant pain (Track B brief) What the prototype actually does
Vendor Vault Repeat and bulk vendor payments are re-typed every cycle, across apps Saved payees with category, amount and cycle. Predicts the next due date from the last payment. Bulk-select and pay N vendors under one approval
Ledger Auto-Match Reconciliation and record-keeping for GST and accounting Auto-tags every payment from its vendor's expense head, matches untagged rows by UPI ID and name, exports a real CSV the accountant can open
Cash Flow Bridge Working capital when supplier payments land before customer money Credit limit computed as avg monthly QR collections × 0.4, drawable inside the payment flow with a split slider, plus a repayment schedule checked against projected collections

Alongside these, the outgoing GMV split by app on Home is the metric the brief actually asks to move. It shows how much of the merchant's payout volume still leaves through rival apps, and it visibly shifts as vendors are migrated.


Synthetic VOC Dataset

npm run voc regenerates a 50-respondent synthetic VOC workbook modelled on merchants around VIT Chennai (Kelambakkam Junction, Padur, Siruseri SIPCOT, Thiruporur, Pudupakkam), with 22 columns matching what the brief requires: collection mode, merchant category, transaction pattern, app per use case, barrier, anonymised verbatim, identified need and team insight.

Every record in it is fabricated. No business was contacted and no name refers to a real establishment. It exists so the analysis pipeline could be built and tested before real fieldwork. It must be replaced entirely before submission, and both generated files carry a warning banner saying so.


Limitations and Next Steps

The feature set is hypothesis-led, not yet validated

This is the most important limitation and the one most easily glossed over. The brief requires that the recommendation originate from primary research. The three features here were chosen from a prior architecture document, not from collected VOCs. They are plausible, and the synthetic dataset agrees with them, but that agreement is circular: the same source produced both.

Until the 50 real VOCs are in, treat the ranking as a hypothesis. If the real fieldwork says the dominant blocker is transaction limits, or that suppliers dictate the rail and nothing else matters, then Vendor Vault and Cash Flow Bridge are answering a question nobody asked, and the prototype should be cut down accordingly.

Payments are simulated

There are no real UPI rails, no bank integration and no Paytm systems behind this. The payment flow runs a 1.5-second timer and writes to local state. The credit product is illustrative: no lending, no NBFC partnership and no regulated product exists here.

Paytm cannot actually see payments made on other apps

The app-split chart is the core argument, and in a real deployment the data behind it would have to come from somewhere. UPI gives no app visibility into another app's transactions. The legitimate route is the RBI Account Aggregator framework, where the merchant consents and Paytm reads bank statement data covering every UPI debit regardless of which app initiated it. That is an architecture claim in the submission, not something built here, and Paytm's current AA integration status should be verified before it is asserted.

Other things that do not work yet

  • No persistence beyond the browser. One localStorage key, no auth, no multi-user, no server. Clearing site data resets everything.
  • Reconciliation matching is exact-then-fuzzy on names. It has no notion of a payee appearing under two different trade names, which is common in practice.
  • The seeded merchants are cash-positive by construction. After a large bulk settlement the 30-day net can go negative, which is correct behaviour but initially read as if something had broken. It now explains itself, but the seed is tuned rather than observed.
  • Fiscal and GST handling is cosmetic. The ledger exports expense heads and a GSTIN is displayed, but there is no GST logic, no invoice matching and no filing format.
  • Confidence values on auto-matches are assigned by rule, not measured. They order matches sensibly but are not calibrated against accuracy.
  • Not visually verified across browsers. Built and tested headlessly. Layout, the SVG gauge and the donut have been checked by code and by numeric assertion, not by eye in every browser.

What I would build next, in order

  1. Collect the 50 real VOCs, then cut whatever the data does not support. This is the critical path and everything else is secondary to it.
  2. Wire the switch-reason capture to a real store, so the ranking accumulates across sessions instead of resetting.
  3. A consented Account Aggregator connection as an onboarding step, turning the app-split chart from seeded data into the merchant's actual bank-statement picture.
  4. Multi-name payee resolution, the largest identifiable block of reconciliation misses.
  5. A server-backed profile, which everything above eventually requires.

Additional Notes

  • No Paytm trademark is reproduced. The Vyapar Hub wordmark and the V mark are original to this prototype. The design language (palette, type scale, component patterns) deliberately matches Paytm's so the concept reads as native to their ecosystem, but no logo or brand asset is used.
  • Zero-config static deploy. HashRouter means deep links resolve without rewrite rules, so the build drops onto Vercel, Netlify, Cloudflare Pages or any static host with no configuration file.
  • Tests cover the parts that are easy to get quietly wrong: timezone-safe date handling, Indian digit grouping, credit clamping, bulk-payment arithmetic, and the auto-match backlog clearing completely.
  • Two demo merchants ship with the app, a Kanpur kirana store and a Coimbatore logistics business, so the segment visibly generalises across merchant categories.

About

A merchant console for outgoing business UPI payments: saved-payee bulk payouts, auto-tagged GST-ready ledger reconciliation, and a working-capital line sized from the merchant's own QR collections. Built for the Paytm Innovation Challenge 2026 (UPI Growth Challenge, Track B).

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages