Reported September 2026
Airbnbdesign

Progressive Banking System with Cashback

Reported by candidates from Airbnb's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.

Get StealthCoderRuns invisibly during the live Airbnb OA. Under 2s to a working solution.
Founder's read

The Airbnb OA reported in September 2026 looks like a normal bank class until mergeAccounts shows up and quietly breaks every shortcut you took in levels 1 to 3. It's a design problem: four levels, one growing class, and a batch function that parses string rows. Cashback, payment IDs, and historical balances all have to survive a merge. If you're taking this in the next day or two, the trick is planning for level 4 before you write level 1. StealthCoder sits invisibly on your screen as a safety net if you blank mid-assessment, but the plan below gets you most of the way.

The problem

Implement a progressive banking system. All operations have a timestamp parameter — a stringified timestamp in milliseconds. All timestamps are unique, lie in the range from 1 to 10^9, and are supplied in strictly increasing order.
Before every operation at timestamp t, process every cashback whose due time is at most t. A cashback due exactly at t is credited before that operation.
Level 1
Initially, the banking system does not contain any accounts. Support account creation, deposits, and transfers between two different accounts:
createAccount(timestamp, accountId) creates a new account with the given identifier if it does not already exist. It returns true when the account is created and false when an active account with accountId already exists.
deposit(timestamp, accountId, amount) deposits the given amount into the specified account and returns its resulting balance. It returns null when the account does not exist.
transfer(timestamp, sourceAccountId, targetAccountId, amount) transfers the given amount from the source account to the target account and returns the resulting source balance. It returns null when either account is missing, the identifiers are equal, or the source has insufficient funds.
Level 2
topSpenders(timestamp, n) returns up to n active accounts with the highest total outgoing transactions. Outgoing transactions include successful transfers out and successful payments. Sort by outgoing total in descending order, then by accountId in ascending lexicographic order. Format every entry as accountId(totalOutgoing). If fewer than n accounts exist, return all of them. Cashback never counts as outgoing activity.
Level 3
pay(timestamp, accountId, amount) withdraws the amount when the account exists and has sufficient funds. A successful payment contributes to outgoing activity, returns the next global identifier paymentK, and schedules floor(amount * 2 / 100) cashback for timestamp + 86400000. A failed payment returns null and does not consume an identifier.
getPaymentStatus(timestamp, accountId, payment) returns IN_PROGRESS before that payment's cashback is processed and CASHBACK_RECEIVED afterward. It returns null when the account or payment does not exist, or when the payment belongs to a different current account lineage.
Level 4
mergeAccounts(timestamp, accountId1, accountId2) merges accountId2 into accountId1. It returns false if the identifiers are equal or either active account is missing; otherwise it returns true.
The survivor receives the absorbed balance and outgoing total. Pending cashback follows the survivor, old payments from the absorbed account become queryable through the survivor, and accountId2 is removed. A removed identifier may later be created as a new, independent account.
getBalance(timestamp, accountId, timeAt) returns the account balance immediately after all activity at timeAt, or after the latest earlier activity when nothing happened exactly then. It returns null if accountId is not active at the query timestamp or if that surviving account did not yet exist at timeAt.
After a merge, the survivor inherits the absorbed account's balance history. For a historical time when both lineages existed, the survivor's historical balance is their sum; internal transfers between the two lineages cancel naturally.
FastPrep Batch Interface
Implement processBankingQueries(queries). Each row contains an uppercase operation name followed by the arguments shown below. Return one string per query in the same order.
Use CREATE_ACCOUNT, DEPOSIT, TRANSFER, TOP_SPENDERS, PAY, GET_PAYMENT_STATUS, MERGE_ACCOUNTS, and GET_BALANCE.
Serialize booleans as true or false, numbers in base 10, and every conceptual null as the empty string.
Serialize TOP_SPENDERS by joining its formatted entries with commas and no spaces.

Function
processBankingQueries(queries: String[][]) → String[]

