# Create a payment request

> Issue a shareable request whose tag lets incoming private payments match themselves to your invoice.

## What this lets you do

Generate a shareable request so a payer can send you a specific amount without either of you revealing a wallet address. It carries a unique `paymentId`, a public `tag` derived from it, and a per-request key the payment is encrypted to. A payment stamped with the tag traces back to this specific request, so incoming private [transfers](/operations/transfer) reconcile to your invoice on their own.

## Constraints & limits

-   **Public request shape.** The shareable `PaymentRequest` carries `paymentId`, `noteAddressHashes`, `amount`, `tokenId`, `tag`, and `paymentPubKey`. It never carries the matching `noteSecret` values or the request private key.
-   **Recipient-local recovery state.** The SDK stores the request's `noteSecretsMap` locally and re-derives `paymentPrivKey` from your `viewingPrivateKey`, `ownerAddress`, and `paymentId` when the request is loaded. Discovery uses `paymentPrivKey` to decrypt a matching Note event, then uses the decoded `noteAddressHash` to look up the spendable `noteSecret`. If `noteSecretsMap` is lost, you can identify the matched payment but cannot reconstruct the note.
-   **Requires NotePayload v2.** Payment-request matching reads the request's slot data (`noteAddressHash`) from the encrypted note payload's v2 codec, so the sender's build must emit the v2 payload.
-   **Tag is not authenticated by the protocol.** A malicious sender could stamp a payment with someone else's `tag`. The recipient sees the payment under "this request" but the sender chose the tag. Pair with off-chain signing if non-repudiation matters.

## What it unlocks next

-   [Discover incoming payments by paymentId](discover-by-paymentid): discovery tag-matches Note events on the request's `tag`, then decrypts each hit with the request's `paymentPrivKey` after the payment lands.
-   Invoice-style UX: a recipient generates request #N, attaches it to their invoice software, scans periodically for matched payments.
-   Bridge use cases where the payment needs to be tied to an off-chain identifier (order ID, contract reference) without exposing it publicly.

**Why discovery is fast:** each request has a public tag derived from its `paymentId`. Discovery uses that tag to find candidate Note events, then decrypts only the matching candidates with the request key. Ordinary [discoverable transfers](/concepts/discoverable-vs-oob) use your viewing key instead.

## How to use it

```ts
const toHex = (value: bigint): `0x${string}` => `0x${value.toString(16)}`;
const paymentRequest = await session.generatePaymentRequest({
    amount: toHex(parseUnits("0.5", 6)),        // 0.5 USDC
    tokenId: USDC_SEPOLIA,
});
```

**Handling payment-request links.** Wallets or frontends may bundle recovery material such as `noteSecretsMap` or the request private key into their payment-request links, so treat any such link as sensitive and only send it over a secure channel.

## How the payer fulfills it

The payer calls `prepareFulfillPaymentRequest`, the wrapper that builds the transfer for a request you sent them. It [encrypts the note to the request's `paymentPubKey`](encrypt-to-pubkey) and sets the `tag` as the `Note` event hint, so only your request state can decrypt it.

```ts
// On the payer's side, after receiving the request:
const prepared = await session.prepareFulfillPaymentRequest({
    paymentRequest,
    inputCommitments: [noteToSpend.commitment],
});
const result = await session.relayTransfer(prepared.relayOptions[0]);
```

The result submits like an ordinary [transfer](/operations/transfer).

## Behind the scenes

SDK call `session.generatePaymentRequest({amount, tokenId})`
Storage Off-chain. The protocol doesn't track requests, so the SDK persists open requests through its configured payment-request storage adapter.
Cryptography X25519 keypair, derived per request by HKDF from your `viewingPrivateKey`, your `ownerAddress`, and the request's `paymentId`. The derivation is deterministic rather than random, so loading the request re-derives the same keypair. Disclosing one request's `paymentPrivKey` does not expose your viewing key, other requests, or non-request incoming payments.
