Documentation
¶
Overview ¶
Package wechat 为 payment 包提供微信支付 V3 实现。
功能特性 ¶
- Native 支付(扫码 code_url)
- JSAPI 预支付与前端调起参数
- 公众号 OAuth(snsapi_base)换取 openid
- 订单查询、退款、关单
- 支付与退款异步通知验签、解密与解析
- 微信支付公钥 / 平台证书两种身份验签方式
快速开始 ¶
验签方式须与商户平台「验证微信支付身份」当前启用项一致(二选一)。
微信支付公钥模式:
client, err := wechat.New(wechat.Config{
AppID: "wx_app_id",
OAuthAppSecret: "app_secret", // JSAPI OAuth 需要
MchID: "1600000000",
MerchantSerialNo: "merchant_api_cert_serial",
MerchantPrivateKey: merchantPrivateKeyPEM,
APIV3Key: "32-byte-api-v3-key................",
VerifyMode: wechat.VerifyModePublicKey, // 可选,有公钥材料时可推断
WechatPayPublicKey: wechatPayPublicKeyPEM,
WechatPayPublicKeyID: "PUB_KEY_ID_xxxxxxxx", // 保留 PUB_KEY_ID_ 前缀
})
平台证书自动模式(须显式 VerifyMode;服务可访问微信 API):
// 启动拉取全量有效平台证书;默认定时刷新(后台 goroutine,与 client 同生命周期、无法停止)。
// 短生命周期/测试请设 PlatformCertAutoRefresh=false。
// 回调未知 serial 会受控补拉:并发合流、冷却约 60s、miss 负向缓存约 5min。
autoRefresh := false // 测试示例
client, err := wechat.New(wechat.Config{
AppID: "wx_app_id",
MchID: "1600000000",
MerchantSerialNo: "merchant_api_cert_serial",
MerchantPrivateKey: merchantPrivateKeyPEM,
APIV3Key: "32-byte-api-v3-key................",
VerifyMode: wechat.VerifyModePlatformCertAuto,
PlatformCertAutoRefresh: &autoRefresh,
})
平台证书静态模式(离线或可手动更换的固定证书):
// 仅登记单张序列号;CERTIFICATE PEM 会与配置序列号交叉校验。
// 轮换窗口请改用自动模式。
client, err := wechat.New(wechat.Config{
AppID: "wx_app_id",
MchID: "1600000000",
MerchantSerialNo: "merchant_api_cert_serial",
MerchantPrivateKey: merchantPrivateKeyPEM,
APIV3Key: "32-byte-api-v3-key................",
VerifyMode: wechat.VerifyModePlatformCertStatic,
PlatformCert: platformCertPEM,
PlatformCertSerialNo: "platform_cert_serial",
})
验签模式 ¶
商户平台「验证微信支付身份」两种方式只能使用一种:
- 微信支付公钥:WechatPayPublicKey + WechatPayPublicKeyID
- 平台证书: - 自动:须 VerifyModePlatformCertAuto(拉取全量证书;回调 serial miss 会补拉一次) - 静态:PlatformCert + PlatformCertSerialNo
VerifyMode 为空时:公钥齐全 → public_key,静态证书齐全 → platform_cert_static; 材料皆空返回配置错误(fail-closed),不会静默自动拉证。
公钥与平台证书材料不可同时完整配置。只配置某一模式的一半字段会返回配置错误。 PlatformCertAutoRefresh 仅平台证书自动模式合法(默认开启定时刷新)。
错误分类 ¶
- payment.ErrInvalidConfig:缺字段、模式与材料不匹配、PEM/序列号校验失败、 自动拉证中可识别的配置/权限类失败等
- payment.ErrGateway:平台证书自动拉取的网络/网关类失败
自动拉证错误类型依赖 gopay 错误文本启发式分类;若对 ErrGateway 重试, 仍应排查 APIv3Key、商户平台证书权限与出网,而非仅当瞬时抖动。
回调 ¶
ParsePaymentNotification / ParseRefundNotification 会用已加载的公钥/平台证书验签, 再用 APIV3Key 解密 resource,并校验 appid / mchid。 自动模式下未知 Wechatpay-Serial 会补拉平台证书再验签(与 gopay 同步验签行为对齐): 并发请求合流为一次全量拉取,成功刷新后全局冷却约 60s; 刷新后仍无目标 serial 则负向缓存约 5min,降低伪造 serial 的出站放大。 序列号已存在仍失败则不再补拉。补拉失败时错误同时可 errors.Is 到 ErrInvalidSignature 与 ErrGateway/ErrInvalidConfig。 业务侧仍须在事务内核对订单、金额、状态并幂等入账;仅在业务成功后返回 SuccessResponse。 回调 URL 仍建议在网关层做 IP/频率限制。
注意事项 ¶
- 金额在 payment.Order 中使用分(int64)
- 证书/密钥内容以 PEM 字符串传入,由调用方负责从文件或密钥管理服务读取
- 平台证书自动模式在 New 时会请求微信证书接口,需保证出网可达
- 默认定时刷新 goroutine 无法停止;进程内反复 New 可能叠加刷新任务,生产宜单例
- 公钥 ID / 证书序列号须与商户平台展示一致(公钥 ID 勿删 PUB_KEY_ID_ 前缀)
Example (JsapiForOnepay) ¶
Example_jsapiForOnepay 演示一码付微信链路需要的 OAuth + JSAPI。
package main
import (
"fmt"
"github.com/f2xme/gox/payment"
)
func main() {
_ = payment.ProviderWechat
fmt.Println("1. url, err := client.OAuthURL(redirectURL, state)")
fmt.Println("2. openID, err := client.ExchangeOAuthCode(ctx, code)")
fmt.Println("3. jsapi, err := client.JSAPIPay(ctx, order, openID)")
}
Output: 1. url, err := client.OAuthURL(redirectURL, state) 2. openID, err := client.ExchangeOAuthCode(ctx, code) 3. jsapi, err := client.JSAPIPay(ctx, order, openID)
Example (NotifyHTTPHandler) ¶
Example_notifyHTTPHandler 演示把支付与退款回调接到标准库 HTTP。 支付侧可运行 handler 见 payment.Example_paymentNotifyHandler;退款侧同理注入 RefundNotifier。
package main
import (
"fmt"
)
func main() {
fmt.Println("POST /payment/wechat/notify → ParsePayment → ledger(success only) → SuccessResponse")
fmt.Println("POST /payment/wechat/refund-notify → ParseRefund → refund ledger → SuccessResponse")
}
Output: POST /payment/wechat/notify → ParsePayment → ledger(success only) → SuccessResponse POST /payment/wechat/refund-notify → ParseRefund → refund ledger → SuccessResponse
Example (PayAndNotify) ¶
Example_payAndNotify 演示 Native 下单与支付/退款回调步骤。 可运行的通用支付回调形态见 payment.Example_paymentNotifyHandler。
package main
import (
"fmt"
)
func main() {
fmt.Println("1. result, err := client.Pay(ctx, order) // PayURL = code_url")
fmt.Println("2. notify, err := client.ParsePaymentNotification(ctx, r)")
fmt.Println("3. // 仅 success:事务内核对 OrderID/Amount 并幂等入账")
fmt.Println("4. client.SuccessResponse().WriteTo(w) // JSON SUCCESS")
fmt.Println("5. refundNotify, err := client.ParseRefundNotification(ctx, r)")
fmt.Println("6. // 退款流水入账(非支付成功账)")
fmt.Println("7. client.SuccessResponse().WriteTo(w) // 与支付回调同一回执格式")
}
Output: 1. result, err := client.Pay(ctx, order) // PayURL = code_url 2. notify, err := client.ParsePaymentNotification(ctx, r) 3. // 仅 success:事务内核对 OrderID/Amount 并幂等入账 4. client.SuccessResponse().WriteTo(w) // JSON SUCCESS 5. refundNotify, err := client.ParseRefundNotification(ctx, r) 6. // 退款流水入账(非支付成功账) 7. client.SuccessResponse().WriteTo(w) // 与支付回调同一回执格式
Index ¶
- type Config
- type Option
- type VerifyMode
- type WechatPay
- func (w *WechatPay) Close(ctx context.Context, orderID string) error
- func (w *WechatPay) ExchangeOAuthCode(ctx context.Context, code string) (string, error)
- func (w *WechatPay) JSAPIPay(ctx context.Context, order *payment.Order, openID string) (*payment.JSAPIResult, error)
- func (w *WechatPay) OAuthURL(redirectURL, state string) (string, error)
- func (w *WechatPay) ParsePaymentNotification(ctx context.Context, req *http.Request) (*payment.PaymentNotification, error)
- func (w *WechatPay) ParseRefundNotification(ctx context.Context, req *http.Request) (*payment.RefundNotification, error)
- func (w *WechatPay) Pay(ctx context.Context, order *payment.Order) (*payment.PaymentResult, error)
- func (w *WechatPay) Query(ctx context.Context, orderID string) (*payment.QueryResult, error)
- func (w *WechatPay) Refund(ctx context.Context, req *payment.RefundRequest) (*payment.RefundResult, error)
- func (w *WechatPay) SuccessResponse() payment.NotifyResponse
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// AppID 是公众号或应用 ID。
AppID string
// OAuthAppSecret 是公众号网页授权密钥。
OAuthAppSecret string
// MchID 是微信支付商户号。
MchID string
// MerchantSerialNo 是商户 API 证书序列号。
MerchantSerialNo string
// MerchantPrivateKey 是商户 API 私钥 PEM 内容。
MerchantPrivateKey string
// APIV3Key 是 32 字节 API v3 密钥。
APIV3Key string
// VerifyMode 指定验签方式。
// 为空时:公钥齐全 → public_key,静态证书齐全 → platform_cert_static;
// 材料皆空须显式 platform_cert_auto,否则配置错误。
// 合法值:public_key、platform_cert_static、platform_cert_auto。
VerifyMode VerifyMode
// WechatPayPublicKey 是微信支付公钥 PEM 内容(公钥模式)。
// 与平台证书模式二选一。
WechatPayPublicKey string
// WechatPayPublicKeyID 是微信支付公钥 ID(公钥模式,含 PUB_KEY_ID_ 前缀时请保留)。
WechatPayPublicKeyID string
// PlatformCert 是微信平台证书 PEM 内容(平台证书静态模式)。
// 与 PlatformCertSerialNo 配套。若 PEM 为 CERTIFICATE,初始化时会与序列号交叉校验。
// 轮换期间微信可能并存两张有效证书;静态模式只登记单序列号,请优先自动模式。
PlatformCert string
// PlatformCertSerialNo 是微信平台证书序列号(平台证书静态模式)。
// 与商户平台展示一致:十六进制大写、可带或不带冒号,比较前会规范化。
PlatformCertSerialNo string
// PlatformCertAutoRefresh 控制平台证书自动模式是否定时刷新。
// 仅平台证书自动模式生效;nil 或 true 为默认开启,false 关闭。
// 开启时 gopay 会启动后台 goroutine(约 12h),与 client 同生命周期且无法停止;
// 短生命周期/测试场景请设为 false。在公钥或静态模式下设置会返回配置错误。
PlatformCertAutoRefresh *bool
}
Config 定义微信支付 V3 配置。
支持两种「验证微信支付身份」方式(二选一,与商户平台一致):
- 微信支付公钥:WechatPayPublicKey + WechatPayPublicKeyID
- 平台证书: - 静态:PlatformCert + PlatformCertSerialNo(离线/固定证书,单序列号) - 自动:VerifyMode=platform_cert_auto,启动时 API 拉取并默认每 12 小时刷新
VerifyMode 为空时仅按材料推断公钥或静态证书模式;材料皆空不会静默进入自动拉证, 须显式设置 VerifyModePlatformCertAuto(fail-closed,与支付宝 adapter 一致)。
func (Config) ResolveVerifyMode ¶ added in v0.2.0
func (c Config) ResolveVerifyMode() VerifyMode
ResolveVerifyMode 返回最终生效的验签模式(含空 VerifyMode 时的字段推断)。 非法或材料不足无法推断时返回空字符串;经 New 创建时 validateConfig 会拒绝。
type Option ¶
type Option func(*options)
Option 定义微信适配器选项。
func WithHTTPTransport ¶
func WithHTTPTransport(transport http.RoundTripper) Option
WithHTTPTransport 设置 HTTP transport。
type VerifyMode ¶ added in v0.2.0
type VerifyMode string
VerifyMode 指定商户平台「验证微信支付身份」所用验签方式。
与商户平台两种方式对齐(只能启用一种);平台证书再细分为静态与自动拉取。
const ( // VerifyModePublicKey 使用微信支付公钥验签。 // 须配置 WechatPayPublicKey + WechatPayPublicKeyID。 VerifyModePublicKey VerifyMode = "public_key" // VerifyModePlatformCertStatic 使用配置中的平台证书 PEM 验签。 // 须配置 PlatformCert + PlatformCertSerialNo。 // 仅登记单张证书序列号;证书轮换窗口请优先自动模式。 VerifyModePlatformCertStatic VerifyMode = "platform_cert_static" // VerifyModePlatformCertAuto 启动时通过 API 拉取平台证书并(默认定时)刷新。 // 须显式设置;不配置公钥与静态证书字段。 VerifyModePlatformCertAuto VerifyMode = "platform_cert_auto" )
type WechatPay ¶
type WechatPay struct {
// contains filtered or unexported fields
}
WechatPay 实现微信 Native、JSAPI、查询、退款、关单和回调处理。
func New ¶
New 创建微信支付适配器。
配置须与商户平台「验证微信支付身份」当前启用方式一致:
- 公钥:WechatPayPublicKey + WechatPayPublicKeyID(可省略 VerifyMode)
- 平台证书静态:PlatformCert + PlatformCertSerialNo(可省略 VerifyMode)
- 平台证书自动:须显式 VerifyModePlatformCertAuto(材料皆空时不会静默自动拉证)
平台证书自动模式在初始化时会请求微信证书接口;默认定时刷新会启动后台 goroutine。 网络类失败多为 payment.ErrGateway,PEM/参数及部分配置类失败为 payment.ErrInvalidConfig。 详见包文档与 Config 字段说明。
Example ¶
ExampleNew 演示微信支付公钥验签模式构造(凭证请替换为真实材料)。 本示例不连接网关,仅展示推荐配置形态。
package main
import (
"fmt"
"github.com/f2xme/gox/payment/adapter/wechat"
)
func main() {
_ = wechat.VerifyModePublicKey
fmt.Println("wechat.New(wechat.Config{")
fmt.Println(" AppID, MchID, MerchantSerialNo, MerchantPrivateKey, APIV3Key,")
fmt.Println(" WechatPayPublicKey, WechatPayPublicKeyID,")
fmt.Println("})")
}
Output: wechat.New(wechat.Config{ AppID, MchID, MerchantSerialNo, MerchantPrivateKey, APIV3Key, WechatPayPublicKey, WechatPayPublicKeyID, })
Example (PlatformCertAuto) ¶
ExampleNew_platformCertAuto 演示平台证书自动拉证模式。
package main
import (
"fmt"
"github.com/f2xme/gox/payment/adapter/wechat"
)
func main() {
_ = wechat.VerifyModePlatformCertAuto
fmt.Println("VerifyMode: wechat.VerifyModePlatformCertAuto")
fmt.Println("New 时会请求微信证书接口;生产建议单例")
}
Output: VerifyMode: wechat.VerifyModePlatformCertAuto New 时会请求微信证书接口;生产建议单例
func (*WechatPay) ExchangeOAuthCode ¶
ExchangeOAuthCode 使用网页授权 code 换取 openid。
func (*WechatPay) JSAPIPay ¶
func (w *WechatPay) JSAPIPay(ctx context.Context, order *payment.Order, openID string) (*payment.JSAPIResult, error)
JSAPIPay 创建 JSAPI 预支付单及前端调起参数。
func (*WechatPay) ParsePaymentNotification ¶
func (w *WechatPay) ParsePaymentNotification(ctx context.Context, req *http.Request) (*payment.PaymentNotification, error)
ParsePaymentNotification 验签、解密并解析微信支付通知。
func (*WechatPay) ParseRefundNotification ¶
func (w *WechatPay) ParseRefundNotification(ctx context.Context, req *http.Request) (*payment.RefundNotification, error)
ParseRefundNotification 验签、解密并解析微信退款通知。
func (*WechatPay) Refund ¶
func (w *WechatPay) Refund(ctx context.Context, req *payment.RefundRequest) (*payment.RefundResult, error)
Refund 发起微信退款。
func (*WechatPay) SuccessResponse ¶
func (w *WechatPay) SuccessResponse() payment.NotifyResponse
SuccessResponse 返回微信要求的成功回执。