| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- 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(util.GetCurrTS(), 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
- }
- // 签名类型
- if p.SignType == "" {
- p.SignType = util.SignTypeMD5
- }
- 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 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
- }
|