Запит на транзакцію
Запити на транзакції розкривають повну потужність Solana Pay, дозволяючи створювати динамічні транзакції на сервері, які можуть обробляти будь-який тип операцій Solana.
На відміну від запитів на переказ, які містять всю платіжну інформацію в URL, запити на транзакції використовують інтерактивні кінцеві точки для створення користувацьких транзакцій на основі даних у реальному часі та бізнес-логіки.
Цей підхід перетворює Solana Pay з простої платіжної системи на повноцінну комерційну платформу, здатну обробляти складні бізнес-сценарії, динамічне ціноутворення та складні потоки транзакцій.
Як працюють запити на транзакції
Запити на транзакції використовують простий формат URL, який вказує на кінцеву точку вашого сервера:
solana:<link>
Значення link
має бути URL до вашої API-кінцевої точки, яка обробляє як запити GET
, так і POST
. Коли користувач сканує QR-код запиту на транзакцію, його гаманець ініціює чотирикроковий процес:
- Початковий GET-запит: Отримує інформацію для відображення, наприклад, назву вашого бізнесу та логотип
- Підтвердження користувача: Гаманець показує користувачу інформацію про бізнес
- POST-запит: Надсилає публічний ключ користувача на вашу кінцеву точку
- Відповідь з транзакцією: Ваш сервер створює користувацьку транзакцію та повертає її як рядок у форматі base64
Потім гаманець представляє цю транзакцію користувачу для схвалення та підписання.
solana:https://myapi.com/pay?amount=100&product=premium&reference=abc123
Створення API-кінцевої точки
Створення кінцевої точки запиту на транзакцію вимагає обробки як GET, так і POST запитів за однією URL-адресою. Ось структура з використанням Next.js App Router:
import { NextRequest, NextResponse } from 'next/server';
// CORS headers for wallet compatibility
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
export async function OPTIONS() {
return new NextResponse(null, { status: 200, headers: corsHeaders });
}
export async function GET() {
// Implementation details below...
}
export async function POST(request: NextRequest) {
// Implementation details below...
}
Обробник GET-запитів
GET-запит надає інформацію для відображення, яка допомагає користувачам зрозуміти, з чим вони взаємодіють:
export async function GET() {
return NextResponse.json({
label: "Coffee Shop Demo",
icon: "https://solana.com/src/img/branding/solanaLogoMark.svg",
}, { headers: corsHeaders });
}
Формат відповіді:
{
"label": "Coffee Shop Demo",
"icon": "https://solana.com/src/img/branding/solanaLogoMark.svg"
}
Ця інформація допомагає користувачу зрозуміти, з чим він збирається взаємодіяти, перш ніж перейти до фактичного створення транзакції.
Обробник POST-запитів
POST-запит — це де запити транзакцій дійсно розкривають свій потенціал. Ваша кінцева точка отримує публічний ключ користувача і будує повністю індивідуальну транзакцію:
export async function POST(request: NextRequest) {
// Parse user's public key from request body
const body = await request.json();
const { account } = body;
// Connect to Solana network
const connection = new Connection(clusterApiUrl("devnet"));
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
// Create transaction with user as fee payer
const transaction = new Transaction({
feePayer: new PublicKey(account),
blockhash: blockhash,
lastValidBlockHeight: lastValidBlockHeight,
});
// ========================================
// ADD YOUR CUSTOM INSTRUCTIONS HERE
// ========================================
// This is where you build your transaction logic.
// You can add any combination of instructions:
// Example 1: Simple SOL transfer
// const transferInstruction = SystemProgram.transfer({
// fromPubkey: new PublicKey(account),
// toPubkey: new PublicKey("YOUR_MERCHANT_WALLET"),
// lamports: LAMPORTS_PER_SOL * 0.01, // 0.01 SOL
// });
// transaction.add(transferInstruction);
// Serialize the transaction for the wallet
const serializedTransaction = transaction.serialize({
requireAllSignatures: false,
verifySignatures: false,
});
return NextResponse.json({
transaction: serializedTransaction.toString('base64'),
message: "Transaction created successfully", // Customize this message
}, { headers: corsHeaders });
}
Advanced Capabilities
Транзакції з обмеженим доступом
Запити транзакцій дозволяють реалізувати складний контроль доступу, перевіряючи умови перед створенням транзакцій. Оскільки ви контролюєте кінцеву точку, ви можете перевіряти володіння NFT, членство в білому списку або будь-які інші критерії:
// Check NFT ownership before building transaction
const nfts = await metaplex.nfts().findAllByOwner({ owner: account }).run();
const hasRequiredNFT = nfts.some(nft =>
nft.collection?.address.toString() === requiredCollection
);
if (!hasRequiredNFT) {
return response.status(403).json({
error: "Access denied: Required NFT not found"
});
}
// Build transaction only for verified users
Часткове підписання для підвищеної безпеки
Для транзакцій, що вимагають схвалення від адміністративної пари ключів або багатосторонньої автентифікації, Solana Pay підтримує часткове підписання. Ваш сервер може додати свій підпис перед надсиланням користувачу:
const transaction = new Transaction({
feePayer: account,
blockhash,
lastValidBlockHeight,
});
// Add your instructions requiring admin signature
transaction.add(customInstruction);
// Partially sign with your admin keypair
transaction.partialSign(adminKeypair);
// Send to user for final signature
const serializedTransaction = transaction.serialize({
requireAllSignatures: false,
});
Example
Як повноцінний приклад, ви можете використати Приклад Solana NFT Minter від Solana Foundation