Yêu cầu giao dịch cho phép các luồng thanh toán tương tác bằng cách cho phép ví giao tiếp với máy chủ của bạn để soạn bất kỳ giao dịch Solana nào. Điều này mở khóa các trường hợp sử dụng nâng cao như đúc NFT, định giá động, giao dịch DeFi đa bước và logic nghiệp vụ tùy chỉnh.
Tổng quan
Yêu cầu giao dịch tuân theo định dạng URL này:
solana:<server-endpoint-url>
Ví thực hiện hai yêu cầu HTTP đến điểm cuối của bạn:
- Yêu cầu GET - Lấy thông tin thanh toán (nhãn, biểu tượng)
- Yêu cầu POST - Lấy giao dịch để ký
Thiết lập Cơ bản
1. Cài đặt Phụ thuộc
pnpm add @solana/pay@beta @solana/kit @solana-program/system \@solana/kit-plugin-rpc @solana/kit-plugin-payer @solana/kit-plugin-instruction-plan
2. Tạo Điểm cuối API
Thiết lập một điểm cuối API xử lý cả yêu cầu GET và POST:
// pages/api/pay.ts (Next.js) or similar endpointimport { address, createNoopSigner, lamports } from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";import { createMerchantClient } from "@solana/pay";const MERCHANT_WALLET = address(process.env.MERCHANT_WALLET);export default async function handler(req, res) {if (req.method === "GET") {return handleGet(req, res);}if (req.method === "POST") {return handlePost(req, res);}return res.status(405).json({ error: "Method not allowed" });}// GET request handlerasync function handleGet(req, res) {const label = "My Store";const icon = "https://mystore.com/icon.png";res.status(200).json({label,icon});}// POST request handlerasync function handlePost(req, res) {try {const { account } = req.body;if (!account) {return res.status(400).json({ error: "Missing account parameter" });}const buyerAddress = address(account);// Create the merchant client for building the transactionconst merchant = createMerchantClient({rpcUrl: process.env.SOLANA_RPC_URL});// Build transfer instruction (createNoopSigner because the wallet signs later)const instruction = getTransferSolInstruction({source: createNoopSigner(buyerAddress),destination: MERCHANT_WALLET,amount: lamports(100_000_000n) // 0.1 SOL});// Build a base64-encoded transactionconst transaction = merchant.pay.buildTransaction(buyerAddress, [instruction]);res.status(200).json({transaction,message: "Thanks for your purchase!"});} catch (error) {console.error("Transaction creation error:", error);res.status(500).json({ error: "Failed to create transaction" });}}
3. Tạo URL Thanh toán
Tạo URL yêu cầu giao dịch:
import { encodeURL } from "@solana/pay";// Your API endpointconst apiUrl = "https://yoursite.com/api/pay";// Create transaction request URLconst url = encodeURL({link: new URL(apiUrl)});console.log(url.toString());// Output: solana:https://yoursite.com/api/pay
Phía Ví: Lấy Yêu cầu Giao dịch
Ở phía ví, sử dụng fetchTransaction để lấy và ký giao dịch từ URL yêu cầu giao
dịch:
import { createWalletClient } from "@solana/pay";const wallet = createWalletClient({rpcUrl: "https://api.mainnet-beta.solana.com",payer: myWalletSigner});// Parse the Solana Pay URLconst parsed = wallet.pay.parseURL(url);if ("link" in parsed) {// This is a transaction request — fetch the transaction from the serverconst transaction = await wallet.pay.fetchTransaction(myWalletSigner.address,parsed.link);// Sign and send the transaction...}
Ví dụ Nâng cao
Thanh toán Token SPL
Tạo giao dịch thanh toán USDC:
import { address } from "@solana/kit";import {fetchMint,findAssociatedTokenPda,getTransferCheckedInstruction,TOKEN_PROGRAM_ADDRESS} from "@solana-program/token";async function createTokenPaymentInstruction(rpc, buyer, amount) {const USDC_MINT = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");// Get mint info for decimalsconst mint = await fetchMint(rpc, USDC_MINT);// Derive associated token accountsconst [buyerATA] = await findAssociatedTokenPda({owner: buyer,tokenProgram: TOKEN_PROGRAM_ADDRESS,mint: USDC_MINT});const [merchantATA] = await findAssociatedTokenPda({owner: MERCHANT_WALLET,tokenProgram: TOKEN_PROGRAM_ADDRESS,mint: USDC_MINT});// Calculate token amount with decimalsconst tokenAmount = BigInt(Math.round(amount * 10 ** mint.data.decimals));return getTransferCheckedInstruction({source: buyerATA,mint: USDC_MINT,destination: merchantATA,authority: buyer,amount: tokenAmount,decimals: mint.data.decimals});}
Định giá Động với Giảm giá
Triển khai định giá động dựa trên dữ liệu khách hàng:
async function handlePost(req, res) {const { account } = req.body;const buyer = address(account);// Check if customer holds discount NFTconst hasDiscountNFT = await checkForDiscountNFT(buyer);// Calculate dynamic pricelet price = 10; // $10 base priceif (hasDiscountNFT) {price = price * 0.8; // 20% discount}const merchant = createMerchantClient({rpcUrl: process.env.SOLANA_RPC_URL});const instruction = await createTokenPaymentInstruction(merchant.rpc,buyer,price);const transaction = merchant.pay.buildTransaction(buyer, [instruction]);const message = hasDiscountNFT? `20% discount applied! Total: $${price.toFixed(2)}`: `Total: $${price.toFixed(2)}`;res.status(200).json({ transaction, message });}
Tích hợp Quản lý Đơn hàng
Tích hợp với hệ thống đơn hàng hiện có của bạn:
import { address, AccountRole, createNoopSigner, lamports } from "@solana/kit";// Enhanced POST handler with order managementasync function handlePost(req, res) {const { account } = req.body;const { orderId } = req.query;if (!orderId) {return res.status(400).json({ error: "Missing order ID" });}// Get order detailsconst order = await db.orders.findById(orderId);if (!order || order.status !== "pending") {return res.status(400).json({ error: "Invalid order" });}const buyer = address(account);// Create payment instruction (createNoopSigner because the wallet signs later)const instruction = getTransferSolInstruction({source: createNoopSigner(buyer),destination: MERCHANT_WALLET,amount: lamports(BigInt(Math.round(order.total * 1e9)))});// Add reference for tracking (accounts is readonly, so spread into a new instruction)const withRef = {...instruction,accounts: [...(instruction.accounts ?? []),{address: address(order.referenceKey),role: AccountRole.READONLY}]};const merchant = createMerchantClient({ rpcUrl: process.env.SOLANA_RPC_URL });const transaction = merchant.pay.buildTransaction(buyer, [withRef]);// Update order statusawait db.orders.update(orderId, {status: "processing",walletAddress: account});res.status(200).json({transaction,message: `Order ${order.id} - ${order.items.length} items`});}
Xác thực Thanh toán
Xác thực các giao dịch đã hoàn thành:
import { address } from "@solana/kit";import { createMerchantClient } from "@solana/pay";const merchant = createMerchantClient({rpcUrl: "https://api.mainnet-beta.solana.com"});async function validatePayment(reference, recipient, amount) {try {const found = await merchant.pay.findReference(reference);// findReference only locates a signature mentioning the reference address.// validateTransfer checks recipient, amount, and token match.await merchant.pay.validateTransfer(found.signature, {recipient,amount,reference});return {success: true,signature: found.signature};} catch (error) {console.error("Payment validation error:", error);}return { success: false };}
Xử lý Lỗi
Triển khai xử lý lỗi mạnh mẽ:
async function handlePost(req, res) {try {const { account } = req.body;if (!account) {return res.status(400).json({error: "Missing account parameter",code: "MISSING_ACCOUNT"});}let buyer;try {buyer = address(account);} catch {return res.status(400).json({error: "Invalid account address",code: "INVALID_ACCOUNT"});}const instruction = getTransferSolInstruction({source: createNoopSigner(buyer),destination: MERCHANT_WALLET,amount: lamports(100_000_000n)});const merchant = createMerchantClient({rpcUrl: process.env.SOLANA_RPC_URL});const transaction = merchant.pay.buildTransaction(buyer, [instruction]);res.status(200).json({transaction,message: "Payment ready"});} catch (error) {console.error("Transaction request error:", error);res.status(500).json({error: "Internal server error",code: "INTERNAL_ERROR"});}}
Thực hành Tốt nhất
Bảo mật
- Xác thực tất cả các tham số đầu vào
- Chỉ tính toán số tiền ở phía máy chủ
- Sử dụng HTTPS cho tất cả các endpoint
- Triển khai giới hạn tốc độ yêu cầu
- Lưu trữ dữ liệu nhạy cảm một cách an toàn
Hiệu suất
- Lưu cache dữ liệu thường xuyên truy cập
- Sử dụng connection pooling
- Triển khai timeout cho request
- Cân nhắc sử dụng webhook để xác nhận thanh toán
Trải nghiệm Người dùng
- Cung cấp thông báo lỗi rõ ràng
- Hiển thị tiến trình giao dịch
- Xử lý vấn đề mạng một cách mượt mà
- Hỗ trợ thử lại giao dịch
Is this page helpful?