# Invoices and Payment Links

`PaymentRequestV2` represents a finite set of payment obligations in one collection. `PaymentLinkV1` reuses the same terms model for repeatable fixed payments. Each obligation has its own approval and progress tracker. Payments go directly to the specified recipients when the transaction executes.

Use `PaymentRequestV2` for a bill that can become paid. Use `PaymentLinkV1` for a checkout that stays available for repeated purchases until its cutoff. Use [Subscriptions](https://docs.bitbadges.io/standards/subscriptions) for recurring authorization and charging.

## JSON Input

Save this complete builder input as `terms.json`. The addresses are valid **demonstration addresses**, not recipients to pay. Replace them, the metadata, and the example 2030 cutoff before use. `ubadge` is the chain denomination; `1000000` is an exact base-unit amount.

```json
{
  "version": 2,
  "kind": "invoice",
  "obligations": [
    {
      "id": "payment-1",
      "payer": {
        "kind": "anyone"
      },
      "payouts": [
        {
          "recipient": "bb1xvenxvenxvenxvenxvenxvenxvenxvenlrd2nm",
          "denom": "ubadge",
          "amount": "1000000"
        }
      ],
      "startTime": "1",
      "endTime": "1893456000000"
    }
  ],
  "name": "Example: anyone",
  "image": "https://example.com/payment.png",
  "description": "Editable payment example. Replace demo addresses, amounts, dates and metadata before creating a collection."
}
```

```bash
bb build payment-request-v2 --schema
bb build payment-request-v2 --list-examples
bb build payment-request-v2 --example anyone | jq '.data' > terms.json
bb build payment-request-v2 --json terms.json --creator "$CREATOR" --output-file proposal.json
```

The example command generates a fresh cutoff. `--schema` returns the structural JSON Schema; the runtime validator also rejects incompatible combinations. JSON property names are case-sensitive. The builder parameter object includes metadata; stored `PaymentRequestV2Terms` contains only `version`, `kind`, and `obligations`. Do not pass the CLI `{ok,data,warnings,error}` envelope where a terms object is required.

## Terms

The V2 builder takes a parameter object with `version: 2`, `kind: "invoice"` or `"payment-link"`, `obligations`, and metadata. Supply either `uri` or the inline `name`, `image`, and `description` metadata fields. Every obligation contains:

| Field | Meaning |
| --- | --- |
| `id` | Unique reference within this collection; used by payment actions and tracking |
| `payer` | `{ "kind": "anyone" }` or `{ "kind": "addresses", "addresses": [...] }` |
| `payouts` | Recipient, denomination, and positive base-unit amount for each payout leg |
| `startTime` | First permitted payment instant, inclusive Unix milliseconds as an integer string |
| `endTime` | Last permitted payment instant, inclusive Unix milliseconds as an integer string |
| `dueAt` | Optional informational due date; does not replace the payment window |
| `requiredPayments` | Finite fixed-payment count; default is one when omitted on a nonpartial invoice |
| `distinctPayers` | Enforce at most one payment per initiator for the obligation |
| `partial` | Finite target in receipt units; payout amounts become amounts per unit |

The runtime schema rejects unknown or incompatible fields. [Payer counts](https://docs.bitbadges.io/standards/payment-payers) and [payment units](https://docs.bitbadges.io/standards/payment-units) describe permitted combinations. All amounts and times are exact integer strings; do not pass a JavaScript floating-point amount into these terms.

## Build and Pay

```bash
bb build payment-request-v2 --help
bb build payment-request-v2 --json terms.json --output-file proposal.json
bb pay-requests show 46
bb pay-requests status 46
bb pay-requests pay 46 --creator "$PAYER" --obligation payment-1 --units 1 --output-file payment.json
```

The first command constructs a collection proposal. Publish through the normal reviewed transaction flow, obtain the real collection ID, then query and pay that collection. `--obligation` is optional only when exactly one obligation exists. `--units` defaults to one and must be one for a fixed payment; it is an integer quantum count for partial payments.

`bb pay-requests list --mine "$PAYER" --open` includes eligible public requests and currently open unpaid obligations. A list result is discovery, not a reservation of capacity. Query status and simulate before signing.

The SDK equivalents are `buildPaymentRequestV2`, `extractPaymentRequestV2Details`, and the V2 payment helpers. MCP exposes `build_payment_request_v2` through the same builder. Generated approvals, terms, and invariants must stay together. Editing `customData` after generation does not safely change an invoice.

## Split Payouts and Assets

One obligation can pay several recipients. Every listed payout is required by the same approval. A failure to pay any leg causes the transaction to fail; it is not a best-effort recipient loop.

Several denominations mean **all** listed assets are owed. They are not alternative checkout currencies. Calculate totals separately by exact denomination. If an obligation has two legs of the same denomination, its total is their sum. Network and protocol fees are additional and must be reviewed separately.

Public payers exclude all payout recipients. This prevents a recipient from counting self-payment toward the invoice. Named addresses must be valid canonical addresses and must satisfy the builder's recipient/payer restrictions. Do not use address aliases that change after creation.

## Immutable Terms

The standard builder freezes the economic terms, roster, approvals, conversion paths, and relevant collection permissions/invariants. The parser compares the actual on-chain configuration with the declared terms. This protects a payer from a manager silently replacing recipients or extending caps while retaining the same invoice description.

These are direct payments. There is no V2 cancellation, denial, escrow, automatic refund, conditional milestone release, or alternative-currency field. Unsupported features cannot be implemented by adding descriptive metadata. A separate agreement can arrange a later refund transaction, but it is not an enforceable branch of this invoice.

## Reusable Links

Set `kind: "payment-link"`. Omit `requiredPayments`, `partial`, and `distinctPayers`. Each use is a separate fixed payment and receipt; multiple independent obligations are supported. A link never reaches a final paid state merely because someone paid once.

Use an opening window and hard cutoff when needed. Repeated requests can succeed until the cutoff, so retries must reconcile the previous transaction first. A payment link gives no permission to debit a wallet later. It is not a subscription.

## Legacy Requests

The older `PaymentRequest` builder remains available as `bb build payment-request` and `bb pay-requests build`. The V2 path is explicitly `bb build payment-request-v2`. Do not reinterpret existing collections as V2 based only on a UI label such as invoice.

Legacy named requests can expose `deny`. Denial records refusal; the independent pay approval is still present, so denial is not cancellation. Public legacy requests cannot deny. V2 invoices and reusable links have no deny action. The still older `Invoices` format is also distinct and must retain its own parser and state handling.

See [State and Tracking](https://docs.bitbadges.io/standards/payment-tracking) for partial evidence, payment activity, cutoff boundaries, and manager reporting.

## Link JSON

Save this complete reusable-link input as `payment-link.json`. Each successful use pays the same fixed legs; there is no `requiredPayments` cap. Replace the demo recipient and metadata.

```json
{
  "version": 2,
  "kind": "payment-link",
  "obligations": [
    {
      "id": "payment-1",
      "payer": {
        "kind": "anyone"
      },
      "payouts": [
        {
          "recipient": "bb1xvenxvenxvenxvenxvenxvenxvenxvenlrd2nm",
          "denom": "ubadge",
          "amount": "1000000"
        }
      ],
      "startTime": "1",
      "endTime": "1893456000000"
    }
  ],
  "name": "Example: link",
  "image": "https://example.com/payment.png",
  "description": "Editable payment example. Replace demo addresses, amounts, dates and metadata before creating a collection."
}
```

```bash
bb build payment-request-v2 --json payment-link.json --creator "$CREATOR" --output-file payment-link-proposal.json
```

## Input Limits

| Input | Runtime requirement |
| --- | --- |
| Obligations | 1–100, unique IDs |
| Obligation ID | 1–64 ASCII letters, digits, `_` or `-` |
| Named roster | 1–100 unique canonical spendable `bb1` addresses |
| Payouts | 1–50; each `(recipient, denom)` pair unique |
| Count, target, timestamp | Positive integer string no greater than `18446744073709551615` |
| Coin amount | Positive integer string below `2^255`; totals must also fit |
| Denomination | 3–128 characters; first character a letter; remaining letters, digits, `/ : . _ -` |
| Window | `startTime <= endTime`; optional `dueAt` lies inside it |

`startTime: "0"` is rejected; use `"1"` for an already-open example. Payers cannot also be recipients. Partial targets cannot combine with `requiredPayments` or enabled `distinctPayers`. A link cannot contain finite counts, partial targets, or enabled distinct-payer limits. Unknown fields are rejected instead of silently becoming guardrails.

## Legacy Request JSON

Save the following input as `payment-request.json`, replace the demo addresses and metadata, then build it with the CLI command below. Legacy input uses display-unit `amount` and a single payer string. `payer: "All"` creates its public pay-only variant. It is not the V2 `payer` object, and a legacy `deny` action is not cancellation of the separate pay path.

<!-- sdk-example: buildPaymentRequest -->
```json
{
  "amount": 5,
  "denom": "BADGE",
  "payer": "bb1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zql3w7",
  "recipient": "bb1xvenxvenxvenxvenxvenxvenxvenxvenlrd2nm",
  "expiration": "30d",
  "uri": "https://example.com/metadata.json"
}
```

```bash
bb build payment-request --json payment-request.json \
  --amount "$(jq -r '.amount' payment-request.json)" \
  --denom "$(jq -r '.denom' payment-request.json)" \
  --payer "$(jq -r '.payer' payment-request.json)" \
  --recipient "$(jq -r '.recipient' payment-request.json)" \
  --creator "$CREATOR" --output-file payment-request-proposal.json
```

The repeated required flags are read from the same JSON file because CLI argument validation runs before loading JSON. Review the resulting proposal using the [CLI lifecycle](https://docs.bitbadges.io/standards/lifecycle). SDK reference: `buildPaymentRequest`.
