1
0
mirror of https://github.com/silenceper/wechat.git synced 2026-02-04 12:52:27 +08:00
Files
wechat/pay/order/pay.go
houseme 96c1f98944 [feature] Format the code and improve Mini Program authorization to o… (#473)
* [feature] Format the code and improve Mini Program authorization to obtain openid(miniprogram/auth/auth.go Code2Session)

* [feature] CheckEncryptedData (https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/user-info/auth.checkEncryptedData.html)

* upgrade json error

* upgrade json error

Co-authored-by: houseme <houseme@outlook.com>
2021-09-08 11:03:23 +08:00

287 lines
8.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package order
import (
"encoding/xml"
"errors"
"strconv"
"strings"
"time"
"github.com/silenceper/wechat/v2/pay/config"
"github.com/silenceper/wechat/v2/util"
)
// https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
var payGateway = "https://api.mch.weixin.qq.com/pay/unifiedorder"
// SUCCESS 表示支付成功
const SUCCESS = "SUCCESS"
// Order struct extends context
type Order struct {
*config.Config
}
// NewOrder return an instance of order package
func NewOrder(cfg *config.Config) *Order {
order := Order{cfg}
return &order
}
// Params was NEEDED when request Unified order
// 传入的参数,用于生成 prepay_id 的必需参数
type Params struct {
TotalFee string
CreateIP string
Body string
OutTradeNo string
TimeExpire string // 订单失效时间格式为yyyyMMddHHmmss如2009年12月27日9点10分10秒表示为20091227091010。
OpenID string
TradeType string
SignType string
Detail string
Attach string
GoodsTag string
NotifyURL string
}
// Config 是传出用于 js sdk 用的参数
type Config struct {
Timestamp string `json:"timestamp"`
NonceStr string `json:"nonceStr"`
PrePayID string `json:"prePayId"`
SignType string `json:"signType"`
Package string `json:"package"`
PaySign string `json:"paySign"`
}
// ConfigForApp 是传出用于 app sdk 用的参数
type ConfigForApp struct {
AppID string `json:"appid"`
MchID string `json:"partnerid"` // 微信支付分配的商户号
PrePayID string `json:"prepayid"`
Package string `json:"package"`
NonceStr string `json:"nonceStr"`
Timestamp string `json:"timestamp"`
Sign string `json:"sign"`
}
// PreOrder 是 Unified order 接口的返回
type PreOrder struct {
ReturnCode string `xml:"return_code"`
ReturnMsg string `xml:"return_msg"`
AppID string `xml:"appid,omitempty"`
MchID string `xml:"mch_id,omitempty"`
NonceStr string `xml:"nonce_str,omitempty"`
Sign string `xml:"sign,omitempty"`
ResultCode string `xml:"result_code,omitempty"`
TradeType string `xml:"trade_type,omitempty"`
PrePayID string `xml:"prepay_id,omitempty"`
CodeURL string `xml:"code_url,omitempty"`
MWebURL string `xml:"mweb_url,omitempty"`
ErrCode string `xml:"err_code,omitempty"`
ErrCodeDes string `xml:"err_code_des,omitempty"`
}
// payRequest 接口请求参数
type payRequest struct {
AppID string `xml:"appid"` // 公众账号ID
MchID string `xml:"mch_id"` // 商户号
DeviceInfo string `xml:"device_info,omitempty"` // 设备号
NonceStr string `xml:"nonce_str"` // 随机字符串
Sign string `xml:"sign"` // 签名
SignType string `xml:"sign_type,omitempty"` // 签名类型
Body string `xml:"body"` // 商品描述
Detail string `xml:"detail,omitempty"` // 商品详情
Attach string `xml:"attach,omitempty"` // 附加数据
OutTradeNo string `xml:"out_trade_no"` // 商户订单号
FeeType string `xml:"fee_type,omitempty"` // 标价币种
TotalFee string `xml:"total_fee"` // 标价金额
SpbillCreateIP string `xml:"spbill_create_ip"` // 终端IP
TimeStart string `xml:"time_start,omitempty"` // 交易起始时间
TimeExpire string `xml:"time_expire,omitempty"` // 交易结束时间
GoodsTag string `xml:"goods_tag,omitempty"` // 订单优惠标记
NotifyURL string `xml:"notify_url"` // 通知地址
TradeType string `xml:"trade_type"` // 交易类型
ProductID string `xml:"product_id,omitempty"` // 商品ID
LimitPay string `xml:"limit_pay,omitempty"` // 指定支付方式
OpenID string `xml:"openid,omitempty"` // 用户标识
SceneInfo string `xml:"scene_info,omitempty"` // 场景信息
XMLName struct{} `xml:"xml"`
}
func (req *payRequest) BridgePayRequest(p *Params, AppID, MchID, nonceStr, sign string) *payRequest {
request := payRequest{
AppID: AppID,
MchID: MchID,
NonceStr: nonceStr,
Sign: sign,
Body: p.Body,
OutTradeNo: p.OutTradeNo,
TotalFee: p.TotalFee,
SpbillCreateIP: p.CreateIP,
NotifyURL: p.NotifyURL,
TradeType: p.TradeType,
OpenID: p.OpenID,
SignType: p.SignType,
Detail: p.Detail,
Attach: p.Attach,
GoodsTag: p.GoodsTag,
}
return &request
}
// BridgeConfig get js bridge config
func (o *Order) BridgeConfig(p *Params) (cfg Config, err error) {
var (
buffer strings.Builder
timestamp = strconv.FormatInt(time.Now().Unix(), 10)
)
order, err := o.PrePayOrder(p)
if err != nil {
return
}
buffer.WriteString("appId=")
buffer.WriteString(order.AppID)
buffer.WriteString("&nonceStr=")
buffer.WriteString(order.NonceStr)
buffer.WriteString("&package=")
buffer.WriteString("prepay_id=" + order.PrePayID)
buffer.WriteString("&signType=")
buffer.WriteString(p.SignType)
buffer.WriteString("&timeStamp=")
buffer.WriteString(timestamp)
buffer.WriteString("&key=")
buffer.WriteString(o.Key)
sign, err := util.CalculateSign(buffer.String(), p.SignType, o.Key)
if err != nil {
return
}
// 签名
cfg.PaySign = sign
cfg.NonceStr = order.NonceStr
cfg.Timestamp = timestamp
cfg.PrePayID = order.PrePayID
cfg.SignType = p.SignType
cfg.Package = "prepay_id=" + order.PrePayID
return
}
// BridgeAppConfig get app bridge config
func (o *Order) BridgeAppConfig(p *Params) (cfg ConfigForApp, err error) {
var (
timestamp = strconv.FormatInt(time.Now().Unix(), 10)
noncestr = util.RandomStr(32)
_package = "Sign=WXPay"
)
order, err := o.PrePayOrder(p)
if err != nil {
return
}
result := map[string]string{
"appid": order.AppID,
"partnerid": order.MchID,
"prepayid": order.PrePayID,
"package": _package,
"noncestr": noncestr,
"timestamp": timestamp,
}
// 签名
sign, err := util.ParamSign(result, o.Key)
if err != nil {
return
}
result["sign"] = sign
cfg = ConfigForApp{
AppID: result["appid"],
MchID: result["partnerid"],
PrePayID: result["prepayid"],
Package: result["package"],
NonceStr: result["noncestr"],
Timestamp: result["timestamp"],
Sign: result["sign"],
}
return
}
// PrePayOrder return data for invoke wechat payment
func (o *Order) PrePayOrder(p *Params) (payOrder PreOrder, err error) {
nonceStr := util.RandomStr(32)
// 通知地址
if len(p.NotifyURL) == 0 {
p.NotifyURL = o.NotifyURL // 默认使用order.NotifyURL
}
param := map[string]string{
"appid": o.AppID,
"body": p.Body,
"mch_id": o.MchID,
"nonce_str": nonceStr,
"out_trade_no": p.OutTradeNo,
"spbill_create_ip": p.CreateIP,
"total_fee": p.TotalFee,
"trade_type": p.TradeType,
"openid": p.OpenID,
"sign_type": p.SignType,
"detail": p.Detail,
"attach": p.Attach,
"goods_tag": p.GoodsTag,
"notify_url": p.NotifyURL,
}
// 签名类型
if param["sign_type"] == "" {
param["sign_type"] = util.SignTypeMD5
}
if p.TimeExpire != "" {
// 如果有传入交易结束时间
param["time_expire"] = p.TimeExpire
}
sign, err := util.ParamSign(param, o.Key)
if err != nil {
return
}
request := new(payRequest).BridgePayRequest(p, o.AppID, o.MchID, nonceStr, sign)
if len(p.TimeExpire) > 0 {
// 如果有传入交易结束时间
request.TimeExpire = p.TimeExpire
}
rawRet, err := util.PostXML(payGateway, request)
if err != nil {
return
}
err = xml.Unmarshal(rawRet, &payOrder)
if err != nil {
return
}
if payOrder.ReturnCode == SUCCESS {
// pay success
if payOrder.ResultCode == SUCCESS {
err = nil
return
}
err = errors.New(payOrder.ErrCode + payOrder.ErrCodeDes)
return
}
err = errors.New("[msg : xmlUnmarshalError] [rawReturn : " + string(rawRet) + "] [sign : " + sign + "]")
return
}
// PrePayID will request wechat merchant api and request for a pre payment order id
func (o *Order) PrePayID(p *Params) (prePayID string, err error) {
order, err := o.PrePayOrder(p)
if err != nil {
return
}
if order.PrePayID == "" {
err = errors.New("empty prepayid")
}
prePayID = order.PrePayID
return
}