Double-Charged at Checkout — JavaScript Bug Hunt

Inspired by the double-submit bugs every payments team has fought (and the idempotency keys Stripe popularised to kill them).

  • Language: JavaScript
  • Layer: Frontend
  • Difficulty: Easy
  • Concepts: Idempotency, Payments
  • Modelled on: Stripe idempotency
  • Visible tests: a repeated key charges exactly once; different keys create separate charges
  • Reward: 50 XP for a complete fix

Briefing

Inspired by the double-submit bugs every payments team has fought (and the idempotency keys Stripe popularised to kill them). An impatient double-click sends the same request twice — the processor must charge exactly once per idempotency key.

Fix chargeProcessor.js.

Bug report

BUG-5150 · Priority: Critical · Reported by: support (chargebacks!)

Contract: process(key, amountCents) — a repeated key must be a no-op that returns the ORIGINAL charge (same id, same amount), creating nothing new.

Observed: double-clicks create two charges; the retry even returns a different charge id than the first attempt.

Logs

[charge] key=ck_1a2b amount=4999 -> ch_001
[charge] key=ck_1a2b amount=4999 -> ch_002   <- duplicate!

The code as shipped

src/pay/chargeProcessor.js (editable)

// Charges with idempotency keys: one key, one charge — ever.
var charges = [];
var seenKeys = {};
var nextId = 1;

exports.process = function (key, amountCents) {
  var charge = { id: "ch_" + nextId, amount: amountCents, key: key };
  nextId++;
  charges.push(charge);
  if (!seenKeys[key]) {
    seenKeys[key] = charge;
  }
  return seenKeys[key];
};

exports.allCharges = function () { return charges.slice(); };
exports.resetAll = function () { charges = []; seenKeys = {}; nextId = 1; };

Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.