Go 语言中 Apple Pay 实际指 App Store 内购(IAP),需用 apple.NewClient 初始化:必填 issuer_id、bundle_id、key_id、private_key_content 四个字符串,isProd 控制沙箱/生产环境;验票须匹配 url(UrlSandbox/UrlProd)与 receipt 环境,status=0 才成功;Server Notifications v2 需用 DecodeSignedPayload 解析 JWT,依赖 client 初始化参数校验签名。

Go 语言里没有原生 Apple Pay(指 Apple Wallet 的 NFC 支付),你真正要集成的是 Apple App Store 内购(IAP)——也就是 in-app purchase,用的是 Apple 的 verifyReceipt 接口和 Server Notifications。别被“Apple Pay”这个词带偏了。
怎么初始化 apple.Client?参数从哪来、哪些必填、哪些容易错
go-pay/gopay 的 apple.NewClient 是入口,但它不处理前端支付,只负责后端验票和解析通知。四个字符串参数缺一不可:
-
issuer_id:在 App Store Connect → “Keys” 页面创建 API Key 时生成的 UUID,不是 Team ID -
bundle_id:必须和 Xcode 工程里Bundle Identifier完全一致,大小写敏感,不能带空格或斜杠 -
key_id:同一 Keys 页面里,对应密钥的 10 位字母数字 ID(如2X9R4HXF34),不是文件名 -
private_key_content:从 Apple 下载的.p8文件内容(含-----BEGIN PRIVATE KEY-----头尾),不是路径,是字符串;常见错误是直接传文件路径或漏读换行符 -
isProd:开发测试务必设为false,否则会连生产地址,沙箱 receipt 永远验证失败
VerifyReceipt 怎么调、为什么总返回 Status != 0
这是最常卡住的环节。Apple 的验票接口返回 Status 字段,0 才代表成功,其他值全是错误(比如 21002 是 receipt 格式非法,21007 是沙箱 receipt 误发到生产地址)。关键点:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 验票地址必须和环境严格匹配:
apple.UrlSandbox对应沙箱 receipt,apple.UrlProd对应线上 receipt;不能靠 guess,也不能硬编码成一个 URL -
shared_secret参数仅对auto-renewable subscription必填,填错或遗漏会导致21004;一次性商品可传空字符串"" - receipt 数据必须是原始 base64 字符串(iOS 侧用
transaction.transactionReceipt或appStoreReceiptURL读取后直接 base64 编码),不能先 JSON decode 再传,也不能去掉换行符 - 推荐加 context 超时:
apple.VerifyReceipt(ctx, url, pwd, receipt),避免阻塞
如何安全解析 Server Notifications(v2)?DecodeSignedPayload 的坑在哪
Apple 的服务器通知(App Store Server Notifications)是 JWT 签名体,不是普通 JSON。gopay 的 apple.DecodeSignedPayload 会自动校验签名并解出 payload,但前提是你的 apple.Client 初始化时用了正确的 issuer_id 和 private_key_content ——它内部要用这些生成 JWT 验证所需的 public key。
立即学习“go语言免费学习笔记(深入)”;
- 通知体是 raw HTTP body(无 key 包裹),直接传给
DecodeSignedPayload即可,不要先json.Unmarshal - 解出 payload 后,用
payload.DecodeTransactionInfo()或payload.DecodeSubscriptionRenewalInfo()提取业务数据;注意字段命名和官方文档不完全一致(比如originalTransactionId在结构体里是OriginalTransactionId) - 通知可能重复投递,
notificationType和subType必须校验(如INITIAL_BUY/RENEWAL/EXPIRED),且需用transactionId做幂等去重
真正的难点不在代码几行,而在于 Apple 后台配置、证书生命周期管理、沙箱测试账号绑定、以及通知签名验证失败时几乎不报具体错误原因。建议把 client_test.go 里的测试用例当参考文档用,比官方文档更贴近真实返回结构。

















