PracHub
QuestionsPremiumCoachesLearningGuidesInterview Prep

Quick Overview

This question evaluates stateful transaction processing and aggregation skills, including per-(account,currency) balance maintenance, timestamp-based ordering, rejection rules, and overdraft coverage via a platform account.

  • medium
  • Stripe
  • Coding & Algorithms
  • Software Engineer

Compute account balances with rejection and overdraft

Company: Stripe

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

You are given a list of transaction records as strings. Each record has the format: ``` account_id,timestamp,currency,amount ``` - `account_id`: string identifier - `timestamp`: integer (not guaranteed sorted) - `currency`: string like `USD`, `EUR` - `amount`: integer (can be positive for credit or negative for debit) Maintain balances **per (account_id, currency)**. ## Part 1 — Final balances Process all transactions and return the final balance for each `(account_id, currency)`. - Exclude entries whose final balance is exactly `0`. - Output can be in any order (unless you choose to sort). ## Part 2 — Reject transactions that would go negative Now process transactions in **increasing `timestamp` order** (tie-breaker can be input order). A transaction is **rejected** if applying it would make that account’s balance for that `currency` fall below `0`. - Rejected transactions have no effect. - Return final non-zero balances as in Part 1. ## Part 3 — Use a platform account for insufficient funds Add support for a special “platform” account that can cover shortfalls. You are given `platform_account_id` as a parameter. Processing is still in **timestamp order**. For a debit transaction (negative `amount`) on account `A` in currency `C`: 1. If `A` has enough balance in `C`, apply it normally. 2. Otherwise, attempt to cover the shortfall using the **platform account’s balance in the same currency `C`**: - Deduct all remaining funds from `A` (bringing it to `0`). - Deduct the remaining required amount from the platform account. 3. If the platform account also lacks sufficient funds in `C`, **reject** the transaction (no balances change). Credits (positive `amount`) always apply. Return final non-zero balances for all accounts (including the platform account if non-zero). ### Constraints (you may assume) - Up to ~10^5 transactions - `timestamp` fits in 64-bit integer - `amount` fits in 64-bit signed integer

Quick Answer: This question evaluates stateful transaction processing and aggregation skills, including per-(account,currency) balance maintenance, timestamp-based ordering, rejection rules, and overdraft coverage via a platform account.

Part 1: Final Balances by Account and Currency

You are given transaction records as strings in the format 'account_id,timestamp,currency,amount'. Compute the final balance for each (account_id, currency) pair by summing all amounts. Timestamps do not matter in this part. Exclude any pair whose final balance is exactly 0. For deterministic grading, return the answer sorted lexicographically by account_id and then currency, with each row formatted as 'account_id,currency,balance'.

Constraints

  • 0 <= len(transactions) <= 10^5
  • timestamp fits in 64-bit signed integer
  • amount fits in 64-bit signed integer
  • Each transaction string is well-formed

Examples

Input: (['alice,10,USD,100','bob,12,EUR,40','alice,11,USD,-30','alice,15,EUR,50'],)

Expected Output: ['alice,EUR,50','alice,USD,70','bob,EUR,40']

Explanation: USD and EUR are tracked separately for alice.

Input: (['a,1,USD,10','a,2,USD,-10','b,3,USD,5'],)

Expected Output: ['b,USD,5']

Explanation: Account a ends at 0 USD and is excluded.

Input: ([],)

Expected Output: []

Explanation: Edge case: no transactions means no balances.

Input: (['a,1,USD,-7'],)

Expected Output: ['a,USD,-7']

Explanation: Negative final balances are allowed in Part 1.

Solution

def solution(transactions):
    balances = {}
    for record in transactions:
        account_id, _timestamp, currency, amount = record.split(',')
        key = (account_id, currency)
        balances[key] = balances.get(key, 0) + int(amount)

    rows = []
    for (account_id, currency), balance in balances.items():
        if balance != 0:
            rows.append((account_id, currency, balance))
    rows.sort()

    return [f'{account_id},{currency},{balance}' for account_id, currency, balance in rows]

Time complexity: O(n + k log k), where n is the number of transactions and k is the number of distinct (account_id, currency) pairs. Space complexity: O(k).

Hints

  1. Use a hash map keyed by (account_id, currency).
  2. Aggregate first, then filter out zero balances and sort once at the end.

Part 2: Final Balances with Rejected Overdrafts

You are given transaction records as strings in the format 'account_id,timestamp,currency,amount'. Process transactions in increasing timestamp order. If two transactions have the same timestamp, keep their original input order. Maintain balances separately for each (account_id, currency). A transaction is rejected if applying it would make that balance go below 0. Rejected transactions have no effect. Return all non-zero final balances sorted lexicographically by account_id and then currency, using the row format 'account_id,currency,balance'.

Constraints

  • 0 <= len(transactions) <= 10^5
  • timestamp fits in 64-bit signed integer
  • amount fits in 64-bit signed integer
  • Each transaction string is well-formed

Examples

Input: (['alice,5,USD,-30','alice,1,USD,100','alice,3,USD,-80','bob,2,EUR,20','bob,4,EUR,-25'],)

Expected Output: ['alice,USD,20','bob,EUR,20']

Explanation: After sorting by time, bob's debit and alice's last debit are both rejected.

Input: (['a,2,USD,-5','a,2,USD,10'],)

