Skip to content
    hazBasehazBaseDocs
    IMPLEMENTATION PATTERNS

    Payments & connected services

    Connect usage and payment with wallet integration and x402.

    All implementation patterns
    EXAMPLE

    Unlock an equipment-report API after payment

    An equipment-report API returns x402 payment terms. After the user reviews and pays them, the service grants access to the report. Your service manages products, orders and access rights, while hazBase handles the wallet payment integration.

    Build a select, review, pay and access flow, with repeat access tied to the same order.

    Architecture and responsibilities

    1. Report API and orders

      Bind price, payment asset, recipient and expiry to the report.

      Merchant backend / Order DB
    2. Payment-term handling

      Select supported chains and assets and display terms for review.

      @hazbase/kit/x402
    3. Authentication and payment

      Submit payment with an authenticated session and operation authorization.

      @hazbase/kit/wallet / @hazbase/auth
    4. Settlement and access

      Verify payment against the order server-side and grant access once.

      Payment verification / Access DB

    Example data model

    Map application records to the data managed by the chain or APIs. Field names and values below illustrate the application model for this architecture.

    Field and exampleStored inPurpose and mapping
    orderId / resourceUrlORDER-001 / report URLYour serverOrder and canonical report URL, associated with the purchaser’s session.
    paymentRequestIdissued request IDPayment requestUse an ID issued by the connected payment service, not an arbitrary string sent to the payment API.
    network / asset / payToeip155:… / 0x… / 0x…Order and requestAllowed chain, token and merchant recipient, checked against your configuration.
    amountAtomic1000000Order and requestAmount in atomic units: 1,000,000 represents one unit for six decimals. Distinguish this from fiat-currency amounts.
    paymentAttemptId / statusattempt ID / settledPayment API and DBPersist the attempt ID after submission and check that attempt’s completion status.
    accessGrantorderId + memberIdYour DBAccess permission for the order, protected against duplicate grants by a unique constraint.

    Processing flow

    1. Create a payment request for the order

      Return payment terms for the report request. Payment-request creation and merchant configuration follow the connected service’s integration contract. The Wallet SDK used here does not register arbitrary merchant orders.

      Merchant API → HTTP 402 / paymentRequestId
    2. Validate and display the terms

      Parse the response and check chain, asset, recipient, resource URL and amount limit against policy. Display the report and amount and obtain the user’s payment confirmation.

      summarizeX402Request / isX402RequestExpired
    3. Pay with an authenticated wallet

      Obtain emailSession, smartAccountAddress, deviceBindingId and highTrustToken through the authentication flow. Send the approved amount and recipient as expectedAmountAtomic and expectedPayTo.

      payX402WithHazbaseWallet(input)
    4. Track the same payment attempt

      If processing continues, retain paymentAttemptId and query its status. Distinguish submitted, verified and settled states; a slow response must not trigger a new payment.

      getX402HazbaseWalletPaymentStatus({ emailSession, paymentRequestId, paymentAttemptId })
    5. Verify on the server and grant access

      The merchant server verifies trusted payment status or proof against the order, purchaser, amount, asset and recipient. Save access using a DB transaction and unique constraint, then return the report.

      Server payment verification → accessGrant → report response

    Before using the code

    • Provide a valid paymentRequestId issued by the connected service and its supported chain, asset and merchant recipient.
    • Integrate a payment confirmation screen and the session, device registration and operation authorization required by the Wallet API.
    • Keep authentication tokens out of logs, URLs and public configuration. Pass values from that user’s authentication flow in auth.
    Install
    npm install --save-exact @hazbase/kit@0.9.0 ethers@6.16.0

    The TypeScript functions below are called from your application with the appropriate connections, permissions and data. Integrate the UI, persistence and recovery using the architecture above.

    Inspect payment terms and submit the approved request

    Use inspectReportPayment to extract the terms, display them for confirmation, then call payApprovedReport. payload is the JSON from the payment service; policy contains your service’s accepted terms.

    pattern-payments.ts
    import { summarizeX402Request, isX402RequestExpired, type HazbaseX402Request } from '@hazbase/kit/x402';
    import { createHazbaseWalletClient, type PayX402WithHazbaseWalletInput } from '@hazbase/kit/wallet';
    import { getAddress } from 'ethers';
    
    export function inspectReportPayment(
      payload: Record<string, unknown>,
      resourceUrl: string,
      policy: { network: string; asset: string; payTo: string; maxAmountAtomic: bigint },
    ) {
      const request = summarizeX402Request(payload, { sourceUrl: resourceUrl }, {
        networks: [policy.network],
        assets: [{ address: policy.asset }],
        requirePayTo: true,
      });
      if (!request) throw new Error('Unsupported payment request.');
      const terms = request.requirement;
      if (terms.resource !== resourceUrl || getAddress(terms.payTo) !== getAddress(policy.payTo)) {
        throw new Error('Payment does not match the requested service.');
      }
      const amount = BigInt(terms.amountAtomic);
      if (amount <= 0n || amount > policy.maxAmountAtomic) throw new Error('Amount is outside policy.');
      if (isX402RequestExpired(request, Date.now())) throw new Error('Payment request expired.');
      return request;
    }
    
    export async function payApprovedReport(
      request: HazbaseX402Request,
      auth: Pick<PayX402WithHazbaseWalletInput,
        'emailSession' | 'smartAccountAddress' | 'deviceBindingId' | 'highTrustToken'>,
    ) {
      // Call only after the user confirms the displayed resource and payment terms.
      if (isX402RequestExpired(request, Date.now())) throw new Error('Payment request expired.');
      const wallet = createHazbaseWalletClient();
      return wallet.payX402WithHazbaseWallet({
        ...auth,
        paymentRequestId: request.paymentRequestId,
        expectedAmountAtomic: request.requirement.amountAtomic,
        expectedPayTo: request.requirement.payTo,
        waitForReceipt: false,
      });
    }
    
    Download the example

    Result and application integration

    Retain paymentRequestId and paymentAttemptId to track status. Do not grant access based only on a paid: true value supplied by the frontend; use merchant-server verification.

    Implementation considerations

    Display amounts and expiry

    Compare atomic amounts as well as display values. The SDK expiry check is a receipt-time guard; the server must also validate the order’s absolute expiry.

    Duplicate payments and access grants

    After a timeout, query the existing attempt and retain payment state per order. Repeated completion notifications must not create duplicate access grants.

    Recipient or resource substitution

    Compare resource URL, asset, chain and recipient with the canonical order. Check the association between the authenticated buyer and request ID on the server.

    Extending the architecture

    Metered and recurring access

    Extend a single report purchase to usage allowances or time-limited access. Your service’s authorization model defines how many uses each payment permits and for how long.

    Wallet extension integration

    You can publish the x402 request on the page and hand it to a compatible wallet. Even when the wallet handles payment UI, the merchant server remains responsible for order verification and access.