这轮 Stripe VO 整体给我的感觉很明确:更偏真实业务建模,而不是算法刷题。开场流程非常干脆,简单自我介绍后,两位面试官直接进入 coding 环节,全程围绕「交易系统中的费用计算」展开,一共两道题,逻辑逐步递进。我对接了 oavoservice 团队协助完成这轮 technical interview,下面把题目与解题节奏完整还原出来。
一、整体节奏:先 clarify,再动手
Stripe Coding 的一个鲜明特点是:写代码之前的澄清比代码本身更重要。两道题都是先抛业务背景,让你自己问清楚规则边界,再开始实现。如果上来就闷头写,很容易写出「能跑但不像生产代码」的东西。
| 阶段 | 重点 |
|---|---|
| Clarify | 不同交易类型 / 状态是否计费?边界规则是什么? |
| 建模 | 解析、交易模型、费用计算、输出结果四层解耦 |
| 实现 | 避免把业务逻辑写死,方便扩展和测试 |
| Followup | 新增交易类型 / 费率调整时改动是否可控 |
二、Part 1:解析交易 CSV,计算每个用户费用
第一题给一个交易记录的 CSV 字符串,要求解析后计算每笔交易中每个用户需要支付的费用。题目表面像字符串解析,但真正写代码前,面试官先抛了几个关键澄清问题:
- 不同交易类型(支付、退款)是否适用不同费用规则?
- 处于 pending 或退款中的交易是否需要计费?
- 金额单位、精度怎么处理?
规则确认清楚后,这题的重点就很明确了:不在 CSV parsing 本身,而在你能否在不把业务逻辑写死的前提下,把费用规则表达清楚。
我的做法是刻意避免把所有逻辑堆在一个函数里,而是先把职责拆开:
from dataclasses import dataclass
from typing import List, Dict
@dataclass
class Transaction:
"""交易模型:只描述基础信息,不掺杂费用逻辑"""
user_id: str
amount: int # 以最小货币单位存储,避免浮点误差
status: str # completed / pending / refunded
method: str
country: str
def parse_transactions(csv_text: str) -> List[Transaction]:
"""解析层:只负责把原始字符串转成结构化对象"""
txns = []
for line in csv_text.strip().splitlines():
uid, amount, status, method, country = line.split(",")
txns.append(Transaction(uid, int(amount), status, method, country))
return txns
费用计算逻辑单独封装,根据交易类型和状态应用对应规则:
def fee_for(txn: Transaction) -> int:
"""费用层:规则集中在这里,解析逻辑不污染业务逻辑"""
if txn.status != "completed": # pending / refunded 不计费
return 0
if txn.method == "refund":
return 0
# 基础规则:2.9% + 固定 30(最小货币单位)
return round(txn.amount * 0.029) + 30
def total_fee_per_user(csv_text: str) -> Dict[str, int]:
result: Dict[str, int] = {}
for txn in parse_transactions(csv_text):
result[txn.user_id] = result.get(txn.user_id, 0) + fee_for(txn)
return result
这样拆分的好处很直接:解析逻辑和业务规则不会互相污染,未来新增交易类型或费用规则,只需改动对应模块,不会牵一发而动全身。
面试官的 followup 几乎都围绕工程可维护性:
- 如果未来多了一种交易状态怎么办?→ 答:状态判断集中在
fee_for,新增分支即可,解析层不动。 - 费用规则调整是否需要重构?→ 答:规则是数据 + 单一函数,调整成本低。
- 如何保证逻辑清晰、容易测试?→ 答:每层都是纯函数,可单测。
这些问题本质上不是在考「能不能写出来」,而是在看你是不是习惯用工程化方式思考。
三、Part 2:基于国家与支付方式的动态费率
第二题在第一题基础上深化,只对 completed 的交易做进一步处理,并引入国家和支付提供商两个新变量。不同国家、不同支付方式对应不同费率结构,要算出最终实际费用。
这里我选择用 查找表(Lookup Table) 处理费率,而不是堆一堆 if-else:
# (payment_provider, buyer_country) -> (variable_rate, fixed_fee)
RATE_TABLE = {
("visa", "US"): (0.029, 30),
("visa", "DE"): (0.014, 25),
("visa", "FR"): (0.014, 25),
("visa", "AT"): (0.014, 25), # 同费率国家也分别列出,保持实现简单可读
("paypal", "US"): (0.034, 49),
}
DEFAULT_RATE = (0.039, 30) # 兜底费率
def dynamic_fee(txn: Transaction) -> int:
if txn.status != "payment_completed":
return 0
rate, fixed = RATE_TABLE.get((txn.method, txn.country), DEFAULT_RATE)
return round(txn.amount * rate) + fixed
这个设计的优势很明显:
- 规则集中在一处,逻辑直观;
- 新增国家或支付方式时,只更新查找表,不影响主流程;
- 避免多层条件判断导致的可读性和维护性问题。
对于德国、法国、奥地利这类费率相同的国家,我也选择在查找表里分别列出,而不是额外做国家分组抽象——保持实现简单、可读,这正是 Stripe 想看到的取舍。
四、面试整体评价
整体来看,这轮 Stripe VO 不难在算法本身,而是考你是否具备把业务问题工程化的能力。面试官更关注:
- 对需求的理解是否到位(clarify 充分);
- 数据模型是否合理(分层解耦);
- 代码结构是不是符合真实生产环境的写法;
- 设计时有没有提前考虑扩展性和各种边界情况。
如果平时更习惯用 LeetCode 模板思路解题,很容易在这种题里显得「能写出来,但不太像实际工程代码」。
五、总结
Stripe Coding 的评分锚点是工程化实现质量:先 clarify 再动手、按职责分层、用数据(查找表)代替硬编码分支、随时回应扩展性 followup。把这套思维练成肌肉记忆,比多刷几十道 hard 更管用。
FAQ
Q1:Stripe VO 的 Coding 到底考什么?
考真实业务建模而非算法技巧。本轮两题都围绕「交易费用计算」递进:Part 1 解析 CSV 算每个用户费用,Part 2 引入国家 / 支付方式做动态费率。重点是分层解耦与扩展性。
Q2:为什么用 Lookup Table 而不是 if-else?
费率规则随国家 × 支付方式组合膨胀,if-else 会越堆越乱。查找表把规则集中成数据,新增组合只改一行表,主流程不动,可读性和可测试性都更好。
Q3:面试官的 followup 一般问什么?
几乎都围绕工程可维护性:新增交易状态怎么办、费率调整要不要重构、如何保证可测试。答题时强调「分层 + 纯函数 + 规则即数据」即可对上他们的评分点。
Q4:怎么准备 Stripe Coding?
把「先 clarify 再动手」练成习惯,刻意训练分层(解析 / 模型 / 规则 / 输出)写法,并对每个设计点准备好扩展性回答。如果想要这两道真题的限时陪练,或需要 VO代面 / VO辅助 的实时对接,可以发岗位 JD 先做题型预测再排练习计划。
正在准备 Stripe 面试?
Stripe VO Coding 考的是工程建模 + 分层解耦 + 扩展性思维,不是刷题量。oavoservice 提供 Stripe 全流程陪练:交易费用建模、Lookup Table 取代 if-else 的取舍演练、followup 应答训练,也支持 VO代面 / VO辅助 的实时对接。教练含前大厂资深工程师,熟悉 Stripe「愿意 merge 的代码质量」评分风格。
立即添加微信 Coding0201,获取 Stripe 真题与陪练。
联系方式
- 微信:Coding0201
- Email:[email protected]
- Telegram:@OAVOProxy