You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
4.6 KiB
107 lines
4.6 KiB
package wechat
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
)
|
|
|
|
type (
|
|
// 预支付响应参数(返回给Flutter客户端)
|
|
AppPayParams struct {
|
|
AppID string `json:"appid"` // 应用ID
|
|
PartnerID string `json:"partnerid"` // 商户号
|
|
PrepayID string `json:"prepayid"` // 预支付ID
|
|
Package string `json:"package"` // 固定值 "Sign=WXPay"
|
|
NonceStr string `json:"noncestr"` // 随机字符串
|
|
TimeStamp string `json:"timestamp"` // 时间戳(秒)
|
|
Sign string `json:"sign"` // 支付签名
|
|
}
|
|
// WxPayTransaction 解密后的核心交易数据(Ciphertext 解密后得到的结构)
|
|
WxPayTransaction struct {
|
|
// 3.1 订单基础信息
|
|
OutTradeNo string `json:"out_trade_no"` // 商户订单号(你下单时传入的 order_no)
|
|
TransactionId string `json:"transaction_id"` // 微信支付订单号(微信生成的唯一订单ID)
|
|
Mchid string `json:"mchid"` // 商户号(你的商户ID,如 1652202222)
|
|
Appid string `json:"appid"` // 应用ID(你的 AppID,如 wx4b5ec8957ad5e425)
|
|
TradeType string `json:"trade_type"` // 交易类型(App 支付固定为 "APP")
|
|
TradeState string `json:"trade_state"` // 支付状态(核心字段)
|
|
TradeStateDesc string `json:"trade_state_desc"` // 支付状态描述(如 "支付成功")
|
|
|
|
// 3.2 金额信息
|
|
Amount struct {
|
|
Total int64 `json:"total"` // 订单总金额(单位:分,如 100 表示 1 元)
|
|
PayerTotal int64 `json:"payer_total"` // 支付金额(用户实际支付的金额,通常与 total 一致)
|
|
Currency string `json:"currency"` // 货币类型(默认 "CNY",人民币)
|
|
PayerCurrency string `json:"payer_currency"` // 用户支付的货币类型(默认 "CNY")
|
|
} `json:"amount"`
|
|
|
|
// 3.3 支付者信息
|
|
Payer struct {
|
|
Openid string `json:"openid"` // 用户唯一标识(App 支付可不返回,JSAPI 支付必返)
|
|
} `json:"payer"`
|
|
|
|
// 3.4 时间信息
|
|
SuccessTime string `json:"success_time"` // 支付成功时间(格式:yyyy-MM-dd'T'HH:mm:ss+TIMEZONE,仅支付成功时返回)
|
|
CreateTime string `json:"create_time"` // 订单创建时间(格式同上)
|
|
CloseTime string `json:"close_time"` // 订单关闭时间(仅订单关闭时返回,格式同上)
|
|
|
|
// 3.5 附加信息
|
|
Attach string `json:"attach"` // 附加数据(你下单时传入的 attach 字段,如 "test_attach")
|
|
NotifyUrl string `json:"notify_url"` // 回调通知地址(你下单时传入的 notify_url)
|
|
NotifyTime string `json:"notify_time"` // 回调通知时间(格式同上)
|
|
NotifyId string `json:"notify_id"` // 回调通知ID(微信生成的唯一标识)
|
|
}
|
|
|
|
ISys interface {
|
|
GenerateOrderNo(prefix string) string
|
|
CreateAppOrder(ctx context.Context, outTradeNo string, totalFee int64, description string, notifyurl string) (result *AppPayParams, err error)
|
|
DecryptWxPayNotify(associatedData, nonce, ciphertext string) (*WxPayTransaction, error)
|
|
}
|
|
)
|
|
|
|
var defsys ISys
|
|
|
|
// ErrNotInited 微信支付未初始化。OnInit 失败时不再由库 Fatal,调用方可选择降级;
|
|
// 此时包级函数返回本错误,而不是 panic。
|
|
var ErrNotInited = errors.New("wechatpay 未初始化:配置缺失或商户私钥加载失败")
|
|
|
|
func OnInit(config map[string]interface{}, option ...Option) error {
|
|
sys, err := newSys(newOptions(config, option...))
|
|
if err != nil {
|
|
// 关键:出错时**不要**给 defsys 赋值。
|
|
// `defsys, err = newSys(...)` 会把 (*WeChat)(nil) 装进 ISys 接口,得到「非 nil 接口 + nil 指针」,
|
|
// 于是 defsys == nil 判断失效、后续调用直接 panic。保持 defsys 为 nil 才能被守卫拦住。
|
|
return err
|
|
}
|
|
defsys = sys
|
|
return nil
|
|
}
|
|
|
|
func NewSys(option ...Option) (ISys, error) {
|
|
sys, err := newSys(newOptionsByOption(option...))
|
|
if err != nil {
|
|
return nil, err // 同上:不返回装着 nil 指针的非 nil 接口
|
|
}
|
|
return sys, nil
|
|
}
|
|
|
|
func GenerateOrderNo(prefix string) string {
|
|
if defsys == nil {
|
|
return ""
|
|
}
|
|
return defsys.GenerateOrderNo(prefix)
|
|
}
|
|
|
|
func CreateAppOrder(ctx context.Context, outTradeNo string, totalFee int64, description string, notifyurl string) (result *AppPayParams, err error) {
|
|
if defsys == nil {
|
|
return nil, ErrNotInited
|
|
}
|
|
return defsys.CreateAppOrder(ctx, outTradeNo, totalFee, description, notifyurl)
|
|
}
|
|
|
|
func DecryptWxPayNotify(associatedData, nonce, ciphertext string) (*WxPayTransaction, error) {
|
|
if defsys == nil {
|
|
return nil, ErrNotInited
|
|
}
|
|
return defsys.DecryptWxPayNotify(associatedData, nonce, ciphertext)
|
|
}
|
|
|