Capxul Docs
How-to guides

Create a payment

Create a personal-Account payment for a validated recipient and understand its current completion boundary.

client.payments.pay accepts product-level Money and a validated recipient. The personal Account path prepares one stored UserOperation, validates the sender, asks the configured wallet to sign one digest, and submits through the backend. A successful result confirms that the backend recorded the submitted hash. It does not claim final chain settlement.

Limited availability

Payment execution requires an environment with active CapxulPaymentsV2 writes. Use client.org(orgId).payments for Organization payments. Check capability status before treating a submitted Payment as settled.

Before you start

Create a client with the Account readiness your application requires, then authenticate the user. This guide assumes you already have an initialized client; see Choose Account readiness if you are unsure which requirement to use.

Create the payment

Parse form text against the selected Account before submit:

import { formatMoney, parseMoney } from "@capxul/sdk";

const account = await client.accounts.read();
if (!account.ok) throw account.error;

const amount = parseMoney(formAmount, account.value.available);
if ("kind" in amount) {
  showFieldError(copyFor(amount.reason)); // app-owned lookup (ADR-0023)
  return;
}

console.log(formatMoney(amount, { grammar: "code" }));

parseMoney uses the selected Account decimal count. It returns a validation error value for missing, malformed, zero, negative, oversized, or over-precise input.

formatMoney accepts any {currency, value, decimals} record, so it also formats the money an activity row carries, whose currency is a plain string. An unsupported currency code fails with INVALID_INPUT.

PAYMENT_STATUSES and PAYMENT_DIRECTIONS are the closed code vocabularies themselves. Render a dropdown from them and keep the wording in the application; the SDK ships no label for a code.

Pass the parsed Money and a validated recipient:

const paid = await client.payments.pay({
  to: { kind: "email", email: "vendor@example.com" },
  amount,
  paymentType: "invoice",
});

if (!paid.ok) {
  console.error(paid.error.code, paid.error.message);
  return;
}

console.log(paid.value.id, paid.value.status);

The personal path accepts handles, emails, Organizations, and saved payees—not a raw wallet address. The paymentType value records the business purpose of the payment.

Interpret the result

paid.ok means the SDK accepted the request and returned a payment record. Read the record's status; do not reinterpret the presence of an ID as final settlement.

Read the latest record when your UI needs to refresh its state:

const payment = await client.payments.get(paid.value.id);
if (!payment.ok) throw payment.error;

console.log(payment.value.status);

Know the unavailable paths

  • An Organization actor passed to payments.pay returns NOT_IMPLEMENTED.
  • A destination target passed to payments.pay returns NOT_IMPLEMENTED.
  • client.payments.cancel returns NOT_IMPLEMENTED; payment cancellation is not available.

See SDK capability status for the current availability summary and the method reference for exact evidence.

On this page