Expected Output: ['a,USD,10']

Explanation: Same timestamp uses input order, so the debit is seen first and rejected.

Input: (['a,1,USD,10','a,2,USD,-10'],)

Expected Output: []

Explanation: The final balance is exactly 0, so it is omitted.

Input: ([],)

Expected Output: []

Explanation: Edge case: empty input.

Solution

def solution(transactions):
    ordered = []
    for index, record in enumerate(transactions):
        account_id, timestamp, currency, amount = record.split(',')
        ordered.append((int(timestamp), index, account_id, currency, int(amount)))
    ordered.sort()

    balances = {}
    for _timestamp, _index, account_id, currency, amount in ordered:
        key = (account_id, currency)
        current = balances.get(key, 0)

        if amount < 0 and current + amount < 0:
            continue

        new_balance = current + amount
        if new_balance == 0:
            balances.pop(key, None)
        else:
            balances[key] = new_balance

    rows = [(account_id, currency, balance) for (account_id, currency), balance in balances.items()]
    rows.sort()
    return [f'{account_id},{currency},{balance}' for account_id, currency, balance in rows]

Time complexity: O(n log n + k log k), where n is the number of transactions and k is the number of distinct (account_id, currency) pairs. Space complexity: O(n + k).

Hints

  1. Sort by timestamp, but keep the original index so ties follow input order.
  2. Before applying a debit, check whether current_balance + amount would be negative.

Part 3: Final Balances with Platform Shortfall Coverage

You are given transaction records as strings in the format 'account_id,timestamp,currency,amount' and a special platform_account_id. Process transactions in increasing timestamp order, breaking ties by original input order. Credits always apply. For a debit on account A in currency C: if A has enough funds, apply it normally. Otherwise, try to cover the shortfall from the platform account in the same currency C. If the platform can cover the shortfall, bring A to 0 and deduct the remaining amount from the platform. If the platform also lacks enough funds, reject the transaction and leave all balances unchanged. If the debit is on the platform account itself, it cannot cover itself beyond its own balance; reject it if it would go negative. Return all non-zero final balances sorted lexicographically by account_id and then currency, using the row format 'account_id,currency,balance'.

Constraints

  • 0 <= len(transactions) <= 10^5
  • timestamp fits in 64-bit signed integer
  • amount fits in 64-bit signed integer
  • platform_account_id is a string identifier
  • Each transaction string is well-formed

Examples

Input: (['platform,1,USD,100','alice,2,USD,30','alice,3,USD,-50'],'platform')

Expected Output: ['platform,USD,80']

Explanation: Alice uses her 30 first, and the platform covers the remaining 20.

Input: (['platform,1,USD,10','alice,2,USD,5','alice,3,USD,-20'],'platform')

Expected Output: ['alice,USD,5','platform,USD,10']

Explanation: The shortfall is 15, but the platform only has 10 USD, so the debit is rejected.

Input: (['platform,1,EUR,50','alice,2,USD,10','alice,3,USD,-15'],'platform')

Expected Output: ['alice,USD,10','platform,EUR,50']

Explanation: Platform funds must be in the same currency, so EUR cannot cover a USD shortfall.

Input: (['platform,1,USD,10','platform,2,USD,-15','alice,3,USD,2','alice,4,USD,-1'],'platform')

Expected Output: ['alice,USD,1','platform,USD,10']

Explanation: A debit on the platform account itself is rejected if it would make the platform balance negative.

Input: ([],'platform')

Expected Output: []

Explanation: Edge case: no transactions.

Solution

def solution(transactions, platform_account_id):
    ordered = []
    for index, record in enumerate(transactions):
        account_id, timestamp, currency, amount = record.split(',')
        ordered.append((int(timestamp), index, account_id, currency, int(amount)))
    ordered.sort()

    balances = {}

    def set_balance(key, value):
        if value == 0:
            balances.pop(key, None)
        else:
            balances[key] = value

    for _timestamp, _index, account_id, currency, amount in ordered:
        key = (account_id, currency)
        current = balances.get(key, 0)

        if amount >= 0:
            set_balance(key, current + amount)
            continue

        debit = -amount

        if current >= debit:
            set_balance(key, current - debit)
            continue

        if account_id == platform_account_id:
            continue

        shortfall = debit - current
        platform_key = (platform_account_id, currency)
        platform_balance = balances.get(platform_key, 0)

        if platform_balance < shortfall:
            continue

        set_balance(key, 0)
        set_balance(platform_key, platform_balance - shortfall)

    rows = [(account_id, currency, balance) for (account_id, currency), balance in balances.items()]
    rows.sort()
    return [f'{account_id},{currency},{balance}' for account_id, currency, balance in rows]

Time complexity: O(n log n + k log k), where n is the number of transactions and k is the number of distinct (account_id, currency) pairs. Space complexity: O(n + k).

Hints

  1. For an insufficient debit, compute shortfall = debit_amount - account_balance.
  2. Only change balances after confirming the platform has enough in the same currency.
Last updated: Jun 2, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Assign Reviewers from Changed Files - Stripe (medium)
  • Generate Account Email Notifications - Stripe (medium)
  • Calculate Transaction Fees - Stripe (medium)
  • Build an Account Transfer Ledger - Stripe (medium)
  • Implement Validation and String Compression - Stripe (hard)