This is the one screen where an Indian donor hands over money. Today the row that takes that money is drawn as a bank.
What is wrong: Razorpay renders with a solid bank-building icon, directly above a row called Bank transfer that carries an outline bank-building icon. Two rows, the same picture, opposite meanings. The label — Razorpay (Cards, UPI & more) — leads with a company name most donors do not choose by, and buries the word they are looking for.
What I recommend: lead with the method, not the processor. Title UPI, cards & netbanking; a quiet rupee icon; and Razorpay named in the subtitle where it does its real job, which is telling the donor who handles their card details.
What I am not recommending: the Razorpay logo. Reasons below — they are about this list's own grammar, not about squeamishness.
Rendered at the two widths that matter: a phone column and the desktop card. The worst case is a host that has Stripe, PayPal, Razorpay and bank transfer all switched on.
| Row | Icon box | Weight |
|---|---|---|
| New credit/debit card | 26 × 18 | grey fill |
| PayPal | 21 × 24 | grey fill |
| Razorpay | 26 × 26 | solid black |
| Bank transfer | 20 × 20 | outline |
The Razorpay icon is the largest and heaviest thing in the column. Not a styling choice — getPaymentMethodIcon is called without a size, so the SVG has no width or height attributes and fills the 26px slot. Every other row passes a size.
FormattedMessage. As written it can never be shown in Hindi or Malayalam.#E0E4EB, #C4C9D1, #CFD2D7), not PayPal blue. The house grammar is shape without brand colour. A brand-blue Razorpay mark would be the only advertisement in the column.razorpay.com/brand/) returns 404 and the newsroom page states only that marks are “subject to Razorpay's Usage Agreement”, which I could not read. I will not hand over a trademark I could not verify the terms for.Two files. Everything is additive or in-place; no component is restructured.
lib/payment-method-utils.js — stop drawing a bankimport React from 'react'; import { Bank } from '@styled-icons/boxicons-solid/Bank'; +import { IndianRupee } from 'lucide-react'; … } else if (service === PAYMENT_METHOD_SERVICE.RAZORPAY) { - // No dedicated icon yet — Razorpay is a card/UPI aggregator, a bank icon reads closer - // than a generic credit card would. - return <Bank size={size} />; + // Deliberately not a bank: the manual bank-transfer row already uses a Landmark icon, + // and Razorpay is UPI-first. Deliberately not the Razorpay mark either — this list + // de-brands (see the grey PayPal icon). ₹ is the one claim that is always true here. + return <IndianRupee size={size || 20} className="h-5 w-5" />; } else if (type === PAYMENT_METHOD_TYPE.PREPAID) {
lucide-react is already a dependency and is what the manual payment providers in this same list use (Landmark, HandCoins). No new package.
components/contribution-flow/utils.js — the words// Razorpay if (hostHasRazorpay && !disabledPaymentMethodTypes?.includes(PAYMENT_METHOD_TYPE.PAYMENT)) { uniquePMs.push({ key: 'razorpay', - title: 'Razorpay (Cards, UPI & more)', + title: ( + <FormattedMessage + id="ContributePayment.razorpay" + defaultMessage="UPI, cards & netbanking" + /> + ), + subtitle: ( + <FormattedMessage + id="ContributePayment.razorpaySubtitle" + defaultMessage="Handled by Razorpay — {collective} never sees your card details." + values={{ collective: collective.name }} + /> + ), paymentMethod: { service: PAYMENT_METHOD_SERVICE.RAZORPAY,
FormattedMessage is already imported in this file, and subtitle is already rendered by PaymentMethodList.tsx (the manual providers use it). Confirm the in-scope variable holding the collective before wiring {collective} — if it is awkward, drop the variable and use “Handled by Razorpay. Your card details are never shared with us.”
PaymentMethodList auto-selects the first enabled option — so on a host with both, an Indian donor lands with a card or PayPal pre-selected and has to notice and change it. Moving the Razorpay block above the “New credit card” block is a cut-and-paste of one block and fixes both the order and the default. I would do it as a separate commit from the icon.formatCurrency passes no locale, so toLocaleString uses the visitor's. An Indian browser gets ₹1,00,000; an American one gets ₹100,000. Below ₹1 lakh the two agree, so this only bites on large gifts. Worth pinning to en-IN for INR eventually; not part of this patch.PayWithRazorpayButton passes name: collective.name, so the donor's payment sheet — and likely their bank SMS — names the Collective, while the money settles into the fiscal host's Razorpay account. If those are different legal entities, that is a mismatch on the most scrutinised screen in the flow. I could not check whose account the key belongs to.prefill: { name, email, contact }. The flow already collected name and email one step earlier, so the donor is retyping them into the sheet on a phone. Three lines of config, not a feature — but it is a change to the payment call, so it belongs to whoever owns that file.PaymentMethodList.tsx, payment-method-utils.js, lucide-react, the repo's own icon SVGs) and rendered and measured in Chrome — but in a harness, not on the real page. Somebody should look at the real step once the app is back up. The prediction to check: the Razorpay icon measures 26 × 26 and the bank-transfer icon 20 × 20; after the patch both are 20 × 20.razorpay.com/brand/ is a 404; the newsroom page names a Usage Agreement it does not show.