FFiber Offers
Documentation / Build

Wallet payment flows

A wallet consumes an offer as a payment intent, verifies it, validates the payer's intended request, resolves it into one new invoice, then sends that invoice through Fiber only after the user confirms.

Wallet responsibilities

Discover

Accept a payment link, an encoded offer, a QR payload, or a Fiber Address. Fetch current resolver metadata when the user chooses to continue.

Verify

Decode the offer and verify its Ed25519 signature. Do not treat resolver transport alone as proof of merchant intent.

Check

Call resolver readiness with the exact amount and asset. Render offer or merchant-node failures before requesting an invoice.

Execute explicitly

Request an invoice with an idempotency key, show the final amount, and call Fiber payment only after explicit user approval.

Recommended payment sequence

  1. Resolve the payment link, address, or encoded offer into a signed offer.
  2. Verify the offer signature and show the merchant, asset, and price semantics.
  3. Collect an amount only for customer-chosen or range pricing.
  4. Call POST /offers/:offer_id/check with the selected amount and asset.
  5. Use the same checkout attempt ID as the idempotency key for invoice.
  6. Show the returned invoice summary and ask for confirmation.
  7. Dry-run the invoice through the payer's Fiber node, then send only when the route is payable.
  8. Retain the resolution ID for status and receipt lookup.
Do not pre-create invoicesThe offer is the reusable object. An invoice is for a specific attempt. Creating invoices only after readiness keeps route and invoice data fresh and avoids stale payment requests.

SDK payment preparation

FiberPaymentFlowClient composes resolver readiness, invoice resolution, and payer-side route confidence. Its preparePayment method does not execute payment.

import {
  FiberOffersClient,
  FiberPaymentClient,
  FiberPaymentFlowClient
} from "@fiber-offers/sdk";

const resolver = new FiberOffersClient({
  resolverUrl: "https://resolver.example"
});

const payer = new FiberPaymentClient({
  url: "http://127.0.0.1:8229"
});

const flow = new FiberPaymentFlowClient({
  resolverClient: resolver,
  paymentClient: payer
});

const request = {
  amount: "1000",
  asset: { asset_type: "ckb", symbol: "CKB" }
};

const prepared = await flow.preparePayment(offerId, request, {
  idempotencyKey: "checkout-order-42"
});

if (!prepared.ok) {
  showPaymentProblem(prepared.failure, prepared.readiness?.next_actions);
  return;
}

showConfirmation(prepared.invoice.invoice);

After confirmation, opt into actual payment. This explicit flag protects application code from accidentally sending funds while it is only testing readiness.

const result = await flow.payOffer(offerId, request, {
  idempotencyKey: "checkout-order-42",
  execute: true
});

if (result.status === "payment_sent") {
  showSuccess(result.payment_hash, result.fee);
}

Pricing cases to render clearly

Fixed amount

Do not show an amount editor. The wallet requests the one fixed amount from the offer and rejects any attempt to substitute a different amount.

Customer chooses amount

Show an amount input with the offer minimum. Call readiness again whenever the selected value changes before enabling the confirmation path.

Amount range

Show both the minimum and maximum. Keep the selected amount visible through invoice confirmation, because that is the value the resolver validates and records.

Failure and retry handling

Readiness results carry a structured code, summary, blockers, warnings, and next_actions. Prefer these to raw RPC error messages in the wallet UI.

SituationWallet behavior
Offer signature invalidStop. Do not request an invoice and show that the payment intent could not be verified.
Amount or asset rejectedReturn to amount or asset selection. The offer remains usable if the new request is valid.
Route or liquidity blockerShow the normalized summary and next actions. Do not imply a payment will succeed.
Invoice request timeoutRetry with the same idempotency key and payment request.
Fiber send failureKeep the raw node error for diagnostics, but present the normalized failure and suggest the next action.

A single-use offer is also safe under concurrent use: if another payer already caused an invoice to be created, the resolver rejects subsequent attempts with OFFER_ALREADY_USED.