什么是Solana Pay以及如何使用它

QuickNode 发布于 2026-07-10 阅读 14

本文是一份Solana Pay的实战指南,详细介绍了如何使用Solana Pay JavaScript库在Solana区块链上构建支付应用。内容包括环境搭建、生成支付请求URL、处理支付(解析URL、构建并发送交易)以及验证交易(通过参考地址查找签名并校验金额与备忘录)。文章还提供了完整的代码示例和步骤说明,并提示了扩展至SPL代币支付的思路。适合有Node.js和TypeScript基础的开发者快速上手Solana Pay。

概述

Solana Pay 是一个基于 Solana 区块链构建的快速、易用、安全的支付解决方案。在本指南中,你将学习如何使用 Solana Pay 创建一个简单的支付应用、处理支付以及验证交易。

你将完成的内容

使用 Solana Pay 创建一个简单的模拟:

  • 模拟结账体验并生成支付 URL。
  • 处理一笔支付。
  • 验证交易。

请关注最后的额外章节!

你需要准备

  • 已安装 Nodejs(版本 16.15 或更高)
  • 具备 TypeScript 经验并已安装 ts-node
  • 一个包含 Devnet SOL 的 Solana Paper 钱包(.json)(示例脚本

什么是 Solana Pay?

Solana Pay 是一个强大且灵活的 JavaScript 库,能够在 Solana 区块链上实现无缝的商务交易。通过使用 Token 转账 URL 方案,Solana Pay 确保钱包和服务之间的互操作性,使企业和开发者无需中介即可轻松接受 SOL 或任意 SPL Token 的支付。由于 Solana 提供了近乎即时的结算和低交易费用,Solana Pay 成为降低买卖双方摩擦的理想工具。

Solana Pay 的工作原理

Solana Pay 示意图来源:docs.solanapay.com

上图展示了客户端(用户钱包)与服务器(商家网站或应用)之间 Solana Pay 交易的过程。

  1. 用户创建订单:用户访问商家的网站或应用,选择要购买的产品或服务。
  2. 商家创建支付链接:网站生成一个 Solana Pay 交易请求 URL,其中可能包含支付链接、“立即支付”按钮或二维码。
  3. 用户扫描并批准交易:用户钱包通过 Solana Pay JavaScript 库解析交易请求 URL,从中提取必要参数。钱包将 Solana 交易发送到集群进行处理。
  4. 服务器查找并验证交易:商家服务器验证链上交易详情(如金额、Token 和接收方地址)是否与指定请求匹配。

此过程确保支付快速处理,交易由 Solana 区块链保护,且商家收到正确的支付金额。

让我们试试看吧!

构建 Solana Pay 应用

设置环境

在开始构建 Solana Pay 应用之前,你需要设置环境并安装所需依赖。打开终端并运行以下命令:

## 为项目创建新目录
mkdir solana-pay-demo
cd solana-pay-demo

## 创建一个启用 .json 导入的 **tsconfig.json**:
tsc -init --resolveJsonModule true

## 初始化一个新的 Node.js 项目并安装依赖
npm init -y
npm install --save @solana/web3.js@1 @solana/pay bignumber.js
### 或者如果你更喜欢 yarn
yarn init -y
yarn add @solana/web3.js@1 @solana/pay bignumber.js

这将在项目目录中创建一个新目录,用 package.jsontsconfig 初始化,并安装必要的依赖项。

在项目目录中创建一个名为 index.ts 的新文件。在终端中输入:

echo > index.ts

准备就绪后,你的环境应如下所示:

环境

获取 Quicknode 端点
使用 Quicknode 端点连接到 Solana 集群

要基于 Solana 构建,你需要一个 API 端点来连接网络。你可以使用公共节点,或自行部署和管理基础设施;但如果你希望获得 8 倍更快的响应时间,可以将繁重的工作交给我们。

了解为何超过 50% 的 Solana 项目选择 Quicknode,并在此处 开始免费试用。我们将使用 Solana Devnet 端点。

复制 HTTP Provider 链接:

安装依赖

index.ts 文件顶部,导入所需的依赖:

import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, sendAndConfirmTransaction, SystemProgram, Transaction, TransactionInstruction } from '@solana/web3.js';
import { encodeURL, validateTransfer, parseURL, TransferRequestURL, findReference } from '@solana/pay';
import BigNumber from 'bignumber.js';
定义关键常量

