Razorpay Previous Year Coding Questions

Razorpay is India's leading fintech infrastructure company, powering payments for 10M+ businesses. Engineering interviews are known to be rigorous — DSA rounds are genuinely hard (Hard-level LeetCode problems appear regularly), and the System Design round is specifically tailored to Razorpay's domain: payment gateways, rate limiting, fraud detection, and distributed transaction flows. The Machine Coding round typically asks for a payment or ledger system design in OOP. Razorpay values engineers who can reason about correctness and consistency under failure — expect questions about idempotency, exactly-once semantics, and distributed locks to come up even in DSA rounds.


Hiring Process

Step 1: Resume Screening Razorpay screens for strong DSA fundamentals and backend experience. Fintech, payments, or high-scale system experience is a plus. Tier-1 college candidates and engineers from product companies are preferred.

Step 2: Online Assessment (HackerEarth) Duration: 60–90 minutes. 2–3 coding problems ranging from Easy to Hard. Problems are often graph or DP-heavy. Some OAs include a SQL problem. No partial scoring — all test cases must pass.

Step 3: DSA Live Coding Round 1 60 minutes. 2 LeetCode Medium problems. Candidates are expected to code clean solutions with edge case handling and complexity analysis. Interviewers sometimes ask follow-up variations to the same problem.

Step 4: DSA Live Coding Round 2 60 minutes. A harder problem — typically a Hard-level LeetCode or a custom problem involving graphs, advanced DP, or data structure design. This round is the primary bar-setting round at Razorpay.

Step 5: Machine Coding / LLD Round 90–120 minutes. Build a working system from a product spec. Common problems: Rate Limiter (Token Bucket/Sliding Window), Payment Ledger, Expense Splitter. Graded on SOLID principles, extensibility, and clean code — not just working output.

Step 6: System Design / HLD Round (SDE-2+) 60–90 minutes covering Razorpay's actual domain. Interviewers expect deep knowledge of distributed transactions (2-phase commit, Saga pattern), idempotency keys, and consistent hashing. Payment Gateway, Fraud Detection, and the Razorpay Settlements system are recurring topics.

Step 7: Hiring Manager / Bar Raiser Round Behavioural and system depth. Candidates walk through a past complex project and justify architecture decisions. Engineering values discussed: correctness, reliability, observability.

Tips: The Hard DSA round is what eliminates most candidates — practice LC Hard on graphs (Word Ladder, Alien Dictionary) and advanced DP (Distinct Subsequences, Regular Expression Matching). For LLD, build a Token Bucket rate limiter from scratch before your interview. For HLD, deeply understand idempotency and the Saga pattern — Razorpay interviewers specifically probe these because they're core to payment reliability.


Preparation Resources

Part 1 — OA Questions

1. Trapping Rain Water Hard

OAArrays · Two Pointers · StackLeetCode: #42

Given n non-negative integers representing an elevation map where each bar has width 1, compute how much water it can trap after raining. This is the single most frequently appearing Razorpay OA problem.

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6

Input: height = [4,2,0,3,2,5]
Output: 9

