Payment Gateway
Problem
Design PaymentGateway, an in-memory service that manages client access to payment modes and routes valid payments to deterministic mock banks. One gateway instance owns its clients, enabled modes, routing progress, bank outcomes, and observed success history.
Requirements
- Manage clients.
addClientadds a missing client with no enabled modes and returnstrue, or returnsfalsefor a duplicate without changing it.removeClientremoves an existing client and its modes and returnstrue, or returnsfalsefor a missing client.hasClientreflects the current membership. - Add mode support. Without
clientId,addSupportForPaymentModeenables a configured, currently disabled gateway mode. WithclientId, it enables the mode for that existing client only when the gateway enables it and the client does not. The method returnstrueonly when it changes the selected set. - Remove mode support. Without
clientId,removePaymentModedisables an enabled gateway mode, removes it from every client, and returnstrue. WithclientId, it removes a mode present for that client and returnstrue. Missing clients or absent memberships returnfalse; disabling and re-enabling a gateway mode preserves its routing cursor, bank outcome positions, and success history. - List mode support.
listSupportedPaymentModesreturns the exact gateway set whenclientIdis absent, the exact client set for a registered client, or an empty array for a missing client. Array order is not judged. - Reject ineligible payments.
makePaymentreturns{status: "REJECTED", bankId: ""}when the client is missing or the mode is not enabled by both the gateway and that client. A rejected payment changes no cursor, outcome position, or success history. - Validate instrument details. A UPI payment requires a non-empty
vpa; CARD requires non-emptycardNumber,expiry, andcvv; NET_BANKING requires non-emptyuserIdandpassword. Missing required details reject the payment, OTP is never required, and extra fields are ignored. - Route and execute payments. Each valid payment reads that mode's cursor from 0 through 99, selects the first configured bank when the cursor is less than its active percentage and the second bank otherwise, then advances the cursor modulo 100. The selected bank consumes its next repeating boolean outcome; the result contains that bank's ID and has status
SUCCESSfortrueorFAILEDforfalse. The gateway records attempts and successes separately for each bank and payment mode. - Adapt and inspect distribution. An unobserved bank whose configured percentage is positive has a 100 percent success rate. Equal rates keep the configured split; unequal rates send 100 percent to the higher-rate bank, while a bank configured at 0 percent stays excluded. For an enabled mode,
showDistributionreturns the two active integer percentages in configured bank order; they sum to 100. Disabled or unknown modes return an empty array.
API
| Signature | Returns | Behavior |
|---|---|---|
PaymentGateway(bankConfigs: BankConfig[], routingRules: RoutingRule[]) | PaymentGateway | Creates the configured banks and routes with empty client, mode, cursor, and history state. |
addClient(clientId: string) | boolean | Attempts to register one client. |
removeClient(clientId: string) | boolean | Attempts to remove one client. |
hasClient(clientId: string) | boolean | Reads current client membership. |
listSupportedPaymentModes(clientId?: string) | string[] | Reads gateway-wide or client-specific mode support. |
addSupportForPaymentMode(paymentMode: string, clientId?: string) | boolean | Attempts to enable gateway-wide or client-specific support. |
removePaymentMode(paymentMode: string, clientId?: string) | boolean | Attempts to disable gateway-wide or client-specific support. |
showDistribution(paymentMode: string) | DistributionEntry[] | Reads the active route allocation. |
makePayment(clientId: string, paymentMode: string, details: object) | PaymentResult | Validates, routes, executes, and records one payment attempt. |
| Type | Shape |
|---|---|
BankConfig | {bankId: string, supportedPaymentModes: string[], outcomePattern: boolean[]} |
RoutingRule | {paymentMode: string, firstBankId: string, secondBankId: string, firstBankPercent: integer} |
DistributionEntry | {bankId: string, percent: integer} |
PaymentResult | {status: string, bankId: string} where status is SUCCESS, FAILED, or REJECTED |
Examples
Given banks A and B for UPI with outcome patterns [false] and [true], and a route from A to B with firstBankPercent 50:
| Step | Operation | Result |
|---|---|---|
| 1 | addClient("shop") | true |
| 2 | addSupportForPaymentMode("UPI") | true |
| 3 | addSupportForPaymentMode("UPI", "shop") | true |
| 4 | showDistribution("UPI") | [{bankId: "A", percent: 50}, {bankId: "B", percent: 50}] |
| 5 | makePayment("shop", "UPI", {}) | {status: "REJECTED", bankId: ""} |
| 6 | makePayment("shop", "UPI", {vpa: "shop@upi"}) | {status: "FAILED", bankId: "A"} |
| 7 | showDistribution("UPI") | [{bankId: "A", percent: 0}, {bankId: "B", percent: 100}] |
| 8 | makePayment("shop", "UPI", {vpa: "shop@upi"}) | {status: "SUCCESS", bankId: "B"} |
Constraints
- Each testcase has 2 to 20 banks, 1 to 3 routing rules, and at most one rule for each of
UPI,CARD, andNET_BANKING. - A rule names two distinct configured banks that support its mode;
firstBankPercentis an integer from 0 through 100. - Each bank supports 1 to 3 distinct modes and has an
outcomePatternof 1 to 100 booleans. - Client and bank IDs contain 1 to 64 ASCII letters, digits, hyphens, or underscores and are compared exactly.
- At most 1,000 clients are registered at once, and a testcase makes at most 50,000 public method calls.
- Payment detail keys and values contain at most 256 characters; a required value must be present and non-empty.
- Constructor inputs satisfy these configuration constraints.
Notes
Calls are sequential and perform no network, database, real-bank, or real-money I/O. Percentage comparisons use exact integer ratios, counters never become negative or exceed 50,000, and all state belongs to one gateway instance for one testcase.