Class MerchantVerificationService

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

public class MerchantVerificationService extends Object
Service for merchant-side voucher verification and redemption (Model B).

In Model B, vouchers can ONLY be redeemed at the issuing merchant, not at the mint. This service provides both offline and online verification capabilities.

Verification Modes

  • Offline: Signature + expiry validation only (no network required)
  • Online: Offline checks + ledger status query (prevents double-spend)

Typical Flow

  1. Customer presents voucher token to merchant
  2. Merchant calls verifyOnline() to check validity and prevent double-spend
  3. If valid, merchant accepts payment and marks voucher as REDEEMED
  4. Voucher cannot be reused (terminal state)

Usage Example

 MerchantVerificationService service = new MerchantVerificationService(ledgerPort);

 // Parse token to get signed voucher (implementation specific)
 SignedVoucher voucher = parseToken(token);

 // Verify online (recommended for production)
 VerificationResult result = service.verifyOnline(voucher, "merchant123");

 if (result.isValid()) {
     // Accept payment
     // Mark as redeemed
     service.markRedeemed(voucher.getSecret().getVoucherId());
 } else {
     // Reject - show errors
     System.err.println("Invalid voucher: " + result.getErrorMessage());
 }
 
See Also:
  • Constructor Details

    • MerchantVerificationService

      public MerchantVerificationService(@NonNull @NonNull VoucherLedgerPort ledgerPort)
      Constructs a MerchantVerificationService.
      Parameters:
      ledgerPort - the port for ledger operations (must not be null)
  • Method Details

    • verifyOffline

      public MerchantVerificationService.VerificationResult verifyOffline(@NonNull @NonNull SignedVoucher voucher, @NonNull @NonNull String expectedIssuerId)
      Verifies a voucher offline (signature + expiry only).

      This method performs cryptographic signature verification and expiry checks without requiring network access. Useful for:

      • Offline merchants (temporary network outage)
      • Quick preliminary checks
      • Testing environments

      Warning: Offline verification cannot detect double-spending. Use online verification for production.

      Parameters:
      voucher - the signed voucher to verify (must not be null)
      expectedIssuerId - the merchant's issuer ID (must not be null or blank)
      Returns:
      the verification result
    • verifyOnline

      public MerchantVerificationService.VerificationResult verifyOnline(@NonNull @NonNull SignedVoucher voucher, @NonNull @NonNull String expectedIssuerId)
      Verifies a voucher online (offline checks + ledger status).

      This method performs all offline checks plus queries the public ledger to check for:

      • Voucher existence in ledger
      • Current status (ISSUED vs REDEEMED/REVOKED/EXPIRED)
      • Double-spend detection

      This is the recommended verification mode for production.

      Parameters:
      voucher - the signed voucher to verify (must not be null)
      expectedIssuerId - the merchant's issuer ID (must not be null or blank)
      Returns:
      the verification result
    • markRedeemed

      public void markRedeemed(@NonNull @NonNull String voucherId)
      Marks a voucher as redeemed in the ledger.

      Call this method after successfully accepting a voucher payment. This records the redemption in the public ledger to prevent double-spending.

      Parameters:
      voucherId - the voucher ID to mark as redeemed (must not be null or blank)
      Throws:
      IllegalArgumentException - if voucherId is invalid
      RuntimeException - if ledger update fails
    • redeem

      public RedeemVoucherResponse redeem(@NonNull @NonNull RedeemVoucherRequest request, @NonNull @NonNull SignedVoucher voucher)
      Redeems a voucher (verify + mark as redeemed).

      This is a convenience method that combines verification and redemption in a single operation. It:

      1. Verifies the voucher online
      2. If valid, marks it as REDEEMED
      3. Returns the redemption response
      Parameters:
      request - the redemption request (must not be null)
      voucher - the parsed voucher from the token (must not be null)
      Returns:
      the redemption response
    • validatePaymentPayload

      public MerchantVerificationService.VerificationResult validatePaymentPayload(@NonNull @NonNull xyz.tcheeric.cashu.common.VoucherPaymentPayload payload, @NonNull @NonNull xyz.tcheeric.cashu.common.VoucherPaymentRequest originalRequest)
      Validates a NUT-18V payment payload against the original payment request.

      This method checks that the payment payload matches the original request:

      • Payment ID matches (if present in request)
      • Issuer ID matches the merchant
      • Amount meets or exceeds the requested amount
      • Mint URL is permitted (if mints are restricted)
      • Proofs have DLEQ if offline verification was required
      Parameters:
      payload - the payment payload received from the customer
      originalRequest - the original payment request
      Returns:
      the verification result
    • processPaymentPayload

      public MerchantVerificationService.VerificationResult processPaymentPayload(@NonNull @NonNull xyz.tcheeric.cashu.common.VoucherPaymentPayload payload, @NonNull @NonNull xyz.tcheeric.cashu.common.VoucherPaymentRequest originalRequest)
      Validates a NUT-18V payment payload structure against the original request.

      This method is the main entry point for handling payment payloads received via the transport methods specified in the payment request. It validates the payload structure matches the original request.

      Note: This method validates the payload structure but does NOT verify individual voucher proofs or mark vouchers as redeemed. The caller should extract vouchers from the proofs and verify them separately using verifyOnline(xyz.tcheeric.cashu.voucher.domain.SignedVoucher, java.lang.String) or verifyOffline(xyz.tcheeric.cashu.voucher.domain.SignedVoucher, java.lang.String), then call markRedeemed(java.lang.String) after successful verification.

      Parameters:
      payload - the payment payload from the customer
      originalRequest - the original payment request
      Returns:
      the verification result