Skip to content

feat(api-utils,ack-pay): add plug-and-play HTTP 402 payment challenge and receipt verification middleware #171

Closed as not planned

Description

@vhtgzzl

Summary

Proposes adding a standardized, plug-and-play HTTP 402 Payment Required middleware in @repo/api-utils\ (and exposing corresponding helper adapters for ACK-Pay services). This middleware automatically issues cryptographically signed ACK-Pay payment challenges when no valid receipt is supplied, extracts and validates incoming payment receipts (Verifiable Credentials / signed JWTs via @agentcommercekit/ack-pay), and injects verified payment context (\�ckPayment) directly into route handlers.

Motivation & Use Case

Currently, services and demos (such as \demos/payments/src/server.ts) implementing HTTP 402 paywalls must manually implement receipt extraction from \Authorization\ / \X-ACK-Payment-Proof\ headers, hand-roll the 402 error response with signed JWT generation, and catch/verify receipts imperatively inside route handlers.

While @repo/api-utils\ already provides \signedPayloadValidator\ for signed request payloads, there is no corresponding middleware for the ACK-Pay 402 challenge-receipt cycle. Having a dedicated middleware will:

  1. Reduce Boilerplate: Route handlers can be declared cleanly with standard declarative middleware syntax (e.g. \�pp.get('/infer', paymentRequiredValidator(config), (c) => ...)).
  2. Standardize HTTP 402 Challenges: Ensure consistent response formats (challenge nonce, expiration ISO timestamps, currency amounts, multi-network payment options) across all ACK-enabled APIs.
  3. Safety & Fail-Closed Behavior: Automatically reject malformed proofs (400 Bad Request) and unverified/untrusted receipt issuers (403 Forbidden).

Proposed Design

  1. Middleware Definition (\ ools/api-utils/src/middleware/payment-required-validator.ts):
    • Configurable with static \PaymentRequestInit\ or a dynamic resolver function (c: Context) => Promise.
    • Header extraction: Supports \Authorization: Bearer \ and \X-ACK-Payment-Proof.
    • Verification: Uses \�erifyPaymentReceipt\ from @agentcommercekit/ack-pay\ with trusted issuer verification and DID resolution.
    • Context Injection: Sets \c.set(\�ckPayment, verifiedResult).
  2. HTTP Exception Helper:
    • Add \paymentRequired(response?: Response, message?: string)\ helper in \ ools/api-utils/src/exceptions.ts.
  3. Unit Tests (\ ools/api-utils/src/middleware/payment-required-validator.test.ts):
    • Assertive Vitest tests verifying 402 challenge issuance, successful receipt resolution, missing header handling, and invalid receipt rejection.

Potential Drawbacks & Considerations

  • None; this is non-breaking and purely additive to the internal workspace tooling and developer experience.

AI Usage Disclosure

Per AI_POLICY.md: This feature proposal was drafted with the assistance of AI tools (Antigravity AI Assistant). All architectural considerations, API contracts, and edge cases have been reviewed and validated against the ACK monorepo standards.

Activity

  1. added a commit that references this issue on Sep 6, 2026
    8dc35f6
  2. venables commented on Sep 9, 2026

    @venables
    Contributor

    Closing this one: both implementations (#172, #190) were closed because @repo/api-utils is an internal package and nothing in the repo needs this middleware yet. We only add abstractions once something needs them.

  3. guidoalejo commented on Oct 3, 2026

    @guidoalejo

    Saw feat(api-utils,ack-pay): add plug-and-play HTTP 402 payment challenge and receipt verification middleware. Sluzen sits in front of an HTTP API and charges per call (HTTP 402). Do you have an API you would price that way?

  4. guidoalejo commented on Oct 4, 2026

    @guidoalejo

    Update since the earlier note: Sluzen no longer requires putting a proxy in front of every API.

    If you already return HTTP 402, register that https URL as a Sluzen facilitator resource. Your handler asks Sluzen to verify the signed payment, runs, and settles on the XRP Ledger only if it succeeded — skip settle and the caller is not charged. Callers stay on your URL (@sluzen/sdk AgentClient → your origin). Facilitator routes are not listed on public discovery.

    If you prefer a Sluzen proxy path, you can mark origin-before-settle so a non-2xx is not charged. Default remains pay-then-origin. Request price lands on your classic r…; the 0.8% take is prepaid outside the caller’s 402; minimum 0.01 XRP.

    If that fits, say so and I will send a one-use Sluzen issuer link.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions