The @solana-commerce/solana-pay package provides complete
Solana Pay functionality for building payment
experiences, compatible with gill and
@solana/kit libraries. It handles URL
encoding/parsing, QR code generation with custom styling, and transaction
construction for both SOL and SPL token transfers.
Installation
pnpm add @solana-commerce/solana-pay
URL Encoding
encodeURL(fields)
Creates a Solana Pay URL that conforms to the
Solana Pay specification. This function
generates solana: protocol URLs that can be shared as links or encoded as QR
codes for mobile wallets to scan.
Parameters
fields(TransferRequestURLFields | TransactionRequestURLFields) - Configuration for the payment URL
TransferRequestURLFields
Used for simple payment requests (direct transfers):
-
recipient(Address, required) - The merchant's wallet address (base58-encoded public key) that will receive the payment. Can be a string orAddresstype fromgill. -
amount(bigint, optional) - Payment amount in lamports (atomic units). For SOL, 1 SOL = 1,000,000,000 lamports (9 decimals). For SPL tokens, use the token's decimal precision (e.g., USDC uses 6 decimals, so 1 USDC = 1,000,000). -
splToken(Address, optional) - The SPL token mint address for token payments. If omitted, the payment is assumed to be in SOL. -
reference(Address | Address[], optional) - Unique reference address(es) used for tracking payments. Generate usinggenerateKeyPairSigner().address. The reference is added as a read-only account to the transaction, allowing you to query for payments using this reference. -
label(string, optional) - Human-readable merchant or app name displayed to the user in their wallet (e.g., "Coffee Shop", "My Store"). -
message(string, optional) - Message displayed to the user before payment (e.g., "Thanks for your purchase!", "Tip for great service"). -
memo(string, optional) - On-chain memo attached to the transaction. Stored permanently on Solana. Useful for order IDs, invoice numbers, or other payment metadata.
TransactionRequestURLFields
Used for complex payment requests (including instructions):
-
link(URL, required) - The link to the transaction (Link). If the URL contains query parameters, it must be URL-encoded. -
label(string, optional) - Human-readable merchant or app name displayed to the user in their wallet (e.g., "Coffee Shop", "My Store"). -
message(string, optional) - Message displayed to the user before payment (e.g., "Thanks for your purchase!", "Tip for great service").
How It Works
The function performs several operations to construct a valid Solana Pay URL:
-
Protocol Prefix - Creates a URL with the
solana:protocol scheme (similar tomailto:orbitcoin:) -
Recipient as Pathname - Uses the recipient's base58 address as the URL pathname (e.g.,
solana:merchantWalletAddress123...) -
Amount Conversion - Converts the bigint lamport amount to a decimal string representation without floating-point precision issues.
-
Query Parameters - Appends all optional fields and references as URL-encoded query parameters
Returns
URL object with the solana: protocol that can be:
- Converted to a string with
.toString()for sharing - Passed to
createQR()for QR code generation - Used directly in anchor tags:
<a href={url.toString()}>Pay with Solana</a>
Example: Basic Payment
import { encodeURL } from "@solana-commerce/solana-pay";import { address } from "gill";const url = encodeURL({recipient: address("merchantWalletAddress123..."),amount: 100000000n, // 0.1 SOL (100 million lamports)label: "Coffee Shop",message: "Thanks for your order!"});console.log(url.toString());// solana:merchantWalletAddress123...?amount=0.1&label=Coffee%20Shop&message=Thanks%20for%20your%20order!
Example: SPL Token Payment
import { encodeURL } from "@solana-commerce/solana-pay";import { address } from "gill";const usdcMint = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");const url = encodeURL({recipient: address("merchantWallet..."),amount: 25000000n, // 25 USDC (6 decimals)splToken: usdcMint,label: "USDC Payment",message: "Pay with USDC stablecoin"});
Example: Payment with Tracking
Use references to identify specific payments:
import { encodeURL } from "@solana-commerce/solana-pay";import { address } from "gill";import { Keypair } from "@solana/web3.js";// Generate unique reference for this orderconst orderReference = (await generateKeyPairSigner()).address;const url = encodeURL({recipient: address("merchantWallet..."),amount: 500000000n, // 0.5 SOLreference: orderReference,memo: `Order-${Date.now()}`, // On-chain memolabel: "E-commerce Store",message: "Complete your purchase"});// Later, query the blockchain for transactions containing this reference// to verify payment was made
URL Parsing
parseURL(url)
Decodes and validates a Solana Pay URL, extracting all payment parameters. This function performs validation and converts amounts from decimal strings back to bigint lamports.
Parameters
url(string | URL) - The Solana Pay URL to parse. Can be a string or URL object.
Returns
A parsed TransferRequestURLFields or TransactionRequestURLFields object.
Example: Parse and Validate
import { parseURL, ParseURLError } from "@solana-commerce/solana-pay";try {const parsed = parseURL("solana:merchant123...?amount=1.5&label=Store&reference=ref123...");console.log(parsed.recipient); // Address objectconsole.log(parsed.amount); // 1500000000n (1.5 SOL in lamports)console.log(parsed.label); // "Store"console.log(parsed.reference); // [Address]// Convert back to human-readable formatconst solAmount = Number(parsed.amount) / 1e9;console.log(`Payment of ${solAmount} SOL`);} catch (error) {if (error instanceof ParseURLError) {console.error("Invalid Solana Pay URL:", error.message);}}
Example: URL Validator Function
import { parseURL, ParseURLError } from "@solana-commerce/solana-pay";function validateSolanaPayURL(urlString: string): {valid: boolean;error?: string;data?: any;} {try {const parsed = parseURL(urlString);// Additional business logic validationif (parsed.splToken) {return {valid: false,error: "Only SOL payments are supported"};}if (parsed.amount && parsed.amount < 1000000n) {return {valid: false,error: "Amount too small (minimum 0.001 SOL)"};}// etc.return {valid: true,data: {recipient: parsed.recipient.toString(),amount: parsed.amount ? Number(parsed.amount) / 1e9 : undefined,token: parsed.splToken?.toString()}};} catch (error) {return {valid: false,error: error instanceof ParseURLError ? error.message : "Unknown error"};}}
QR Code Generation
createQR(url, size, background, color)
Generates an SVG QR code optimized for Solana Pay URLs. The function produces styled, high-quality QR codes with rounded corners and customizable colors.
Parameters
url(string | URL) - The Solana Pay URL to encode in the QR codesize(number, default:512) - Width and height in pixelsbackground(string, default:'white') - Background color (hex or named color). Should be light for wallet compatibility.color(string, default:'black') - Foreground/dot color (hex or named color). Should be dark for contrast.
createStyledQRCode(url, options)
Returns
Promise<string> - SVG markup as a string that can be:
- Set as
imgelement src:<img src={qrCode} /> - Saved to a file
Example
import { createQR, encodeURL } from "@solana-commerce/solana-pay";import { address } from "gill";async function generatePaymentQR() {const url = encodeURL({recipient: address("merchant..."),amount: 100000000n, // 0.1 SOLlabel: "Coffee Shop"});const qrCode = await createQR(url.toString(),400, // 400x400 pixels"white", // White background"black" // Black foreground);// Display in browserdocument.getElementById("qr-container").innerHTML = qrCode;// Or use as image sourcedocument.getElementById("qr-image").src = qrCode;}
Example: Branded QR Code
import { createStyledQRCode, encodeURL } from "@solana-commerce/solana-pay";import { address } from "gill";async function createBrandedQR() {const url = encodeURL({recipient: address("merchant..."),amount: 25000000n, // 25 USDC (6 decimals)splToken: address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),label: "Coffee Shop",message: "Scan to pay with USDC"});const qr = await createStyledQRCode(url.toString(), {width: 600,margin: 3,color: {dark: "#9945FF", // Solana purplelight: "#F5F5DC" // Beige},errorCorrectionLevel: "H", // Higher correction for logodotStyle: "dots", // Circular dotscornerStyle: "extra-rounded",logo: "/coffee-logo.png", // Your logologoSize: 120,logoBackgroundColor: "#FFFFFF", // White padding behind logologoMargin: 10});return qr; // SVG string}
Transaction Building
createTransfer(rpc, sender, fields)
Builds a complete Solana transaction message for a payment transfer. This
function automatically detects whether to create a SOL or SPL token transfer
based on the splToken parameter, and constructs all necessary instructions.
The function sets a blockhash lifetime for the transaction using the latest
blockhash from the RPC client and returns a complete, unsigned transaction ready
for signing and submission to the RPC (type
TransactionMessageWithBlockhashLifetime, compatible with Solana Kit/Gill).
Parameters
-
rpc(Rpc<SolanaRpcApi>) - Solana RPC client fromgill. Create withcreateSolanaClient(rpcUrl).rpc. -
sender(Address) - The payer's wallet address. Must be a funded account that will sign the transaction. -
fields(CreateTransferFields) - Transfer configuration:recipient(Address, required) - Destination wallet addressamount(bigint, required) - Amount in lamports (SOL) or token atomic units (SPL)splToken(Address, optional) - SPL token mint address. If omitted, creates SOL transfer.reference(Address | Address[], optional) - Reference address(es) for trackingmemo(string, optional) - On-chain memo text
Returns
Promise<TransactionMessageWithBlockhashLifetime> - Complete transaction
message with:
- Version 0 format (supports address lookup tables)
- Blockhash lifetime (transaction expires after ~60 seconds)
- All necessary instructions (transfer + optional memo)
- Ready to be signed with wallet and submitted to RPC
Error Handling
Throws CreateTransferError with specific messages:
"sender not found"- Sender account doesn't exist"recipient not found"- Recipient account doesn't exist
Example: SOL Payment
import { createTransfer } from "@solana-commerce/solana-pay";import { createSolanaClient } from "gill";import { address } from "gill";const rpc = createSolanaClient("https://api.mainnet-beta.solana.com").rpc;// Build SOL transfer transactionconst txMessage = await createTransfer(rpc, address("sender-wallet-address"), {recipient: address("merchant-wallet-address"),amount: 100000000n, // 0.1 SOLmemo: "Coffee purchase"});// Transaction is ready to sign and send// (wallet signing is handled separately)console.log("Transaction ready:", txMessage);
Example: USDC Payment
import { createTransfer } from "@solana-commerce/solana-pay";import { createSolanaClient } from "gill";import { address } from "gill";const rpc = createSolanaClient("https://api.mainnet-beta.solana.com").rpc;const usdcMint = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");const txMessage = await createTransfer(rpc, address("sender-wallet"), {recipient: address("merchant-wallet"),amount: 25000000n, // 25 USDC (6 decimals)splToken: usdcMint,reference: [address("unique-ref-123...")],memo: "Order #12345"});
Is this page helpful?