我们需要完成的第一项任务是创建一个带有钱包并为其注入资金的账户。我们将使用下面的便捷工具自动生成新钱包并空投 1 SOL。(如果你更偏好手动方式,也可以使用 Keypair.generate()requestAirdrop() 函数实现。)

🔑 生成一个带有 Devnet SOL 的新钱包

注意: 过于频繁地发送空投请求可能会触发 429(请求过多)错误。

创建新钱包

成功生成密钥对后,你会注意到两个新常量:secretpayer(Keypair)。secret 是一个 32 字节数组,用于生成公钥和私钥。payer 是一个 Keypair 实例,用于签署交易(我们已经空投了一些 Devnet SOL 来支付 Gas 费用)。如果尚未添加,请务必在代码的导入下方添加:

const secret = [0,...,0]; // 替换为你的密钥
const payer = Keypair.fromSecretKey(new Uint8Array(secret));

你还需要定义一些稍后将在应用中使用的常量。在 payer 常量下方添加以下代码:

// 常量
const myWallet = 'DemoKMZWkk483hX4mUrcJoo3zVvsKhm8XXs28TuwZw9H'; // 替换为你的钱包地址(这是支付将发送到的目的地)
const recipient = new PublicKey(myWallet);
const quickNodeEndpoint = 'https://example.solana-devnet.quiknode.pro/123456/'; // 替换为你的 Quicknode 端点
const connection = new Connection(quickNodeEndpoint, 'confirmed');
const amount = new BigNumber(0.1); // 0.1 SOL
const reference = new Keypair().publicKey;
const label = 'Quicknode Guide Store';
const message = `Quicknode Demo - Order ID #0${Math.floor(Math.random() * 999999) + 1}`;
const memo = 'QN Solana Pay Demo Public Memo';

myWalletquickNodeEndpoint 替换为你的钱包地址和上一步中的 Quicknode 端点。

以下是每个常量的作用总结:

  • myWallet 是支付的目标地址。
  • recipient 是支付将发送到的目标公钥。
  • quickNodeEndpoint 是你在上一步创建的 Quicknode 端点。
  • connection 是与 Solana 网络的连接。
  • amount 是要发送的 SOL 数量(以 SOL 为单位,而非 lamports)。
  • reference(可选)是一个随机密钥,用于在链上验证交易(可以用作客户端 ID)——有关参考密钥的更多信息,请参阅此处
  • label(可选)描述转账请求的来源(例如商店名称、品牌、应用)——钱包在批准交易前可能会用它向买家显示信息。
  • message(可选)是一条描述转账请求性质的消息(例如商品名称、订单 ID 或感谢语)——钱包在批准交易前可能会用它向买家显示信息。
  • memo(可选)是一个公开备忘,将包含在链上交易中。

以下是在 Phantom 钱包中买家的批准流程中显示 messagelabel 的示例:

Phantom 钱包

设置好环境后,你就可以构建应用了。

模拟结账体验

在这一步中,你将创建一个名为 generateUrl 的函数,该函数通过生成支付请求 URL 来模拟结账体验:

async function generateUrl(
    recipient: PublicKey,
    amount: BigNumber,
    reference: PublicKey,
    label: string,
    message: string,
    memo: string
) {
    console.log('1. 创建支付请求链接');
    const url: URL = encodeURL({ recipient, amount, reference, label, message, memo });
    console.log('支付请求链接:', url);
    return url;
}

通常,你应该在后端服务器上生成支付请求 URL,以确保数据的安全性和完整性。通过在服务器端处理敏感数据和交易详情,可以降低将关键信息暴露给恶意用户或第三方的风险。这种方法还允许你更好地控制和管理交易数据,从而更容易实施额外的安全措施,例如验证交易或监控欺诈活动。

不过,在本指南中,我们将通过客户端运行函数来简化此过程。

此时,你应该能够生成一个支付请求 URL。要测试它,请调用你的函数:

