Reported April 2026
Stripehash table

Payment Ledger with Refunds and Date Queries

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

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

Stripe reported this one in April 2026, and it looks like a plain simulation until you read the constraint: up to 100000 operations, with date queries mixed in. Scan every payment on each PAYMENTS_ON or PAYMENTS_BETWEEN call and you're at 10^10 work. This is a ledger problem with strict validation, partial refunds, and range queries over dates. If you've got an OA invite, expect the same shape. StealthCoder sits in the background as a safety net if you blank mid-assessment, but the structure here is learnable in one evening.

The problem

Implement a lightweight payment ledger that records payments, applies full or partial refunds, tracks net revenue, and retrieves recorded payments by date.
Implement processPaymentLedger. Process the operations in order and return one result string for every operation.
Operation format
PAYMENT payment_id timestamp amount: record a payment with a unique ID, a strict UTC timestamp in YYYY-MM-DDTHH:mm:ssZ form, and a positive amount. First validate the timestamp, then check whether the ID is already recorded. Return RECORDED on success, INVALID_TIMESTAMP for an invalid timestamp, or DUPLICATE_PAYMENT for a valid-timestamp operation whose ID already exists. A rejected payment has no effect.
REFUND payment_id amount: refund a positive amount from an existing payment. Multiple partial refunds are allowed, but their cumulative amount may not exceed the original payment amount. Return REFUNDED, PAYMENT_NOT_FOUND, or INVALID_REFUND. A rejected refund has no effect. A successful full refund does not remove the payment from date-query results.
TOTAL: return the current net revenue as a decimal integer. Net revenue is the sum of recorded payment amounts minus successful refunds.
PAYMENTS_ON date: return the matching payment IDs as one comma-separated string in original recording order. The date must use strict YYYY-MM-DD form. Return the empty string when no payment matches, or INVALID_DATE when the date is invalid.
PAYMENTS_BETWEEN start_date end_date: return payment IDs whose payment date is in the inclusive range, ordered by payment date and then by original recording order within one date. Return the empty string when no payment matches. Return INVALID_RANGE when either date is invalid or start_date is after end_date.
Operation lines are otherwise well-formed. Payment IDs are non-empty and contain no spaces.
Interview follow-ups
The interview report also asked how to optimize date lookup for large data, validate timestamps, query a range such as one month, and persist runtime state in a database. This callable exercise judges the in-memory behavior above; database persistence remains a discussion follow-up.

Function
processPaymentLedger(operations: String[]) → String[]

Examples
Example 1
operations = ["PAYMENT p1 2026-04-10T09:00:00Z 100","PAYMENT p2 2026-04-10T10:30:00Z 70","TOTAL","REFUND p1 30","TOTAL","PAYMENTS_ON 2026-04-10"]
return = ["RECORDED","RECORDED","170","REFUNDED","140","p1,p2"]
The two unique payments create revenue of 170. Refunding 30 from p1 reduces it to 140. Both payments were recorded on 2026-04-10, so the date query returns their IDs in recording order.
Example 2
operations = ["PAYMENT pay-7 2026-04-11T12:00:00Z 50","PAYMENT pay-7 2026-04-12T12:00:00Z 90","REFUND pay-7 20","REFUND pay-7 31","REFUND missing 1","TOTAL"]
return = ["RECORDED","DUPLICATE_PAYMENT","REFUNDED","INVALID_REFUND","PAYMENT_NOT_FOUND","30"]
The duplicate ID is rejected. The successful partial refund leaves 30 of net revenue. Refunding another 31 would exceed the payment's remaining refundable amount.
Example 3
operations = ["PAYMENT p1 2026-02-30T09:00:00Z 25","PAYMENT p2 2026-04-11T09:00:00Z 40","PAYMENTS_ON 2026-13-01","PAYMENTS_BETWEEN 2026-04-12 2026-04-10","PAYMENTS_BETWEEN 2026-04-09 2026-04-12","TOTAL"]
return = ["INVALID_TIMESTAMP","RECORDED","INVALID_DATE","INVALID_RANGE","p2","40"]
February 30 and month 13 are invalid. The reversed range is also invalid. Only p2 is recorded, and it falls inside the final inclusive range.

Constraints
1 <= operations.length <= 100000.
Every operation has one of the documented forms and uses single spaces between tokens.
Payment IDs are non-empty ASCII strings without spaces or commas.
Every payment and refund amount is a positive integer at most 10^12.
Net revenue, cumulative refunds, and every intermediate monetary value fit in a signed 64-bit integer.
A valid payment timestamp is a real UTC instant written exactly as YYYY-MM-DDTHH:mm:ssZ. Query dates use exact YYYY-MM-DD form.
Date-query results must be produced without scanning unrelated payment IDs; preserve recording order among returned IDs.

Reported by candidates. Source: FastPrep

Pattern and pitfall

The trick is indexing by date at write time. Keep a hash map from payment ID to amount, refunded total, and date. Keep a second structure keyed by date string that holds IDs in recording order. Since YYYY-MM-DD strings sort lexicographically, a sorted list of distinct dates plus binary search gives you range queries without scanning everything. Keep a running net total as a 64-bit value, so TOTAL is O(1). The pitfalls are all validation. Check the timestamp before the duplicate ID, and reject Feb 30 and month 13 with real calendar logic including leap years. Enforce the exact format with length and digit checks. A rejected operation must change nothing. Full refunds stay in date results. Check start > end before returning results. If you freeze on the calendar validation or the ordering of checks mid-OA, StealthCoder is the hedge that gives you the working structure live.

Drill it cold or hedge it with StealthCoder. Either way, don't walk into the OA hoping you remember the trick.

If this hits your live OA

You can drill Payment Ledger with Refunds and Date Queries 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. Made for the candidate who got the OA invite this morning and has 72 hours, not six months.

Get StealthCoder

Related leaked OAs

⏵ The honest play

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

Stripe reuses patterns across OAs. Made for the candidate who got the OA invite this morning and has 72 hours, not six months. Works on HackerRank, CodeSignal, CoderPad, and Karat.

Payment Ledger with Refunds and Date Queries FAQ

What's the trick to this Stripe ledger problem?+

Index payments by date when you record them. A hash map holds payment state by ID, and a date-keyed map holds IDs in insertion order. That turns every date query into a lookup instead of a scan over 100000 operations.

How do I validate the timestamp correctly?+

Check exact length and the fixed characters first: dashes, T, colons, Z. Then confirm digits, then ranges. Month 1-12, hour 0-23, minute and second 0-59, and day within the month's length with leap years. Don't rely on a lenient date parser that rolls Feb 30 forward.

How should I handle PAYMENTS_BETWEEN efficiently?+

Keep a sorted list of distinct dates. Binary search for the start and end bounds, then concatenate each date's ID list in order. Date strings in YYYY-MM-DD compare correctly as plain strings. Return INVALID_RANGE if either date is bad or start is after end.

What mistakes cost the most points here?+

Wrong check order on PAYMENT, where timestamp must be validated before the duplicate check. Letting rejected operations change state. Removing fully refunded payments from date results. Using 32-bit integers when amounts reach 10^12. Allowing a refund total above the original amount.

How do I prepare in 48 hours?+

Write this ledger from scratch twice. Focus on the date validator, the two-map design, and the sorted date list with binary search. Test against the three examples, especially the invalid date and reversed range cases. The persistence follow-up is discussion only, so just know to mention a database index on date.

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

OA at Stripe?
Invisible during screen share
Get it