Payments & connected services
Connect usage and payment with wallet integration and x402.
← All implementation patternsUnlock 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
Report API and orders
Bind price, payment asset, recipient and expiry to the report.
Merchant backend / Order DBPayment-term handling
Select supported chains and assets and display terms for review.
@hazbase/kit/x402Authentication and payment
Submit payment with an authenticated session and operation authorization.
@hazbase/kit/wallet / @hazbase/authSettlement 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 example | Stored in | Purpose and mapping |
|---|---|---|
orderId / resourceUrlORDER-001 / report URL | Your server | Order and canonical report URL, associated with the purchaser’s session. |
paymentRequestIdissued request ID | Payment request | Use an ID issued by the connected payment service, not an arbitrary string sent to the payment API. |
network / asset / payToeip155:… / 0x… / 0x… | Order and request | Allowed chain, token and merchant recipient, checked against your configuration. |
amountAtomic1000000 | Order and request | Amount in atomic units: 1,000,000 represents one unit for six decimals. Distinguish this from fiat-currency amounts. |
paymentAttemptId / statusattempt ID / settled | Payment API and DB | Persist the attempt ID after submission and check that attempt’s completion status. |
accessGrantorderId + memberId | Your DB | Access permission for the order, protected against duplicate grants by a unique constraint. |
Processing flow
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 / paymentRequestIdValidate 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 / isX402RequestExpiredPay 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)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 })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.
npm install --save-exact @hazbase/kit@0.9.0 ethers@6.16.0The 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.
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,
});
}
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.