(async () => {
    try {
        const url = await generateUrl(recipient, amount, reference, label, message, memo);
        console.log('成功');
    } catch (err) {
        console.error(err);
    }
})();

然后在终端中输入:

ts-node index.ts

你应该会在终端中看到一个 URL 对象。注意 href 属性,它是用于发送支付的实际 URL。它应该类似于这样:solana:DemoKMZWkk483hX4mUrcJoo3zVvsKhm8XXs28TuwZw9H?amount=0.1&reference=BurHXKwdNbcnShxWXL1khovANkYY6FWS4yTdN6FWCDsX&label=Quicknode+Guide+Store&message=Quicknode+Demo+-+Order+ID+%230405909&memo=QN+Solana+Pay+Demo+Public+Memo8

如果你希望清理终端输出,可以删除或注释掉 console.log('支付请求链接:', url); 这一行。

干得漂亮。你已经成功生成了一个支付请求 URL。如果你正在构建一个实际应用,你可能会将这个 URL 发送给买家(通常通过二维码),然后买家使用它来支付。我们将在未来的指南中介绍如何构建前端和二维码,不过你可以将你的 URL 的 href 传入二维码生成器(例如 qr-code-generator.com)来测试。

接下来继续处理支付请求 URL。

处理支付

接下来,你将创建 processPayment 函数,该函数解析支付请求链接以提取支付详情,创建新的 Solana 交易,并将其发送到 Solana 区块链。通常这由用户的钱包应用或使用钱包适配器的去中心化应用(dApp)处理。前端组件会解析支付请求链接并通过用户的钱包发起资金转移。让我们来看看如何操作。

index.ts 文件中添加以下代码:

async function processPayment(url: URL, payer: Keypair) {
    // 解析支付请求链接
    console.log('2. 解析支付请求链接');
    const { recipient, amount, reference, label, message, memo } = parseURL(url) as TransferRequestURL;
    if (!recipient || !amount || !reference) throw new Error('无效的支付请求链接');

    console.log('3. 组装交易');
    const tx = new Transaction();

    // 如果提供了备忘,则添加备忘指令
    if (memo != null) {
        tx.add(
            new TransactionInstruction({
                programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'),
                keys: [],
                data: Buffer.from(memo, 'utf8'),
            })
        );
    }
    // 创建转账指令
    const ix = SystemProgram.transfer({
        fromPubkey: payer.publicKey,
        toPubkey: recipient,
        lamports: amount.multipliedBy(LAMPORTS_PER_SOL).integerValue(BigNumber.ROUND_FLOOR).toNumber()
    });
    // 如果提供了参考密钥,则将其添加到指令中
    if (reference) {
        const ref = Array.isArray(reference) ? reference : [reference];
        for (const pubkey of ref) {
            ix.keys.push({ pubkey, isWritable: false, isSigner: false });
        }
    }
    // 将转账指令添加到交易中
    tx.add(ix);

    // 将交易发送到 Solana 网络并确认已处理
    console.log('4. 🚀 发送并确认交易');
    const txId = await sendAndConfirmTransaction(connection, tx, [payer]);
    console.log(`      交易: https://explorer.solana.com/tx/${txId}?cluster=devnet`);
}

让我们分解一下这里发生的事情:

  • 首先,我们使用 @solana/pay 库中的 parseURL 函数解析支付请求链接。该函数返回一个包含支付详情(包括 recipient、amount、reference、label、message 和 memo)的对象,我们对其进行解构以便访问。
  • 接下来,我们创建一个新的空 Solana Transaction
  • 如果提供了备忘,我们会使用 Solana Memo 程序向交易添加一个备忘指令。如果你不熟悉 Solana Memo 程序,可以在我们的指南中阅读更多内容:此处。简而言之,它会对我们的 memo 进行编码并存储在链上交易数据中。
  • 接着,我们使用 SystemProgram.transfer 函数创建并添加一个转账指令。如果提供了参考密钥,我们也会将其添加到指令中。
  • 注意:如此处所示,转账指令必须位于备忘指令之后。Solana Pay 的验证函数期望转账指令位于最后位置(请参阅 Solana Pay 规范)。

