Banking System Coding 面试深度解析:账户、转账、Top Spender、Scheduled Payment 和 Merge
基于 Airbnb、Affirm、Stripe、Plaid 等 FinTech / Marketplace 面经抽象的 banking system coding 面试指南,拆解 ledger、idempotency、scheduled payment、account merge、history balance、edge cases、测试和英文讲解。
Banking system coding 是 FinTech、marketplace、payment、risk 和 ledger 类岗位里非常高频的题型。你可能面的是 Airbnb、Affirm、Stripe、Plaid、Robinhood、Coinbase,也可能是一个普通 backend team,但题目会长得很像:创建账户、存钱、转账、查询 top spender、执行 scheduled payment、merge account、查询历史余额。
这类题不是难在算法,而是难在状态会不断变化。每一问都建立在前一问的数据模型上。如果你一开始只用一个 balance map 硬写,后面遇到 scheduled payment、account merge、history balance 时会非常痛苦。
这篇文章基于多类公开面经做匿名抽象,拆解 banking system coding 的稳定建模方式和面试表达。
快速答案
Banking system coding 面试主要考:
- 你能不能设计稳定的 state model。
- 你能不能处理时间顺序和 delayed execution。
- 你能不能区分 balance、transaction、ledger、history。
- 你能不能在 multi-part follow-up 下保持代码可扩展。
- 你能不能主动提 edge cases:重复请求、余额不足、账户不存在、merge 后历史查询。
这类题要先建模,再写函数。不要一上来就写代码。
常见题目结构
典型面试会分成 4-5 个 level:
| Level | 功能 | 核心考点 |
|---|---|---|
| 1 | create account、deposit、transfer | 基础状态和输入校验 |
| 2 | top spender / top activity | transaction aggregation、排序和 tie breaker |
| 3 | scheduled payment | 时间推进、pending task、execution order |
| 4 | merge account | identity、alias、history preservation |
| 5 | historical balance | ledger / snapshots / event replay |
越往后越不是“写个 map”能解决的问题。
第一步:先定义数据模型
面试前 2-3 分钟,你应该先和面试官确认:
- account id 是否唯一?
- transaction timestamp 是否单调递增?
- transfer 是原子操作吗?
- scheduled payment 是在每次 API call 前执行,还是有独立 scheduler?
- merge 后旧 account id 是否还能查询历史?
- top spender 统计 outgoing amount 还是 total activity?
一个稳的初始模型:
accounts:
account_id -> {
active: boolean
balance: number
created_at: timestamp
}
ledger:
list of {
txn_id
timestamp
type
account_id
counterparty_id
amount
status
}
outgoing_total:
account_id -> number
scheduled_payments:
min-heap ordered by execute_at, payment_id
alias:
old_account_id -> canonical_account_id
你不一定一开始全部实现,但脑子里要有这个方向。
面试中可以说:
“I will start with current balance for fast operations, but I will also record ledger events because later follow-ups like historical balance or account merge are much easier with an append-only transaction log.”
这句话能直接体现你考虑到了后续扩展。
Level 1:账户、存款、转账
基础 API 通常包括:
createAccount(timestamp, accountId)deposit(timestamp, accountId, amount)transfer(timestamp, sourceId, targetId, amount)
常见规则:
- 创建重复账户失败。
- 存款到不存在账户失败。
- 转账双方必须存在且 active。
- 不能自己转给自己。
- 余额不足失败。
- amount 必须为正数。
- 成功后返回新余额或 transaction id。
这部分不要写复杂。重点是输入校验顺序清楚。
更稳的 transfer 逻辑:
resolve source and target canonical accounts
validate accounts exist and active
validate source != target
validate amount > 0
validate source balance >= amount
debit source
credit target
append debit and credit ledger events
update outgoing_total[source]
return success
很多候选人会忘记 top spender 需要 outgoing_total,所以后面再补时要回扫全部交易。可以提前在成功转账时维护。
Level 2:Top Spender / Top Activity
这类 follow-up 常见要求是:
“Return top N accounts by total outgoing transfer amount. If tie, sort by account id.”
关键点:
- 只统计成功 outgoing transfer,失败不算。
- deposit 不算 outgoing。
- scheduled payment 执行成功后通常算 outgoing。
- merge 后金额归到 canonical account 还是保留旧 account,要问清楚。
- tie breaker 要明确。
如果 N 很小、账户不多,直接排序可以:
sort accounts by (-outgoing_total, account_id)
return top n
如果面试官追问 scale,可以讲:
- 读多写少:每次 query 排序可以接受。
- 写多读多:维护 balanced tree / heap with lazy update。
- 分布式:按 account shard 聚合,再做 top-k merge。
但现场 coding 不要过早优化。先写对。
Level 3:Scheduled Payment
Scheduled payment 是这类题最容易出错的部分。
常见 API:
schedulePayment(timestamp, accountId, amount, delay)cancelPayment(timestamp, accountId, paymentId)- 每次 operation 前,先执行所有
execute_at <= timestamp的 pending payment。
关键问题:
- 如果到期时余额不足,payment 是失败、跳过、还是继续 pending?
- cancel 一个已经执行的 payment 要返回什么?
- 同一时间多个 payment 执行顺序是什么?
- payment id 如何生成?
- scheduled payment 是否计入 outgoing total?
稳的实现方式:
processDuePayments(timestamp):
while heap not empty and heap.peek.execute_at <= timestamp:
payment = pop
if payment.status != pending:
continue
account = resolve(payment.account_id)
if account active and balance >= amount:
debit balance
mark executed
append ledger event
outgoing_total[account] += amount
else:
mark failed
每个 public API 一开始先调用 processDuePayments(timestamp)。这样系统状态始终推进到当前时间。
注意不要遍历所有 scheduled payments,这会慢。用 min-heap 按 execute_at 排序。
Level 4:Merge Account
Account merge 通常是最容易把代码打乱的 follow-up。比如:
“Merge account B into account A. Balance should move to A. Future operations on B should fail or redirect. Historical balance queries should still work.”
这里一定要问清楚语义。常见选择:
- current balance:B 的余额转入 A,B inactive。
- future operations:B 不再允许 deposit / transfer。
- historical query:B 在 merge 前的历史仍然能查。
- top spender:B 的 outgoing total 是否合并到 A。
- scheduled payments:B 的 pending payment 是否转到 A,还是取消。
一个 pragmatic 做法:
canonical(account_id):
follow alias until active canonical account
merge(timestamp, target, source):
processDuePayments(timestamp)
validate target and source active
target.balance += source.balance
target.outgoing_total += source.outgoing_total
source.active = false
alias[source] = target
append merge ledger event
但是 historical balance 不能只靠 current balance。你需要 ledger。
面试中可以说:
“For current operations I will resolve aliases to canonical accounts. For historical queries I will not rewrite old ledger events; I will keep original account ids in the ledger so pre-merge history remains answerable.”
这能避免很多逻辑混乱。
Level 5:History Balance
最后一问常见是:
“Return balance of account X at timestamp T.”
如果你只存 current balance,就很难答。常见方案有三种。
方案一:Event replay
从 ledger 开始回放到时间 T,计算余额。
优点:实现简单,历史准确。
缺点:每次 query 慢。
适合面试 coding 的第一版。
方案二:Balance snapshots
每次交易后记录:
balance_history[account_id] = [
(timestamp, balance)
]
查询时二分找到 timestamp <= T 的最后一条。
优点:查询快。
缺点:merge、alias 和 backfill 更复杂。
方案三:Ledger + periodic snapshot
生产系统常用折中:定期 snapshot,加上之后的 events replay。
面试中可以先实现方案二,因为代码比较直接:
recordBalance(account_id, timestamp):
history[account_id].append((timestamp, current_balance))
getBalance(account_id, timestamp):
entries = history[account_id]
idx = upper_bound(entries, timestamp) - 1
return entries[idx].balance
merge 的时候要注意:source account merge 后,旧 account 的历史不能消失;target account 在 merge timestamp 的 balance 要记录一次。
Ledger 思维为什么重要
Banking system 题本质是 ledger 题。真实支付系统不会只存一个余额字段,因为你需要:
- audit trail
- dispute / refund
- idempotency
- reconciliation
- historical balance
- fraud investigation
- compliance
即使现场代码不需要完整 ledger,你也应该在 design discussion 里提到。
可以这样表达:
“For the coding version, I will keep current balance for simplicity. But I will also append transaction records so that audit, history, merge, and reconciliation can be supported later.”
这句话对 FinTech 面试很加分。
必测 edge cases
建议现场主动说你会测这些:
- create duplicate account
- deposit negative amount
- transfer to self
- transfer with insufficient balance
- transfer to missing account
- top spender tie breaker
- scheduled payment with insufficient balance
- cancel already executed payment
- two payments at same timestamp
- merge missing account
- merge account into itself
- transfer from merged account
- historical balance before account creation
- historical balance after merge
如果时间不够,至少手动 dry run 3-4 个高风险 case。
常见挂法
只用 balance map
前两问能过,后面 history 和 merge 会崩。
没有统一处理时间推进
scheduled payment 到期后,有的 API 执行了,有的 API 忘了执行,状态不一致。
Merge 后重写历史
把 source account 的历史全改到 target account,导致 pre-merge 查询失真。
Top spender 统计错
把 deposit 算进去,或者失败 transfer 也算出去。
英文不讲 trade-off
FinTech 面试官很在意 reliability。你只写代码不讲 idempotency、audit、reconciliation,会少很多信号。
英文讲解模板
可以提前准备这些表达:
- “I will keep current balances for fast reads, and append ledger events for audit and historical queries.”
- “Before every public operation, I will process scheduled payments due at or before the current timestamp.”
- “For merge, I will keep old ledger events unchanged so historical balance remains correct.”
- “Only successful outgoing transfers should count toward top spender.”
- “In production, I would add idempotency keys to avoid double charging on retries.”
- “If this needs to scale, I would shard accounts by account id and reconcile ledger events asynchronously.”
一周准备计划
第 1 天:基础账户 API
写 create、deposit、transfer,补所有 validation。
第 2 天:Top N
实现 outgoing total、排序、tie breaker,练 scale follow-up。
第 3 天:Scheduled Payment
实现 min-heap、cancel、process due payments、同 timestamp 顺序。
第 4 天:Merge Account
实现 active flag、alias、balance merge、pending payment 语义。
第 5 天:History Balance
实现 balance snapshots 和 binary search。
第 6 天:Ledger Discussion
准备 audit、idempotency、reconciliation、refund、duplicate request 的回答。
第 7 天:完整 mock
用 60 分钟从 level 1 写到 level 4,最后 15 分钟做 code review 和 system follow-up。
相关阅读
- FinTech SDE 面试准备指南
- Stripe Debugging 和 Integration 面试准备指南
- Plaid SDE 面试流程解析
- LeetCode Coding Strategy Guide
需要 FinTech / Stateful Coding Mock?
Banking system coding 最需要练的是状态建模、时间推进、edge cases 和英文解释。Interview Coach Pro 可以按 Stripe、Airbnb、Affirm、Plaid、Robinhood、Coinbase 这类风格做 stateful coding mock,帮你把多问 follow-up 练到稳定。
相关面试辅导
如果你正在准备类似面试,可以直接从下面的专项辅导开始。