Examples
Example 1
queries = [["CREATE_ACCOUNT","1","alice"],["CREATE_ACCOUNT","2","bob"],["DEPOSIT","3","alice","1000"],["TRANSFER","4","alice","bob","250"],["TOP_SPENDERS","5","2"]]
return = ["true","true","1000","750","alice(250),bob(0)"]
The transfer leaves alice with 750 and records 250 of outgoing activity for alice. Bob has no outgoing activity, so alice ranks first.
Example 2
queries = [["CREATE_ACCOUNT","1","a"],["DEPOSIT","2","a","1000"],["PAY","3","a","200"],["GET_PAYMENT_STATUS","4","a","payment1"],["DEPOSIT","86400003","a","1"],["GET_PAYMENT_STATUS","86400004","a","payment1"],["GET_BALANCE","86400005","a","86400003"]]
return = ["true","1000","payment1","IN_PROGRESS","805","CASHBACK_RECEIVED","805"]
Paying 200 leaves 800 and schedules floor(2% of 200) = 4 for timestamp 86400003. The cashback is credited before the deposit at that same timestamp, so the deposit returns 805.
Example 3
queries = [["CREATE_ACCOUNT","1","a"],["DEPOSIT","2","a","100"],["CREATE_ACCOUNT","3","b"],["DEPOSIT","4","b","500"],["PAY","5","b","200"],["MERGE_ACCOUNTS","6","a","b"],["GET_PAYMENT_STATUS","7","a","payment1"],["GET_PAYMENT_STATUS","8","b","payment1"],["TOP_SPENDERS","9","2"],["GET_BALANCE","10","a","4"],["GET_BALANCE","11","a","5"],["GET_BALANCE","86400005","a","86400005"]]
return = ["true","100","true","500","payment1","true","IN_PROGRESS","","a(200)","600","400","404"]
After b is absorbed, its payment is queried through a and its outgoing total belongs to a. The inherited history totals both lineages, and the pending cashback is later credited to a.

Constraints
1 <= queries.length <= 2000.
External timestamps are unique, strictly increasing integers from 1 through 10^9.
Every query uses one valid operation shape described above, and 1 <= timeAt <= timestamp.
Account identifiers match [a-z][a-z0-9_]{0,19}.
Amounts are positive integers at most 10^9, and every balance and outgoing total fits in a signed 64-bit integer.
1 <= n <= 10^5 for TOP_SPENDERS.

Reported by candidates. Source: FastPrep

Pattern and pitfall

The pattern is design plus simulation. Process due cashbacks before every operation, using a min-heap or a queue keyed by due time. Since timestamps strictly increase, a plain FIFO queue works, because every cashback is due exactly 86400000 after its payment. The edge case that breaks naive code is the merge. Pending cashback must follow the survivor, old payment IDs must resolve through the survivor, and a removed ID can be recreated as an independent account. So key payments by an internal account lineage ID, not the name string. Store per-account balance history as timestamped snapshots and binary search for getBalance. After a merge, the survivor's history at earlier times is the sum of both lineages. Pitfalls: failed payments must not consume an ID, cashback must not count as outgoing, and null serializes as an empty string. If the merge logic tangles on the live OA, StealthCoder can give you a working structure.

If this hits your live OA and you blank, StealthCoder solves it in seconds, invisible to the proctor.

If this hits your live OA

You can drill Progressive Banking System with Cashback cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built by an Amazon engineer who would have shipped this the night before his JPMorgan OA if he'd had it.

Get StealthCoder

Related leaked OAs

⏵ The honest play

You've seen the question. Make sure you actually pass Airbnb's OA.

Airbnb reuses patterns across OAs. Built by an Amazon engineer who would have shipped this the night before his JPMorgan OA if he'd had it. Works on HackerRank, CodeSignal, CoderPad, and Karat.

Progressive Banking System with Cashback FAQ

What's the real trick in this Airbnb banking problem?+

Separate the account name from the account's internal identity. Payments, pending cashback, and balance history attach to an internal ID. Then mergeAccounts becomes a re-pointing operation, and recreating a removed name later doesn't corrupt old payments.

How do I handle cashback timing correctly?+

Before every operation at timestamp t, pop all cashbacks with due time at most t and credit them. A cashback due exactly at t is credited first. Every delay is the same 86400000, so a FIFO queue stays sorted. Cashback adds to balance but never to outgoing totals.

How should getBalance with timeAt work after merges?+

Keep a list of (timestamp, balance) snapshots per account. Binary search for the latest entry at or before timeAt. On merge, the survivor's history for times when both existed is the sum of both lineages. Return null if the survivor didn't exist at timeAt.

What are the common bugs in topSpenders?+

Sorting by the wrong tie-break is the big one. Sort by outgoing total descending, then accountId ascending lexicographically. Format as accountId(total), join with commas and no spaces. Count only transfers out and successful payments, never cashback or deposits.

How do I prepare for this in 48 hours?+

Write the class incrementally, one level at a time, and test each level against the examples. Spend your time on levels 3 and 4, since they carry the hidden edge cases. Practice the string-row batch parser too, including empty-string output for null.

Problem reported by candidates from a real Online Assessment. Sourced from a publicly-available candidate-aggregated repository. Not affiliated with Airbnb.

OA at Airbnb?
Invisible during screen share
Get it