Prepare for Backend Parsing, API Integration, AI Coding, and Bug Fixing Rounds
StripeSoftware Engineer · OnsiteLast updated
Tests parsing structured text payloads, managing transactional state machines, and implementing strict idempotency semantics with robust error handling.
Payment Event Stream Processor
At payment platforms like Stripe, ingestion pipelines often receive high-volume streams of semi-structured event logs from external partner rails. These events must be parsed, validated, and applied against an account balance while respecting idempotency and lifecycle constraints.
Implement a function process_payment_events(events: list[str]) -> dict that parses a sequence of payment action records, validates each record against business rules, and returns the processing results.
Record Format
Each string in events follows the format:
ACTION|idempotency_key=;amount=;currency=;charge_id=
- Supported actions:
AUTHORIZE,CAPTURE,REFUND. - Key-value attributes are separated by semicolons (
;). - Key-value pairs may appear in any order.
AUTHORIZE: Requiresidempotency_key,amount, andcurrency. Generates a unique internalcharge_idrepresented bych_. Sets the initial authorized balance.CAPTURE: Requiresidempotency_key,charge_id, andamount. Captures up to the remaining authorized amount for that charge. Captured funds increase the merchant's settled balance.REFUND: Requiresidempotency_key,charge_id, andamount. Refunds up to the total captured amount for that charge. Refunded funds decrease the settled balance.
Business & Idempotency Rules
- Malformed Syntax: If a string lacks the pipe separator, has unknown actions, has missing mandatory fields, or contains a non-integer or non-positive amount (
amount <= 0), reject the record with reason"MALFORMED". - Idempotency:
- If an
idempotency_keyis reused with identical parameters, return the identical previous outcome without re-applying financial mutations ("DUPLICATE"). - If an
idempotency_keyis reused with different parameters, reject it with reason"IDEMPOTENCY_CONFLICT".
- If an
- State Invariants:
CAPTUREcannot capture more than the currently uncaptured authorized balance.REFUNDcannot refund more than the currently captured, unrefunded balance.- Any reference to an unknown or unauthorized
charge_idmust be rejected with reason"INVALID_CHARGE". - Over-capturing or over-refunding must be rejected with reason
"INSUFFICIENT_FUNDS".
Interface
def process_payment_events(events: list[str]) -> dict:
pass
Output Format
Return a dictionary with two keys:
"settled_balances": A dictionary mapping currency (e.g.,"USD") to the total settled amount (integer cents)."results": A list of strings, one per input event:"SUCCESS","DUPLICATE", or"ERROR: ".
Example
events = [
"AUTHORIZE|idempotency_key=k1;amount=1000;currency=usd",
"CAPTURE|idempotency_key=k2;charge_id=ch_k1;amount=600",
"CAPTURE|idempotency_key=k2;charge_id=ch_k1;amount=600",
"REFUND|idempotency_key=k3;charge_id=ch_k1;amount=700",
"REFUND|idempotency_key=k4;charge_id=ch_k1;amount=400"
]
result = process_payment_events(events)
# result == {
# "settled_balances": {"usd": 200},
# "results": [
# "SUCCESS",
# "SUCCESS",
# "DUPLICATE",
# "ERROR: INSUFFICIENT_FUNDS",
# "SUCCESS"
# ]
# }
Constraints
1 <= len(events) <= 100001 <= amount <= 10^9- Currencies are case-insensitive and should be normalized to lowercase.
Hints
Hint 1
Separate the parsing and syntax validation phase from the business execution phase to keep the code modular and easy to debug.
Hint 2
Store the full input payload alongside each idempotency key so you can distinguish an exact duplicate from an idempotency conflict.
Hint 3
Track charge state with a dedicated structure containing original currency, authorized amount, captured amount, and refunded amount.
Follow-up questions
- How would you extend this design to support concurrent workers processing events for the same merchant?
- If the event feed is an infinite stream rather than an in-memory list, how would you manage memory for idempotency key retention?
- How would you support multi-currency conversions and fee deductions during capture and refund events?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.