UPI AutoPay Review

Reads a UPI AutoPay mandate back against the plan before the PIN, flagging a cap far above the price, no end date or the wrong merchant.

Stops₹199 plan approved as a ₹9,999 mandate

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/upi-autopay-review.json

The files land in your project. They're yours to edit.

Usage

upi-autopay-review-usage.tsx
import { UpiAutopayReview, type Mandate } from "@/components/ui/upi-autopay-review"

<UpiAutopayReview
  defaultValue={sent}
  plan={{ name: "StreamMax Premium", price: 199, frequency: "monthly", merchant: "StreamMax" }}
  now={NOW}
  onApprove={async (mandate) => {
    await new Promise((resolve) => setTimeout(resolve, 600))
    setResult(`Approved: up to ₹${mandate.maxAmount}, ${mandate.frequency}`)
  }}
  onDecline={() => setResult("Declined")}
/>

What it catches

  • A cap far above the plan price (capRatio)
  • No fixed schedule ("as presented")
  • No end date or 30+ year validity
  • Merchant name that doesn't match the plan
  • Debits above the PIN-free (AFA) limit

Props

UpiAutopayReview props
PropTypeDefault
value

Controlled mandate, as it will be approved.

Mandate—
defaultValue

Initial mandate when uncontrolled: what the merchant sent.

Mandate—
onValueChange

Called after a one-tap fix changes the cap or end date.

(mandate: Mandate) => void—
plan

What the user signed up for, to compare the cap, schedule and merchant against.

MandatePlan | null—
now

"Now" in epoch ms, for deterministic rendering. Read from the clock after mount when omitted.

number—
capRatio

How far above the plan price the cap may sit before it's flagged. Default 1.5.

number—
locale

Locale for money and dates.

string"en-IN"
onApprove

Called on Approve. With warnings, the first press goes to the first fix and the second approves. May return a Promise.

(mandate: Mandate) => void | Promise<void>—
onDecline

Called on Decline.

() => void—
messages

Any visible string.

Partial<UpiAutopayMessages>—

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Mandate terms in a <dl>; warnings summarised and linked via aria-describedby
  • After a fix, focus moves to the next fix or Approve
  • Approve uses aria-disabled while pending; status is role=status

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: The PSP/bank enforces mandate terms; your server should create mandates with the plan's real cap and validity and verify them after registration.

  • Format and consistency checks only; it does not register or validate the mandate with NPCI or the bank
  • Merchant match compares names you pass
  • The ₹15,000 limit is the general case; some categories have ₹1,00,000

Rules

Rules last verified:

Confirmed: every mandate specifies a validity period the customer can modify or withdraw; issuer pre-debit notice at least 24 hours before; opt-out of a debit; AFA above ₹15,000 (₹1,00,000 for insurance, mutual funds, credit card bills). Informational; check the official source. Copied components don't update themselves, so re-check after rule changes (see updating a component).

What it stops

₹199 plan approved as a ₹9,999 mandate

Also known as

upi autopay · e-mandate · subscription · recurring payment · npci · upi pin · mandate · paytm

Field type

Dependencies

Pairs well with