到目前为止,我们已经创建了一个模拟环境,卖家可以生成支付请求链接,买家可以响应该请求并向商家付款。最后,我们需要一种方法来验证商家是否收到了付款,并且付款详情正确无误。

验证支付

为了验证是否已收到付款,你将创建 verifyPayment 函数,该函数将查询 Solana 区块链以检查是否已收到付款,并且付款详情与原始请求匹配。在典型环境中,这类函数由卖家的后端调用。在这个简化示例中,我们也会从客户端处理它。在 index.ts 文件中添加以下代码:

async function verifyTx(
    recipient: PublicKey,
    amount: BigNumber,
    reference: PublicKey,
    memo: string
) {
    console.log(`5. 验证支付`);
    // 商家应用通过其在转账链接中提供的唯一参考地址定位交易签名
    const found = await findReference(connection, reference);

    // 商家应用应始终验证交易是否向接收者转账了预期金额
    const response = await validateTransfer(
        connection,
        found.signature,
        {
            recipient,
            amount,
            splToken: undefined,
            reference,
            memo
        },
        { commitment: 'confirmed' }
    );
    return response;
}

让我们分解一下这里发生的事情:

  • 首先,我们使用 @solana/pay 库中的 findReference 函数从集群中定位交易签名。该方法对提供的参考地址执行 getSignaturesForAddress RPC 调用。
  • 接下来,我们使用 @solana/pay 库中的 validateTransfer 函数验证交易是否向接收者转账了预期金额。该方法对提供的交易签名执行 getTransaction RPC 调用。然后检查 recipient 是否收到了正确的 amount,并且 memo 是否与预期值匹配。

支付日!

最后,让我们运行应用。在 index.ts 文件中,将之前的 async 代码块替换为以下内容:

(async () => {
    try {
        console.log(`⚡️ Solana Pay Demo ⚡️`);
        console.log(`  处理支付:`);
        console.log(`   - 金额 ${amount} SOL`);
        console.log(`   - 发送方 ${payer.publicKey.toBase58()}`);
        console.log(`   - 接收方 ${recipient.toBase58()}`);
        const url = await generateUrl(recipient, amount, reference, label, message, memo);
        await processPayment(url, payer);
        const response = await verifyTx(recipient, amount, reference, memo);
        if (!response || response.meta?.err) throw new Error('未验证通过');
        console.log('🎉 支付确认!');
    } catch (err) {
        console.error(err);
    }
})();

现在运行应用。在终端中输入:

ts-node index

你应该会在终端中看到以下输出:

结果

你应该能够通过终端中的 Explorer URL 在 Solana 区块链上查看交易(并同时看到备忘和转账指令):

Solana Explorer 上的交易

干得漂亮!

额外加分:SPL Token

这个练习对于创建和发送 SOL 付款很有帮助,但如果你想发送 SPL Token(例如 USDC)呢?Solana Pay 也使得发送 SPL Token 变得容易。作为加分任务,尝试修改 generateUrlprocessPaymentverifyTx 函数,以发送 SPL Token 而不是 SOL。

提示:

  • generateUrl 函数添加一个 splToken 参数。
  • SystemProgram.transfer 指令替换为 createTransferCheckedInstruction 指令(你需要为 payerrecipientgetOrCreateAssociatedTokenAccount)。
  • 你需要在 validateTransfer 函数中添加一个 splToken 参数。

总结

在本指南中,你学习了如何使用 Solana Pay 创建简单支付应用、处理支付以及验证交易的基本原理。通过利用 Solana Pay,你可以轻松地在 Solana 区块链上构建快速、安全且用户友好的支付解决方案。

请记住,本指南提供了一个简化示例,在实际应用中,你需要考虑额外的安全措施、用户体验和错误处理。将敏感数据和交易处理分离在客户端和服务器端至关重要,以最大限度降低关键信息暴露的风险。

如果你需要帮助或想分享你使用 Solana Pay 构建的内容,请通过 DiscordTwitter 告诉我们。

我们 ❤️ 反馈!

如果你对本指南有任何反馈或问题,请告诉我们。我们期待听到你的意见!

资源

  • 原文链接: quicknode.com/guides/sol...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~

相关文章

0 条评论