Approach: Two-pointer. Keep leftMax and rightMax. Move the pointer with the smaller max. The water at that position is max - height[pointer]. This avoids the O(n) extra space of the prefix max array approach. Complexity: Time O(n) · Space O(1) Follow-up: 3D version — Trapping Rain Water II (LeetCode #407) uses a min-heap BFS.


2. Longest Substring Without Repeating Characters Medium

OASliding Window · HashingLeetCode: #3

Given a string s, find the length of the longest substring without repeating characters.

Input: s = "abcabcbb"
Output: 3
Explanation: "abc"

Input: s = "pwwkew"
Output: 3
Explanation: "wke"

Approach: Sliding window with a hash map storing character → last seen index. When a repeat is found, move the left pointer to max(left, lastSeen[char] + 1). Update window size at each step. Complexity: Time O(n) · Space O(min(n, 26)) Follow-up: Longest Substring with At Most K Distinct Characters (LeetCode #340).


3. House Robber Medium

OADynamic ProgrammingLeetCode: #198

Given an array of non-negative integers representing the amount of money at each house, find the maximum amount you can rob without robbing two adjacent houses.

Input: nums = [2,7,9,3,1]
Output: 12
Explanation: Rob houses 1, 3, 5 (0-indexed 0, 2, 4) → 2+9+1=12.

Input: nums = [1,2,3,1]
Output: 4

Approach: dp[i] = max amount robbing up to house i. dp[i] = max(dp[i-1], dp[i-2] + nums[i]). Space-optimise to two variables. Complexity: Time O(n) · Space O(1) Follow-up: House Robber II (circular, LeetCode #213), House Robber III (tree, LeetCode #337).


4. Coin Change Medium

OADynamic Programming · BFSLeetCode: #322

Given an array of coin denominations and an amount, return the fewest number of coins needed to make up that amount, or -1 if impossible.

Input: coins = [1,5,10,25], amount = 36
Output: 3
Explanation: 25 + 10 + 1.

Input: coins = [2], amount = 3
Output: -1

Approach: dp[i] = minimum coins for amount i. Initialise to infinity except dp[0]=0. For each amount, try every coin: dp[i] = min(dp[i], dp[i-coin]+1). Return dp[amount] or -1. Complexity: Time O(amount × coins) · Space O(amount) Follow-up: Coin Change II — count the number of combinations (LeetCode #518).


5. Task Scheduler Medium

OAGreedy · HeapLeetCode: #621

Given a list of CPU tasks and a cooldown interval n, return the minimum number of intervals required to finish all tasks (idle intervals count).

Input: tasks = ["A","A","A","B","B","B"], n = 2
Output: 8
Explanation: A→B→idle→A→B→idle→A→B

Input: tasks = ["A","A","A","B","B","B"], n = 0
Output: 6

Approach: Count frequencies. Most frequent task count is f_max. Number of slots: (f_max - 1) (n + 1) + count_of_tasks_with_max_freq. Answer is max(this, total task count). *Complexity: Time O(n) · Space O(1) (only 26 letters) Follow-up: Reorganize String (LeetCode #767) — can tasks be arranged so no two adjacent are the same?


6. Maximum Points You Can Obtain from Cards Medium

OASliding Window · ArraysLeetCode: #1423

Given a row of cards with cardPoints array, take exactly k cards from either end to maximise total points.

Input: cardPoints = [1,2,3,4,5,6,1], k = 3
Output: 12
Explanation: Take [1,6,5] from right → 12.

Input: cardPoints = [9,7,7,9,7,7,9], k = 7
Output: 55

Approach: Equivalent to finding the minimum sum subarray of length (n-k) in the middle. Sliding window of size (n-k), track minimum sum. Answer = totalSum - minMidSum. Complexity: Time O(n) · Space O(1) Follow-up: What if you can take at most k cards from each end (not exactly k)?


Part 2 — Phone Screen Questions

7. Meeting Rooms II Medium

Phone ScreenHeap · Sorting · IntervalsLeetCode: #253

Given an array of meeting time intervals, find the minimum number of conference rooms required. Razorpay frames this as "minimum payment processing threads needed."

Input: intervals = [[0,30],[5,10],[15,20]]
Output: 2

Input: intervals = [[7,10],[2,4]]
Output: 1

Approach: Sort by start time. Min-heap of end times. For each interval, if its start >= heap top (earliest ending meeting), pop and reuse the room. Otherwise, add a new room. Heap size at the end is the answer. Complexity: Time O(n log n) · Space O(n) Follow-up: Count concurrent transactions at any point in time — same problem, max overlap.


8. Kth Largest Element in an Array Medium

Phone ScreenHeap · QuickSelectLeetCode: #215

Find the kth largest element in an unsorted array.

Input: nums = [3,2,1,5,6,4], k = 2
Output: 5

Input: nums = [3,2,3,1,2,4,5,5,6], k = 4
Output: 4

Approach: Min-heap of size k. Iterate the array: if current > heap top, pop and push current. The heap top is the kth largest. Alternative: QuickSelect achieves O(n) average. Complexity: Time O(n log k) heap · O(n) avg QuickSelect · Space O(k) Follow-up: Find Median from Data Stream (LeetCode #295) using two heaps.


9. Product of Array Except Self Medium

Phone ScreenPrefix Product · ArraysLeetCode: #238

Given an integer array, return an array where each element is the product of all other elements. Must run in O(n) without division.

Input: nums = [1,2,3,4]
Output: [24,12,8,6]

Input: nums = [-1,1,0,-3,3]
Output: [0,0,9,0,0]

Approach: Two passes. First pass: result[i] = product of all elements to the left. Second pass: multiply by running right product. Complexity: Time O(n) · Space O(1) (output array doesn't count) Follow-up: Handle zeros explicitly — what if two or more zeros exist?


10. 3Sum Medium

Phone ScreenTwo Pointers · SortingLeetCode: #15

Find all unique triplets in the array that sum to zero.

Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]

Input: nums = [0,0,0]
Output: [[0,0,0]]

Approach: Sort. Fix the first element and run two pointers for the remaining pair summing to -nums[i]. Skip duplicates at the first, left, and right pointers. Complexity: Time O(n²) · Space O(1) Follow-up: 3Sum Closest (LeetCode #16), 4Sum (LeetCode #18).


11. Sliding Window Maximum Hard

Phone ScreenDeque · Sliding WindowLeetCode: #239

Given an array and window size k, return the maximum of each sliding window.

Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [3,3,5,5,6,7]

Approach: Monotonic deque storing indices. Remove indices outside the window from the front. Remove indices whose values are less than the current element from the back (they can never be the maximum). The front of the deque is always the current window maximum. Complexity: Time O(n) · Space O(k) Follow-up: Minimum of each sliding window — same deque, but keep ascending order instead.


12. Decode Ways Medium

Phone ScreenDynamic Programming · StringsLeetCode: #91

A message encoded by mapping 'A'→1, 'B'→2, ..., 'Z'→26. Given a string of digits, count the number of ways to decode it.

Input: s = "12"
Output: 2
Explanation: "AB" (1,2) or "L" (12).

Input: s = "226"
Output: 3

Input: s = "06"
Output: 0

Approach: dp[i] = number of ways to decode s[0..i-1]. If s[i-1] != '0', dp[i] += dp[i-1]. If s[i-2..i-1] is between 10-26, dp[i] += dp[i-2]. Complexity: Time O(n) · Space O(1) Follow-up: Decode Ways II where '*' can be any digit 1-9 (LeetCode #639).


13. Binary Tree Level Order Traversal Medium

Phone ScreenTrees · BFSLeetCode: #102

Return the level-order traversal of a binary tree's values as a list of lists.

Input: root = [3,9,20,null,null,15,7]
Output: [[3],[9,20],[15,7]]

Approach: BFS with a queue. At each level, record the queue size, process exactly that many nodes, and enqueue their children. Append each level's values to the result. Complexity: Time O(n) · Space O(n) Follow-up: Zigzag level order (LeetCode #103), right side view (LeetCode #199).


Part 3 — Onsite Questions

14. Word Ladder Hard

OnsiteGraph · BFSLeetCode: #127

Given a beginWord, endWord, and a word list, find the length of the shortest transformation sequence where each step changes exactly one letter and every intermediate word must be in the word list.

Input: beginWord="hit", endWord="cog", wordList=["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: hit→hot→dot→dog→cog

Input: wordList does not contain endWord
Output: 0

Approach: BFS. For each word, try changing each character to a-z. If the new word is in the word set and not visited, add to queue. Track level count. Use bidirectional BFS for 10× speedup on large inputs. Complexity: Time O(M²×N) where M=word length, N=word count · Space O(M²×N) Follow-up: Word Ladder II — return all shortest paths (LeetCode #126, much harder — BFS + backtracking).


15. Alien Dictionary Hard

OnsiteGraph · Topological SortLeetCode: #269

Given a sorted list of words from an alien language, derive the order of characters in that alphabet. Return "" if invalid (cycle).

Input: words = ["wrt","wrf","er","ett","rftt"]
Output: "wertf"

Input: words = ["z","x"]
Output: "zx"

Approach: Compare adjacent words to extract ordering constraints (u comes before v if words[i][j] != words[i+1][j]). Build a directed graph and run topological sort (Kahn's). If any cycle is detected, return "". Complexity: Time O(C) where C = total characters · Space O(1) (26 letters) Follow-up: What if two words have the same prefix but the shorter one comes second (e.g., "abc" after "ab") — return "".


16. Clone Graph Medium

OnsiteGraph · DFS · HashingLeetCode: #133

Given a reference to a node in a connected undirected graph, return a deep copy of the entire graph.

Input: adjList = [[2,4],[1,3],[2,4],[1,3]]
Output: [[2,4],[1,3],[2,4],[1,3]]  (deep copy)

Approach: DFS with a visited map (original node → cloned node). When visiting a node, create its clone, store in the map, then recursively clone all neighbours and attach to the clone's adjacency list. Complexity: Time O(V+E) · Space O(V) Follow-up: What changes if the graph can be disconnected?


17. Course Schedule II Medium

OnsiteGraph · Topological SortLeetCode: #210

Return any valid course order to finish all courses given prerequisite pairs, or an empty array if impossible.

Input: numCourses=4, prerequisites=[[1,0],[2,0],[3,1],[3,2]]
Output: [0,1,2,3] or [0,2,1,3]

Input: prerequisites=[[1,0],[0,1]]
Output: []

Approach: Kahn's BFS topological sort. Compute in-degrees. Start with 0-in-degree nodes. Process and reduce neighbours. If total processed == numCourses, return the order. Otherwise return []. Complexity: Time O(V+E) · Space O(V+E) Follow-up: Same problem with DFS — use a three-color marking (unvisited, visiting, visited).


18. Serialize and Deserialize Binary Tree Hard

OnsiteTrees · DFS · DesignLeetCode: #297

Design an algorithm to serialize a binary tree to a string and deserialize it back to the same tree structure.

Input: root = [1,2,3,null,null,4,5]
Serialized: "1,2,N,N,3,4,N,N,5,N,N"
Deserialized: original tree

Approach: Preorder DFS. Serialize: visit node, append value (or "N" for null), then left and right subtrees. Deserialize: split by delimiter, use an index/queue to consume tokens in preorder. Reconstruct recursively. Complexity: Time O(n) · Space O(n) Follow-up: BST serialization can use more compact format (no need for null markers) — explain why.


19. Find Median from Data Stream Hard

OnsiteHeap · DesignLeetCode: #295

Design a data structure that supports addNum(num) and findMedian() in O(log n) and O(1) respectively.

addNum(1), addNum(2), findMedian() → 1.5
addNum(3), findMedian() → 2.0

Approach: Two heaps — a max-heap for the lower half and a min-heap for the upper half. Keep sizes balanced (differ by at most 1). On each add, push to max-heap, pop its max and push to min-heap, then rebalance if sizes drift. Median is the top of the larger heap or average of both tops. Complexity: Time O(log n) add · O(1) median · Space O(n) Follow-up: Sliding Window Median (LeetCode #480).


20. Minimum Window Substring Hard

OnsiteSliding Window · HashingLeetCode: #76

Given strings s and t, find the minimum window substring of s that contains all characters of t.

Input: s = "ADOBECODEBANC", t = "ABC"
Output: "BANC"

Input: s = "a", t = "aa"
Output: ""

Approach: Sliding window. Expand right pointer, adding characters to a window map. When all t characters are satisfied (using a 'formed' counter), try to shrink from the left while still satisfied. Track the minimum window. Complexity: Time O(|s|+|t|) · Space O(|s|+|t|) Follow-up: Substring with Concatenation of All Words (LeetCode #30).


21. Regular Expression Matching Hard

OnsiteDynamic Programming · RecursionLeetCode: #10

Implement regex matching with '.' (any single character) and '*' (zero or more of the preceding element).

Input: s = "aa", p = "a*"
Output: true

Input: s = "ab", p = ".*"
Output: true

Input: s = "aab", p = "c*a*b"
Output: true

Approach: dp[i][j] = true if s[0..i-1] matches p[0..j-1]. If p[j-1] is '': dp[i][j] = dp[i][j-2] (zero occurrences) OR (dp[i-1][j] if s[i-1] matches p[j-2]). Otherwise dp[i][j] = dpi-1 if chars match (or p[j-1] is '.'). *Complexity: Time O(m*n) · Space O(m*n) Follow-up: Wildcard Matching (LeetCode #44) where '*' matches any sequence — slightly different DP transitions.


22. Reverse Nodes in k-Group Hard

OnsiteLinked List · RecursionLeetCode: #25

Given a linked list, reverse the nodes in every k-node group. If the remaining nodes are fewer than k, leave them as is.

Input: head = [1,2,3,4,5], k = 2
Output: [2,1,4,3,5]

Input: head = [1,2,3,4,5], k = 3
Output: [3,2,1,4,5]

Approach: Check if k nodes exist. Reverse the first k nodes (standard reversal). Recursively process the rest and connect. Use a dummy head node for clean pointer manipulation. Complexity: Time O(n) · Space O(n/k) recursion stack Follow-up: Iterative solution using a stack to collect k nodes at a time.


23. Distinct Subsequences Hard

OnsiteDynamic Programming · StringsLeetCode: #115

Given strings s and t, return the number of distinct subsequences of s that equal t.

Input: s = "rabbbit", t = "rabbit"
Output: 3

Input: s = "babgbag", t = "bag"
Output: 5

Approach: dp[i][j] = number of ways to form t[0..j-1] using s[0..i-1]. If s[i-1] == t= dpi-1 + dp[i-1][j] (use this char or skip it). Else: dp[i][j] = dp[i-1][j]. Complexity: Time O(m*n) · Space O(m*n), reducible to O(n) Follow-up: Why does this problem have exponential brute-force but polynomial DP?


24. Best Time to Buy/Sell Stock with Cooldown Medium

OnsiteDynamic Programming · State MachineLeetCode: #309

Buy and sell stocks with unlimited transactions but after selling you must wait one day (cooldown) before buying again. Maximize profit.

Input: prices = [1,2,3,0,2]
Output: 3
Explanation: buy(1)→sell(2)→cooldown→buy(0)→sell(2) = 3

Approach: State machine DP. Three states: held (own stock), sold (just sold, in cooldown), rest (can buy). held[i] = max(held[i-1], rest[i-1]-price). sold[i] = held[i-1]+price. rest[i] = max(rest[i-1], sold[i-1]). Complexity: Time O(n) · Space O(1) Follow-up: With transaction fee (LeetCode #714) — same state machine, subtract fee on sell.


25. Palindrome Partitioning Medium

OnsiteBacktracking · DPLeetCode: #131

Return all possible ways to partition a string such that every substring is a palindrome.

Input: s = "aab"
Output: [["a","a","b"],["aa","b"]]

Approach: Backtracking. At each position, try every prefix as a partition. If the prefix is a palindrome, recurse on the suffix. Pre-compute a DP palindrome table (isPalin[i][j]) to check palindrome in O(1). Complexity: Time O(n * 2^n) · Space O(n²) Follow-up: Palindrome Partitioning II — minimum cuts for palindrome partitioning (LeetCode #132).


26. Count Vowels Permutation Hard

OnsiteDynamic ProgrammingLeetCode: #1220

Count the number of strings of length n that consist only of vowels (a,e,i,o,u) and follow the rules: 'a' can only be followed by 'e'; 'e' by 'a' or 'i'; 'i' by anything except 'i'; 'o' by 'i' or 'u'; 'u' by 'a'. Return count mod 10^9+7.

Input: n = 1
Output: 5

Input: n = 2
Output: 10

Input: n = 5
Output: 68

Approach: DP where dp[c] = number of strings of current length ending in vowel c. Transition follows the rules (work backwards: what can precede each vowel?). Iterate n-1 times updating the five counts. Complexity: Time O(n) · Space O(1) Follow-up: Matrix exponentiation reduces this to O(log n) for very large n.


27. Flatten Nested List Iterator Medium

OnsiteStack · Iterator · DesignLeetCode: #341

Given a nested list of integers, implement an iterator to flatten it. Each element is either an integer or a list whose elements may also be integers or lists.

Input: [[1,1],2,[1,1]]
Output: [1,1,2,1,1]

Input: [1,[4,[6]]]
Output: [1,4,6]

Approach: Stack storing iterators of each list level. hasNext() peeks at the top iterator; if the next element is a list, push its iterator. If it's an integer, return true. next() calls hasNext() then returns the integer. Complexity: Time O(1) amortised per next() · Space O(depth) Follow-up: What if the nesting can be infinite depth?


28. Jump Game II Medium

OnsiteGreedyLeetCode: #45

Return the minimum number of jumps to reach the last index from index 0. You can always reach the end.

Input: nums = [2,3,1,1,4]
Output: 2
Explanation: 0→1→4

Input: nums = [2,3,0,1,4]
Output: 2

Approach: Greedy BFS. Track current jump's max reach (curEnd) and next jump's max reach (curFarthest). When i reaches curEnd, increment jump count and update curEnd to curFarthest. Complexity: Time O(n) · Space O(1) Follow-up: Minimum number of jumps where each jump is exactly k steps (circular array problem).


29. Two City Scheduling Medium

OnsiteGreedy · SortingLeetCode: #1029

A company must send n people to city A and n people to city B. costs[i] = [aCost, bCost]. Minimise total travel cost.

Input: costs = [[10,20],[30,200],[400,50],[30,20]]
Output: 110
Explanation: City A: [10,30], City B: [200,50] → total 110

Approach: Sort by (aCost - bCost). Sending the person with the most negative difference to city A saves the most. Send first n people to A and last n to B. Complexity: Time O(n log n) · Space O(1) Follow-up: What if there are k cities and each city needs exactly m people?


30. Reorganize String Medium

OnsiteGreedy · HeapLeetCode: #767

Given a string, rearrange it so that no two adjacent characters are the same. Return "" if impossible.

Input: s = "aab"
Output: "aba"

Input: s = "aaab"
Output: ""

Approach: Max-heap by frequency. At each step, pop the two most frequent characters, append them, decrement counts, and re-push if non-zero. If only one character remains and its count > 1, impossible. Complexity: Time O(n log k) where k = distinct chars · Space O(k) Follow-up: Task Scheduler (LeetCode #621) is the same greedy idea with idle slots.


31. Maximum Width of Binary Tree Medium

OnsiteTrees · BFSLeetCode: #662

Return the maximum width of a binary tree where width is the length between the leftmost and rightmost non-null nodes at any level (including nulls in between).

Input: root = [1,3,2,5,3,null,9]
Output: 4

Approach: BFS with position numbering. For a node at position i, left child is 2i and right child is 2i+1. At each level, width = last_position - first_position + 1. Normalise positions by subtracting the level's min to prevent integer overflow. Complexity: Time O(n) · Space O(n) Follow-up: Why must you normalise positions at each level? What overflow occurs without it?


32. Word Break II Hard

OnsiteBacktracking · DP · MemoisationLeetCode: #140

Return all valid sentence segmentations of a string using words from a dictionary.

Input: s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"]
Output: ["cats and dog","cat sand dog"]

Approach: Memoised backtracking. Recursively try each prefix; if it's in the dictionary, recurse on the suffix. Cache the results for each suffix position to avoid redundant work. Complexity: Time O(n × 2^n) worst case · Space O(n × 2^n) Follow-up: How does memoisation here differ from DP — why can't we guarantee polynomial time even with memoisation?


33. LRU Cache Medium

OnsiteDesign · Linked ListLeetCode: #146

Design an LRU cache with O(1) get and put.

LRUCache(2)
put(1,1), put(2,2), get(1)→1, put(3,3) evicts 2, get(2)→-1

Approach: Doubly linked list (MRU at head, LRU at tail) + hash map. get: move node to head. put: insert at head; if over capacity, remove tail node and delete from map. Complexity: Time O(1) · Space O(capacity) Follow-up: LFU Cache (LeetCode #460) — evicts least frequently used, with LRU as tiebreaker.


34. Implement HashMap Easy

OnsiteDesign · HashingLeetCode: #706

Implement a HashMap from scratch without using built-in hash table libraries. Support put(key, value), get(key), and remove(key).

hashMap.put(1,1), put(2,2), get(1)→1, get(3)→-1
put(2,1), get(2)→1, remove(2), get(2)→-1

Approach: Fixed-size array of buckets (size 1024). Hash function: key % bucketSize. Each bucket is a linked list of (key, value) pairs for collision handling (chaining). put checks for existing key to update or appends a new node. Complexity: Time O(1) avg, O(n/b) worst · Space O(n+b) Follow-up: Discuss open addressing vs. chaining trade-offs. When does load factor matter?


35. Design Rate Limiter — Token Bucket (LLD) Medium

Onsite · LLDOOP · Design

Design a rate limiter that allows at most N requests per second per user using the Token Bucket algorithm. The limiter must support multiple concurrent users.

RateLimiter.configure(maxTokens=10, refillRate=10/sec)
RateLimiter.allow(userId="u1") → true  // has tokens
RateLimiter.allow(userId="u1") → true  // continues until bucket empty
RateLimiter.allow(userId="u1") → false // bucket empty
// After 1 second, 10 tokens refilled

Approach: Classes: Bucket (tokens, lastRefillTime, maxTokens, refillRate), RateLimiter (userBuckets map). allow(userId): get or create bucket, refill based on elapsed time since lastRefill (add elapsed rate tokens, cap at max), consume one token if available. Thread-safety: per-user lock (not global lock) for maximum concurrency. *Key Algorithms: Token Bucket allows burst; Leaky Bucket enforces steady rate; Sliding Window Log is most accurate but memory-heavy Follow-up: Implement Sliding Window Counter rate limiter using Redis for distributed systems.


36. Design Payment Ledger (LLD) Medium

Onsite · LLDOOP · Design

Design a double-entry payment ledger for Razorpay. Every financial transaction must create two ledger entries (debit + credit) to keep the ledger balanced. Support recording transactions, querying account balance, and fetching transaction history.

Ledger.recordTransaction(txnId, debitAccount, creditAccount, amount, currency)
Ledger.getBalance(accountId) → amount
Ledger.getHistory(accountId, from, to) → [Transaction]
Ledger.reconcile() → isBalanced (sum of all debits == sum of all credits)

Approach: Classes: Account (id, currency, entries), LedgerEntry (txnId, accountId, type: DEBIT/CREDIT, amount, timestamp), Transaction (id, debitEntry, creditEntry, metadata). Ledger stores a map of accountId → List. getBalance sums credits minus debits. reconcile checks globalDebits == globalCredits. Idempotency: check txnId exists before inserting. Key Design Principles: Immutability (entries never updated, only new entries added), Idempotency on txnId, Double-entry invariant enforced at write time Follow-up: How do you handle multi-currency transactions? What is the exchange rate source of truth?


37. Design Payment Gateway (HLD) Hard

Onsite · HLDSystem Design

Design Razorpay's payment gateway that processes payments between merchants and customers via bank networks, UPI, and card rails.

Functional: initiatePayment(orderId, amount, method), getPaymentStatus(paymentId), refund(paymentId)
Non-functional: 99.99% uptime, p99 latency <500ms, 50K TPS at peak, exactly-once processing

Approach: Flow: Customer → API Gateway → Payment Service → Routing Engine (picks UPI/card/netbanking) → Bank Adapter → Bank/PSP. Key concerns: (1) Idempotency — each payment has a unique idempotency key stored in Redis; duplicate requests return the cached response. (2) Exactly-once — use distributed locks (Redis SETNX) before sending to bank; if the bank call times out, a reconciliation job resolves the status. (3) Saga pattern for multi-step flows (auth → capture → settle). Status is stored in MySQL with events published to Kafka for async notification. (4) PCI-DSS — card data never touches Razorpay servers, tokenised via Visa/Mastercard. Key Concepts: Idempotency keys, Saga/2PC for distributed transactions, Redis SETNX locks, Kafka for event streaming, reconciliation jobs Follow-up: How do you handle the case where the bank confirms payment but Razorpay's response to the merchant times out?


38. Design Fraud Detection System (HLD) Hard

Onsite · HLDSystem Design

Design a real-time fraud detection system for Razorpay that scores every payment transaction for fraud risk and can block suspicious transactions before they reach the bank.

Functional: scoreTransaction(txn) → riskScore, blockTransaction(txnId), getAlerts()
Non-functional: <100ms scoring latency (in the payment critical path), 50K TPS, false positive rate <0.1%

Approach: Every payment goes through a synchronous Fraud Scoring Service (in the payment flow, before routing to bank). The service runs rules engine checks (velocity rules: >5 txns/min from same IP), feature computation (aggregated from Redis: user's 24h spend, device fingerprint), and an ML model (pre-trained, loaded in memory for low latency). Score > threshold → block. Score in grey zone → trigger async manual review queue. Kafka consumes every transaction for async feature store updates (Flink/Spark Streaming computes rolling aggregates). Historical fraud data stored in HDFS/S3 for model retraining. Key Concepts: Synchronous rules engine in critical path, async ML feature computation, Redis for real-time aggregates, velocity checks, device fingerprinting Follow-up: How do you reduce false positives for high-value merchants without compromising fraud detection?


39. Design URL Shortener (HLD + LLD) Medium

Onsite · HLDSystem Design

Design a URL shortening service (like bit.ly) that creates short codes for long URLs and redirects users at low latency. Razorpay uses this for payment links.

POST /shorten → {shortCode: "abc123"}, GET /abc123 → 301 redirect
Non-functional: 100M URLs, <10ms redirect latency, 1000 writes/sec, 100K reads/sec

Approach: Short code generation: base62 encoding of an auto-incremented ID (or MD5 hash + truncate). Store mapping in MySQL (shortCode → longUrl, createdAt, expiresAt). Redis cache for hot redirects (LRU eviction). On redirect: check Redis first, fall back to DB, write-through to cache. Scale writes: single-leader DB with read replicas; use a central ID generator (Twitter Snowflake). Custom alias: check uniqueness before insert. Analytics: async Kafka event per redirect for click tracking. Key Concepts: Base62 encoding, Redis cache for redirect hot path, Snowflake IDs, 301 vs 302 redirect (301 is cached by browser, 302 is not) Follow-up: Why use 301 vs 302 for the redirect, and how does that affect analytics?


40. Consistent Hashing Hard

OnsiteSystem Design · Data Structures

Implement consistent hashing with virtual nodes. Support adding/removing nodes and looking up which node is responsible for a given key.

ring.addNode("server1")  // adds K virtual nodes for server1
ring.addNode("server2")
ring.getNode("payment_key_123") → "server2"
ring.removeNode("server1")
ring.getNode("payment_key_123") → "server2" (or "server3" if exists)

Approach: Hash ring (sorted map of hash → serverName). Each server is hashed K times (virtual nodes) with suffix ":0", ":1", ... to distribute load evenly. getNode hashes the key and finds the first server clockwise (ceiling in the sorted map, wrapping around). removeNode deletes all virtual node entries. Adding/removing a server only remaps 1/n of keys. Key Concepts: Virtual nodes for uniform distribution, O(log n) lookup via sorted map, minimal key remapping on topology change Follow-up: How many virtual nodes per server is optimal? What's the trade-off?


41. Maximum Subarray (Divide and Conquer) Medium

OnsiteDivide and Conquer · DPLeetCode: &#35;53

Find the maximum sum contiguous subarray. Razorpay asks specifically for the divide-and-conquer approach to test recursion understanding, not just Kadane's.

Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6

Approach (D&C): Split array at mid. Max subarray is either entirely in left half, entirely in right half, or crosses the mid. For crossing: compute max suffix sum ending at mid and max prefix sum starting at mid+1. Combine. Complexity: Time O(n log n) D&C vs O(n) Kadane's · Space O(log n) Follow-up: Why would an interviewer ask for D&C when Kadane's is clearly better? (Testing recursion understanding, and the D&C generalises to a segment tree for range max queries.)


42. Implement Queue Using Two Stacks Easy

OnsiteStack · DesignLeetCode: &#35;232

Implement a FIFO queue using only two stacks. Push should be O(1) amortised.

push(1), push(2), peek()→1, pop()→1, empty()→false

Approach: Stack1 for input, Stack2 for output. push to Stack1. On pop/peek, if Stack2 is empty, pour all of Stack1 into Stack2 (reverses order). Pop/peek from Stack2. Complexity: Time O(1) amortised · Space O(n) Follow-up: Implement a stack using two queues (LeetCode &#35;225).


43. Merge K Sorted Lists Hard

OnsiteHeap · Linked List · D&CLeetCode: &#35;23

Merge k sorted linked lists into one sorted list.

Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]

Approach: Min-heap of size k storing (value, listIndex, node). Pop minimum, add to result, push next node from same list. Alternatively: Divide and Conquer — merge pairs of lists repeatedly (like merge sort). Complexity: Time O(N log k) heap · Space O(k) Follow-up: Razorpay frames this as merging k payment streams sorted by timestamp.


44. Trapping Rain Water II Hard

OnsiteHeap · BFS · 3DLeetCode: &#35;407

Given an m×n matrix of heights, compute how much water it can trap in 3D.

Input: heightMap = [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]]
Output: 4

Approach: Min-heap (priority queue) BFS. Start by adding all border cells. Pop the minimum-height cell, BFS its unvisited neighbours. Water above the current cell's height that can be trapped = max(0, currentBound - neighbour height). Update the bound for the neighbour as max(currentBound, neighbour height). Complexity: Time O(m*n * log(m*n)) · Space O(m*n) Follow-up: This is Razorpay's favourite "follow-up to the OA" question when a candidate solves Trapping Rain Water too quickly.


45. Numbers of Islands II (Union-Find) Hard

OnsiteUnion-Find · GraphsLeetCode: &#35;305

Given an m×n grid starting as all water, process addLand operations and after each operation return the current number of islands.

Input: m=3, n=3, positions=[[0,0],[0,1],[1,2],[2,1]]
Output: [1,1,2,3]

Approach: Union-Find with islands count. On each addLand(r,c): create a new component (islands++). Check 4 neighbours — if they're land and have a different root, union them (islands--). Path compression + union by rank for near-O(1) per operation. Complexity: Time O(k * α(m*n)) · Space O(m*n) Follow-up: How does this relate to dynamic connectivity in payment network graphs?


46. Minimum Cost to Connect All Points Medium

OnsiteGraph · MST · Prim'sLeetCode: &#35;1584

Given points on a 2D plane, find the minimum cost to connect all points where the cost of connecting two points is their Manhattan distance. (Minimum Spanning Tree problem.)

Input: points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
Output: 20

Approach: Prim's algorithm. Start with any point. Min-heap of (cost, point). Greedily add the cheapest edge to an unvisited point. Repeat until all points are in the MST. O(n² log n) total. Complexity: Time O(n² log n) · Space O(n) Follow-up: Kruskal's MST using Union-Find requires sorting all O(n²) edges — compare with Prim's for dense graphs.


47. Substring with Concatenation of All Words Hard

OnsiteSliding Window · HashingLeetCode: &#35;30

Given a string s and an array of words of equal length, return all starting indices of substrings that are a concatenation of all words in any order.

Input: s = "barfoothefoobarman", words = ["foo","bar"]
Output: [0,9]

Input: s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
Output: []

Approach: Run a sliding window starting at each offset 0 to wordLen-1. Within each offset, maintain a window of exactly totalWords words. Use two frequency maps — required and current. Slide by one word at a time, adding to the right and removing from the left. Complexity: Time O(n × wordLen) · Space O(words count) Follow-up: Why is this much harder than the original Minimum Window Substring?


48. Random Pick with Weight Medium

OnsitePrefix Sum · Binary Search · ProbabilityLeetCode: &#35;528

Given an array of weights, implement a function pickIndex() that randomly picks an index with probability proportional to its weight.

Input: w = [1,3], pickIndex() → 0 (25% chance) or 1 (75% chance)

Approach: Build prefix sum array. Generate a random float in [0, totalWeight). Binary search for the first prefix sum that exceeds the random value. Complexity: O(n) build · O(log n) per pick · Space O(n) Follow-up: Razorpay uses this for weighted routing of payments across multiple bank partners based on success rate.


49. Median of Two Sorted Arrays Hard

OnsiteBinary Search · Divide and ConquerLeetCode: &#35;4

Find the median of two sorted arrays with overall O(log(m+n)) time complexity.

Input: nums1 = [1,3], nums2 = [2]
Output: 2.0

Input: nums1 = [1,2], nums2 = [3,4]
Output: 2.5

Approach: Binary search on the smaller array. Find a partition such that the left half of the combined array has (m+n)/2 elements. Check that maxLeft1 <= minRight2 and maxLeft2 <= minRight1. Adjust the partition accordingly. Complexity: Time O(log(min(m,n))) · Space O(1) Follow-up: Kth element in two sorted arrays — the binary search partition generalises directly.


50. Design Notification System (HLD) Hard

Onsite · HLDSystem Design

Design Razorpay's notification system that sends payment success/failure alerts to merchants and customers via SMS, email, and webhooks with guaranteed delivery.

Functional: sendNotification(txnId, event, channels), getDeliveryStatus(notificationId)
Non-functional: at-least-once delivery, <2s delivery for SMS/email, webhook retry on failure

Approach: Payment events published to Kafka. Notification Service consumes events and fans out to channel-specific workers (SMS Worker → Twilio/MSG91, Email Worker → SendGrid/SES, Webhook Worker → merchant endpoint). Each worker writes to its own queue (separate Kafka topics or SQS). Retry with exponential backoff on failure (3 retries, then dead-letter queue for manual investigation). Idempotency: store notificationId in DB before sending; check on retry. Delivery status tracked in MySQL. Webhooks: store payload + signature (HMAC-SHA256) for merchant verification. Key Concepts: Fan-out via Kafka, at-least-once delivery with idempotency, exponential backoff, HMAC webhook signatures, dead-letter queues Follow-up: How do you handle a merchant's webhook endpoint being down for 24 hours — how long do you retry and what happens to the events?


Preparation Tips

Razorpay's Hard DSA round is where most candidates fail — graphs (Word Ladder, Alien Dictionary, Clone Graph) and advanced DP (Regular Expression Matching, Distinct Subsequences, Wildcard Matching) are the highest-frequency Hard problems. Study these specifically. For LLD, implement a Token Bucket rate limiter and a double-entry ledger from scratch before your interview. For HLD, the two most important concepts are idempotency (and idempotency keys) and the Saga pattern — Razorpay interviewers probe these specifically because they are core to how any payment system maintains correctness. Understand the difference between 2-phase commit and Saga, and when to use each.

Join Telegram group to get the latest Razorpay OA questions and connect with candidates currently in the Razorpay interview process.

Useful Resources