Class VoucherRedemptionService

java.lang.Object
xyz.tcheeric.cashu.voucher.app.VoucherRedemptionService

public class VoucherRedemptionService extends Object
Service for secure voucher redemption with duplicate protection.

This service implements a defense-in-depth approach to prevent double redemption:

  1. Fingerprint check: Fast lookup in redemption tracking store
  2. Ledger status check: Verify voucher is in ISSUED state
  3. Atomic recording: Record redemption before updating ledger
  4. Ledger update: Mark voucher as REDEEMED

Concurrency Safety

The VoucherRedemptionPort.tryRecordRedemption(java.lang.String, java.lang.String, java.lang.String) method provides atomic compare-and-swap semantics. If two concurrent redemption attempts occur, only one will succeed in recording the redemption.

Model B Enforcement

This service optionally verifies that the redeeming merchant matches the voucher's issuer ID (Model B constraint).

See Also:
  • Constructor Details

    • VoucherRedemptionService

      public VoucherRedemptionService(@NonNull @NonNull VoucherLedgerPort ledgerPort, @NonNull @NonNull VoucherRedemptionPort redemptionPort)
      Constructs a VoucherRedemptionService with required dependencies.
      Parameters:
      ledgerPort - the port for voucher status operations (must not be null)
      redemptionPort - the port for redemption tracking (must not be null)
  • Method Details

    • redeem

      public RedeemVoucherResponse redeem(@NonNull @NonNull SignedVoucher voucher, @NonNull @NonNull String merchantId, boolean verifyOnline)
      Attempts to redeem a voucher with duplicate protection.

      This method performs the following steps:

      1. Computes fingerprint from voucher
      2. Checks if fingerprint already redeemed (fast path rejection)
      3. Optionally queries ledger for current status
      4. Validates voucher signature and expiry
      5. Optionally verifies merchant ID matches (Model B)
      6. Atomically records redemption
      7. Updates ledger status to REDEEMED
      Parameters:
      voucher - the signed voucher to redeem (must not be null)
      merchantId - the merchant attempting redemption (must not be null)
      verifyOnline - whether to verify status on ledger (recommended: true)
      Returns:
      redemption response with success/failure details
    • redeem

      public RedeemVoucherResponse redeem(SignedVoucher voucher, String merchantId)
      Convenience method for redeeming with default online verification.
      Parameters:
      voucher - the signed voucher to redeem
      merchantId - the merchant attempting redemption
      Returns:
      redemption response
    • isRedeemed

      public boolean isRedeemed(SignedVoucher voucher)
      Checks if a voucher has been redeemed based on its fingerprint.
      Parameters:
      voucher - the voucher to check
      Returns:
      true if the voucher has been redeemed
    • getRedemptionRecord

      public Optional<VoucherRedemptionPort.RedemptionRecord> getRedemptionRecord(SignedVoucher voucher)
      Gets redemption details for a voucher.
      Parameters:
      voucher - the voucher to look up
      Returns:
      redemption record if found