Interface VoucherLedgerPort


public interface VoucherLedgerPort
Port for voucher ledger operations (public audit trail).

This port defines the interface for publishing and querying voucher status on a public ledger. In the Nostr implementation, this uses NIP-33 (parameterized replaceable events) to maintain a public audit trail of voucher lifecycle.

Hexagonal Architecture

This is a port (interface) in hexagonal architecture terminology. The actual implementation (adapter) is provided by the infrastructure layer (e.g., NostrVoucherLedgerRepository in cashu-voucher-nostr module).

Design Principles

  • Infrastructure-agnostic: No dependencies on Nostr or any specific storage
  • Pluggable: Can be implemented with Nostr, SQL, IPFS, etc.
  • Testable: Easy to mock for unit testing application services

Model B Constraint

The ledger tracks vouchers that are only redeemable at the issuing merchant. Status transitions enforce Model B business rules (no mint redemption).

Example Usage

 // Publishing a new voucher
 SignedVoucher voucher = ...;
 ledgerPort.publish(voucher, VoucherStatus.ISSUED);

 // Checking voucher status
 Optional<VoucherStatus> status = ledgerPort.queryStatus(voucherId);
 if (status.isPresent() invalid input: '&'invalid input: '&' status.get() == VoucherStatus.ISSUED) {
     // Voucher is valid for redemption
 }

 // Marking voucher as redeemed
 ledgerPort.updateStatus(voucherId, VoucherStatus.REDEEMED);
 
See Also:
  • Method Details

    • publish

      void publish(SignedVoucher voucher, VoucherStatus status)
      Publishes a voucher to the public ledger with the given status.

      This creates a new ledger entry for the voucher. If a voucher with the same ID already exists, the behavior depends on the implementation:

      • NIP-33 (Nostr): Replaces the previous entry (replaceable event)
      • SQL: May throw exception or update existing record

      The voucher's signature should be verified before publishing, but this method does not perform validation (separation of concerns).

      Parameters:
      voucher - the signed voucher to publish (must not be null)
      status - the initial status (typically ISSUED, must not be null)
      Throws:
      IllegalArgumentException - if parameters are invalid
      RuntimeException - if publishing fails (network, storage, etc.)
    • queryStatus

      Optional<VoucherStatus> queryStatus(String voucherId)
      Queries the current status of a voucher from the ledger.

      Returns the most recent status of the voucher as recorded in the ledger. If the voucher does not exist in the ledger, returns Optional.empty().

      This is a read-only operation and does not modify the ledger state.

      Parameters:
      voucherId - the unique voucher identifier (must not be null or blank)
      Returns:
      the current status, or empty if voucher not found
      Throws:
      IllegalArgumentException - if voucherId is null or blank
      RuntimeException - if query fails (network, storage, etc.)
    • updateStatus

      void updateStatus(String voucherId, VoucherStatus newStatus)
      Updates the status of an existing voucher in the ledger.

      This method changes the voucher's status, recording a state transition in the public ledger. Common transitions:

      • ISSUED → REDEEMED (merchant accepts voucher)
      • ISSUED → REVOKED (issuer cancels voucher)
      • ISSUED → EXPIRED (time-based expiry)

      Terminal states (REDEEMED, REVOKED, EXPIRED) should not be changed once set, but this is enforced at the service layer, not by this port.

      Parameters:
      voucherId - the unique voucher identifier (must not be null or blank)
      newStatus - the new status to set (must not be null)
      Throws:
      IllegalArgumentException - if parameters are invalid
      RuntimeException - if update fails (network, storage, voucher not found, etc.)
    • exists

      default boolean exists(String voucherId)
      Checks if a voucher exists in the ledger.

      This is a convenience method equivalent to:

      queryStatus(voucherId).isPresent()
      Parameters:
      voucherId - the unique voucher identifier (must not be null or blank)
      Returns:
      true if the voucher exists in the ledger, false otherwise
      Throws:
      IllegalArgumentException - if voucherId is null or blank
      RuntimeException - if check fails (network, storage, etc.)
    • queryVoucher

      default Optional<SignedVoucher> queryVoucher(String voucherId)
      Queries the full voucher details from the ledger.

      This retrieves both the voucher secret and its current status from the ledger. This is useful for validation and verification operations.

      Note: This is an optional operation. Implementations may not support retrieving full voucher details from the ledger (e.g., if only status transitions are stored). Default implementation throws UnsupportedOperationException.

      Parameters:
      voucherId - the unique voucher identifier (must not be null or blank)
      Returns:
      the complete voucher with current status, or empty if not found
      Throws:
      UnsupportedOperationException - if implementation doesn't support full retrieval
      IllegalArgumentException - if voucherId is null or blank
      RuntimeException - if query fails (network, storage, etc.)