Bugnik
← All problems

21Gift Card Wallet API

Java · Spring BootEasyBug fixSuggested time: 60 min

Gift card wallet REST API (codes, ownership, expiry, idempotent redemptions)

Sign in to start

Before you start

You'll get more out of this problem if you're comfortable with:

Language

  • Java records and their accessor methods
  • == vs equals for Strings
  • java.time.LocalDate comparisons (isBefore, isAfter)

Libraries and frameworks

  • Spring Boot REST controllers: @RequestHeader, @PathVariable, ResponseEntity

Build and test tools

  • Reading javac "cannot find symbol" errors in the Maven output
  • Reading JUnit 5 / MockMvc test failures

Reasoning

  • Inclusive expiry dates and idempotent requests

Background

The Payments team at a large online retailer runs the gift card wallet: customers load cards, check balances and spend them at checkout. Since the last release, customers can't see or spend their own cards through the API, and cards are refused on their expiry date even though the terms say they're valid through that day.

How the system works

Card codes

A gift card code has 12 letters or digits. Customers type it any way they like (abcd-efgh 2345), so every code is normalized first: spaces and dashes removed, then upper-cased (ABCDEFGH2345). Any other character, or a length other than 12 after normalizing, is invalid. Cards are stored and returned under their normalized code.

Rules

  • Issue: a new card needs a valid code, an owner (customer id) and an amount from 1 cent to $2,000.00 (200000 cents). It starts ACTIVE. A code that already exists is rejected.
  • Ownership: every call except issuing names the customer in the X-Customer-Id header. Only the card's owner may read or use it; customer ids are compared exactly (case-sensitive).
  • Expiry: a card is valid through its expiry date: it can be used on that date and stops working the day after. "Today" comes from the service's clock (UTC).
  • Redeem: takes a positive amount from an ACTIVE, unexpired card. The amount may equal the balance (leaving 0) but not exceed it. Each redemption gets a transaction id (T-1, T-2, ...).
  • Idempotency: a redemption is identified by the card and the orderId. Redeeming the same order on the same card again returns the original redemption and changes nothing (even if the amount differs). The same order id on a different card is a separate redemption.
  • Reload: adds a positive amount to an ACTIVE, unexpired card; the balance may reach but not exceed $2,000.00.
  • Freeze: sets the card to FROZEN. A frozen card can't be redeemed or reloaded. Freezing an already frozen card is fine.
  • Checks happen in this order: card exists → owner → (for redemptions) repeated order → amount → frozen → expired → balance or limit.

API

Method and pathBodySuccess
POST /api/gift-cards{code, ownerId, amountCents, expiresOn}201 card
GET /api/gift-cards/{code}200 card
POST /api/gift-cards/{code}/redemptions{orderId, amountCents}201 redemption; 200 with the original for a repeated order
POST /api/gift-cards/{code}/reloads{amountCents}200 card
POST /api/gift-cards/{code}/freeze200 card
  • A card is {code, ownerId, balanceCents, expiresOn, status} (expiresOn as YYYY-MM-DD).
  • A redemption is {transactionId, code, orderId, amountCents, balanceAfterCents}.
  • {code} in the path may be typed in any format; it's normalized.

Errors

Errors return {"error": "<CODE>", "message": "..."}:

StatusCodes
400INVALID_CODE, INVALID_AMOUNT, INVALID_REQUEST (missing header, missing/blank fields, malformed JSON)
403FORBIDDEN (not the owner)
404NOT_FOUND (unknown card)
409DUPLICATE_CODE, INSUFFICIENT_FUNDS, BALANCE_LIMIT, CARD_FROZEN, CARD_EXPIRED

Project structure

src/main/java/com/bugnik/giftcards/
  GiftCardsApplication.java      Spring Boot entry point
  config/TimeConfig.java         the Clock bean ("today")
  model/                         GiftCard, CardStatus, Redemption
  repository/                    in-memory GiftCardRepository and RedemptionRepository
  service/GiftCardService.java   the rules above
  service/GiftCardException.java business errors (status + code)
  util/CardCodes.java            code normalization and validation
  web/                           GiftCardController, request bodies, ApiError, ApiExceptionHandler
src/test/java/com/bugnik/giftcards/   unit and MockMvc tests

Your task

Customers and the support team have reported problems since the last release:

  • The project no longer builds.
  • Customers get 403 FORBIDDEN when they look up or spend their own gift card through the API.
  • A card whose expiry date is today is refused as expired.

Find and fix the defects so that all tests pass.

  • Do not modify src/test/ or pom.xml.
  • Keep your changes minimal. Fix the defects; don't rewrite modules.
  • Additional hidden tests check the behaviour described in this README.