Solana Pay transfer requests do not need a backend

A transfer request is a URL. The wallet builds the transfer. There is no server.

solana:<recipient>?amount=<amount>&label=...&message=...&memo=...&reference=...

Spec: https://docs.solanapay.com/spec

A transaction request is different. That one is solana:https://... and needs a public HTTPS endpoint (GET for label, POST for a base64 transaction). Skip that if you only want SOL or an SPL token to land on an address.

Traps:

  1. Amount must be 0.5, never .5.
  2. Label, message, and memo need encodeURIComponent. Spaces become %20, not +.
  3. The URL does not pin a cluster. Many wallets default to mainnet. If you built it for devnet, switch the wallet or it looks stuck after the preview.
  4. Scanning a QR is not a payment. You know it paid when the transfer is on-chain. Put a unique reference pubkey on the URL, then getSignaturesForAddress(reference).
  5. A transfer request cannot call a custom Anchor program or move tokens out of a PDA. Those instructions are not in the URL.

A single static HTML file that builds the same string in the browser, no SDK: https://brewpage.app/public/67pLF7de2V/invoice.html

Check the recipient on an explorer before anyone pays. Not a wallet.

If the file is useful: 0.05 SOL for the file (memo solana-pay-invoice-v1) 0.01 SOL tip (memo solana-pay-tip-v1)

solana:5eTkbrezyM3Jfe7pfbXvH76LxRBwUrF1LPGZXfDaWEKA?amount=0.05&label=Static%20Solana%20Pay%20Invoice&message=solana-pay-invoice-v1&memo=solana-pay-invoice-v1
solana:5eTkbrezyM3Jfe7pfbXvH76LxRBwUrF1LPGZXfDaWEKA?amount=0.01&label=Static%20Solana%20Pay%20Invoice%20tip&message=solana-pay-tip-v1&memo=solana-pay-tip-v1

Open in Phantom or Solflare. Confirm the address starts with 5eTkbrezyM3 before you pay.