Solana Pay 集成指南(2026):扫码收款、真实的坑,和 Finality 的捷径
Solana Pay 是开放、零费用的扫码支付规范——但把它集成好,比 hello-world 难得多。一支在生产环境跑着它的团队写的指南:URI 格式、三个真会咬人的坑、二维码之后才开始的活,以及一次 API 调用的捷径。
先给答案:Solana Pay 是一个开放规范(Apache-2.0,零费用,无中间人),把一笔完整的付款——收款人、金额、代币、备注——编码进一个 URI,任何 Solana 钱包扫码即付。把 URI 拼出来是一个下午的事;把支付做出来——检测、金额匹配、对账、结算——才是真正的工程。这篇两头都讲实话:怎么自己把二维码这一侧建起来、我们在 Finality 生产环境里踩过的三个坑,以及那条捷径——一次 API 调用,返回一个自带 Solana Pay 二维码、支持交易所直付、链上分账已经做完的收银台。
Solana Pay 到底是什么
规范定义了两种请求类型,分清楚它们能省你一周:
- Transfer Request——
solana:<收款地址>?amount=...&spl-token=...——是非交互式的:二维码本身就包含整笔付款。钱包不需要回调你的服务器;顾客扫码、确认,转账就发生了。收银台要的就是它。 - Transaction Request——
solana:https://你的服务器/...——让钱包去你的服务端取一笔构造好的交易。复杂流程用得上,"付我 49 USDC"用不上。
商户收银选 Transfer Request 没有悬念:不需要服务器往返,印在纸上能用,显示在手机屏上能用,贴在邮件里也能用。
构造 URI(真代码)
规范里的格式:
solana:<收款地址>?amount=<小数金额>&spl-token=<mint>&label=<商户名>&message=<订单号>一个生产形状的构造函数:
export function buildSolanaPayUri(opts: {
recipient: string; // 收款地址
amount: string; // 人类可读小数: "49.00" —— 不是原子量
splToken?: string | null; // SPL mint; 省略 = 原生 SOL
label?: string; // 显示在钱包确认界面
message?: string; // 比如你的订单号
}): string {
const p = new URLSearchParams();
p.set("amount", opts.amount);
if (opts.splToken) p.set("spl-token", opts.splToken);
if (opts.label) p.set("label", opts.label);
if (opts.message) p.set("message", opts.message);
return `solana:${opts.recipient}?${p.toString()}`;
}把它渲染成二维码,主流 Solana 钱包(Phantom 打头)原生就能扫。省略 spl-token,钱包转原生 SOL;带上一个 mint(比如 USDC 的),钱包会自己处理代币转账,连关联代币账户(ATA)都自动派生。
三个真会咬人的坑
这些不是纸上谈兵——每一个都是我们踩过、或者亲眼看别人踩进去的 bug 类别:
一、amount 是小数,不是原子量。规范写得很明白:用 uiAmountString,不是原始代币数量。USDC 有 6 位精度——想编码 49.00 却写成了 49000000,钱包就会礼貌地请你的顾客确认一笔四千九百万美元的付款。真正危险的地方在于:EVM 那边的对应标准 EIP-681,恰恰完全相反——它要的是原子量(wei)。如果你的收银台两个生态都支持,这是全项目最容易写反的一处。
二、裸地址二维码是一个架构级的自相矛盾。偷懒的集成只编码地址,让顾客自己填金额。可只要你的订单模型要求精确金额——任何严肃的支付系统都要求——顾客手填差一分钱,订单就永远卡在待支付,钱躺在地址里动不了。一个对金额零容忍的模型,配一个不带金额的二维码,等于设计了一台客服工单制造机。永远编码完整 URI。
三、label 和 message 不是装饰。它们是顾客在钱包确认界面上看到的东西。一笔来自陌生地址、没有任何标识的付款请求,看起来就像钓鱼;带着商户名和订单号的,看起来才像结账。label 放店名,message 放订单号——顺便也让顾客自己的账单历史变得可读。
教程都不写的部分:扫码之后
诚实地把工程量摊开。URI 是最容易的那 10%。现在顾客把 49 USDC 转到了你的地址上——你的活才刚开始:
- 检测。得有东西盯着链、注意到这笔转账——最好是入块的那一瞬间,因为顾客正盯着转圈的加载图标。这意味着你要跑(或租)可靠的节点基础设施。
- 金额匹配。48.99 收不收?分两笔付的算不算?报价过期之后才到的钱怎么办?
- 地址架构。所有订单共用一个地址,就得靠金额猜测把付款对到订单上——脆弱又难看。一单一个专属地址,对账天生一比一,但地址派生的规模化管理就落到你头上了。
- 结算与分账。只要有任何需要拆分的东西——平台费、合作方佣金——你就是在写链上结算逻辑。
- 通知你的后端。带签名的 webhook、重试、幂等——这些无聊的管道,决定你会不会给一笔订单发两次货。
这些没有一样是做不出来的。但它们全都是和你的产品毫无关系的通用基础设施。
捷径:一次 API 调用,二维码已含
这一层正是 Finality 卖的东西,所以把话说直白。一次调用:
curl -X POST https://finality.money/v1/intents \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"chain": "solana",
"token": "USDC",
"amount": "49.00",
"merchant_order_id": "ORD-3141"
}'返回一个专属收款地址和一个托管收银台,正确的 Solana Pay URI(小数金额、对的 mint、你的标识)已经渲染好——顾客要是选了 EVM 链,同一个收银台自动换成 EIP-681 二维码,因为它本来就覆盖五条链。然后是那些你不想自己造的部分:
- 自营全节点检测——入块后亚秒级,页面翻成"已支付"
- 一单一个专属地址——对账天生一比一;从币安、Coinbase 提现的普通转账就是一次有效付款,不需要钱包 App
- 非托管原子结算——固定 1% 在链上切出,其余在同一个流程里落进你的钱包;没有平台余额,没有打款环节
payment.settledwebhook——带签名、自动重试、可重放;只在钱真正到你钱包之后才触发- 部分支付、迟到支付都有明确处理和展示,不会静默丢失
而且因为金库和结算规则都活在链上,这里存在两样自己集成(或任何别家网关)给不了的东西:Solana 上真正自动扣款的订阅——计费条款焊死在顾客自控金库的地址里,不发续费邮件(架构)——以及在顾客付款交易内部直接切给分销商钱包的佣金。
FAQ
哪些钱包支持 Solana Pay 二维码?
主流 Solana 钱包基本全支持,Phantom 排第一。这个 URI 方案在手机上也可以作为深链直接点开。
顾客压根没有钱包怎么办?
一单一个专属地址意味着:交易所提现就是一次付款——顾客从币安/Coinbase 账户像普通转账一样发出来就行。在 Finality 这是开箱即用的,扫不扫码都一样。
能不花真钱测试吗?
能——用 sk_test_ 密钥建一笔沙盒付款,点"模拟支付",完整排练检测-结算-webhook 全流程,二维码也包括在内。
一次集成能顺便覆盖 EVM 链吗?
在 Finality 可以——收银台在 Solana 上吐 Solana Pay URI,在 Ethereum、Base、BNB Chain 上吐 EIP-681 URI,小数与原子量的相反约定已经替你处理好。这半句话的价值比听起来大,见第一个坑。
一段话说清 Solana Pay 是什么?
一个 URI 方案:目的地、金额、代币、label、message——编码成钱包能扫描、预填、一个动作发出的形态。它是金额进码规则的正式文法;付款本身是一笔普通的 Solana 转账。
Solana Pay 二维码怎么生成?
把 URI 编好、渲染成二维码——或者跳过手工组装:每笔 Finality 付款自带二维码,地址、金额、你的店名标签都已编入,坑也提前填了。
Solana Pay 只能收 SOL,还是也支持 USDC?
任何 SPL 代币,走 spl-token 参数——实践里的流通货币是 USDC。参数省略等于请求原生 SOL;收银台永远该写明白。
Solana Pay 付款多快确认?
它底下就是普通 Solana 转账——亚秒检测,实测。顾客手机还没放下,已支付状态已经翻过来了。
Solana Pay 交易有什么费用?
顾客付网络费——几厘钱,我们实测优先费中位数为零。商户侧是网关的份额;这里是固定 1%。
怎么验证一笔 Solana Pay 付款真的结算了?
不是信钱包的成功页——是盯链:把转账和专属地址、金额匹配上,然后对着签名的 payment.settled webhook 行动。检测才是这套集成的正活。
Solana Pay 能处理退款吗?
这个方案只负责请求付款;退款是你主动发起的普通转账——老规矩,注意交易所地址。把"退款时收集钱包地址"设计进流程。
Solana Pay 适合线下收银吗?
可以说是最合身的——柜台指南整个建在它上面:打印或屏显二维码、顾客的手机当终端、你的控制台一秒左右出确认。
Solana Pay 的 transaction request 模式是什么?
在静态转账 URI 之外,规范还支持一种服务器往返:钱包去取一笔待签交易——复杂流程里强大,收银台上过度。支付形状的集成很少需要;简单 URI 就罩得住整家店。
做 Solana Pay 检测要自己跑 RPC 节点吗?
玩具流量,公共 RPC 凑合能撑;真金流水,检测可靠性就是产品本身——我们为什么自营节点讲的就是这件事,用网关等于继承它的基础设施,而不是自建你的。
想练手就自己把 URI 拼一遍,想收款就直接跳到结果:建一笔沙盒订单,15 分钟扫出你的第一个 Solana Pay 二维码——检测、结算、webhook 都已接好,钱落在只有你能控制的钱包里。完整参考在文档和 API 测试台。