pay.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. package order
  2. import (
  3. "encoding/xml"
  4. "errors"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "github.com/silenceper/wechat/v2/pay/config"
  9. "github.com/silenceper/wechat/v2/util"
  10. )
  11. // https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
  12. var payGateway = "https://api.mch.weixin.qq.com/pay/unifiedorder"
  13. // SUCCESS 表示支付成功
  14. const SUCCESS = "SUCCESS"
  15. // Order struct extends context
  16. type Order struct {
  17. *config.Config
  18. }
  19. // NewOrder return an instance of order package
  20. func NewOrder(cfg *config.Config) *Order {
  21. order := Order{cfg}
  22. return &order
  23. }
  24. // Params was NEEDED when request Unified order
  25. // 传入的参数,用于生成 prepay_id 的必需参数
  26. type Params struct {
  27. TotalFee string
  28. CreateIP string
  29. Body string
  30. OutTradeNo string
  31. TimeExpire string // 订单失效时间,格式为 yyyyMMddHHmmss,如 2009 年 12 月 27 日 9 点 10 分 10 秒表示为 20091227091010。
  32. OpenID string
  33. TradeType string
  34. SignType string
  35. Detail string
  36. Attach string
  37. GoodsTag string
  38. NotifyURL string
  39. }
  40. // Config 是传出用于 js sdk 用的参数
  41. type Config struct {
  42. Timestamp string `json:"timestamp"`
  43. NonceStr string `json:"nonceStr"`
  44. PrePayID string `json:"prePayId"`
  45. SignType string `json:"signType"`
  46. Package string `json:"package"`
  47. PaySign string `json:"paySign"`
  48. }
  49. // ConfigForApp 是传出用于 app sdk 用的参数
  50. type ConfigForApp struct {
  51. AppID string `json:"appid"`
  52. MchID string `json:"partnerid"` // 微信支付分配的商户号
  53. PrePayID string `json:"prepayid"`
  54. Package string `json:"package"`
  55. NonceStr string `json:"nonceStr"`
  56. Timestamp string `json:"timestamp"`
  57. Sign string `json:"sign"`
  58. }
  59. // PreOrder 是 Unified order 接口的返回
  60. type PreOrder struct {
  61. ReturnCode string `xml:"return_code"`
  62. ReturnMsg string `xml:"return_msg"`
  63. AppID string `xml:"appid,omitempty"`
  64. MchID string `xml:"mch_id,omitempty"`
  65. NonceStr string `xml:"nonce_str,omitempty"`
  66. Sign string `xml:"sign,omitempty"`
  67. ResultCode string `xml:"result_code,omitempty"`
  68. TradeType string `xml:"trade_type,omitempty"`
  69. PrePayID string `xml:"prepay_id,omitempty"`
  70. CodeURL string `xml:"code_url,omitempty"`
  71. MWebURL string `xml:"mweb_url,omitempty"`
  72. ErrCode string `xml:"err_code,omitempty"`
  73. ErrCodeDes string `xml:"err_code_des,omitempty"`
  74. }
  75. // payRequest 接口请求参数
  76. type payRequest struct {
  77. AppID string `xml:"appid"` // 公众账号 ID
  78. MchID string `xml:"mch_id"` // 商户号
  79. DeviceInfo string `xml:"device_info,omitempty"` // 设备号
  80. NonceStr string `xml:"nonce_str"` // 随机字符串
  81. Sign string `xml:"sign"` // 签名
  82. SignType string `xml:"sign_type,omitempty"` // 签名类型
  83. Body string `xml:"body"` // 商品描述
  84. Detail string `xml:"detail,omitempty"` // 商品详情
  85. Attach string `xml:"attach,omitempty"` // 附加数据
  86. OutTradeNo string `xml:"out_trade_no"` // 商户订单号
  87. FeeType string `xml:"fee_type,omitempty"` // 标价币种
  88. TotalFee string `xml:"total_fee"` // 标价金额
  89. SpbillCreateIP string `xml:"spbill_create_ip"` // 终端 IP
  90. TimeStart string `xml:"time_start,omitempty"` // 交易起始时间
  91. TimeExpire string `xml:"time_expire,omitempty"` // 交易结束时间
  92. GoodsTag string `xml:"goods_tag,omitempty"` // 订单优惠标记
  93. NotifyURL string `xml:"notify_url"` // 通知地址
  94. TradeType string `xml:"trade_type"` // 交易类型
  95. ProductID string `xml:"product_id,omitempty"` // 商品 ID
  96. LimitPay string `xml:"limit_pay,omitempty"` // 指定支付方式
  97. OpenID string `xml:"openid,omitempty"` // 用户标识
  98. SceneInfo string `xml:"scene_info,omitempty"` // 场景信息
  99. XMLName struct{} `xml:"xml"`
  100. }
  101. func (req *payRequest) BridgePayRequest(p *Params, AppID, MchID, nonceStr, sign string) *payRequest {
  102. request := payRequest{
  103. AppID: AppID,
  104. MchID: MchID,
  105. NonceStr: nonceStr,
  106. Sign: sign,
  107. Body: p.Body,
  108. OutTradeNo: p.OutTradeNo,
  109. TotalFee: p.TotalFee,
  110. SpbillCreateIP: p.CreateIP,
  111. NotifyURL: p.NotifyURL,
  112. TradeType: p.TradeType,
  113. OpenID: p.OpenID,
  114. SignType: p.SignType,
  115. Detail: p.Detail,
  116. Attach: p.Attach,
  117. GoodsTag: p.GoodsTag,
  118. }
  119. return &request
  120. }
  121. // BridgeConfig get js bridge config
  122. func (o *Order) BridgeConfig(p *Params) (cfg Config, err error) {
  123. var (
  124. buffer strings.Builder
  125. timestamp = strconv.FormatInt(time.Now().Unix(), 10)
  126. )
  127. order, err := o.PrePayOrder(p)
  128. if err != nil {
  129. return
  130. }
  131. buffer.WriteString("appId=")
  132. buffer.WriteString(order.AppID)
  133. buffer.WriteString("&nonceStr=")
  134. buffer.WriteString(order.NonceStr)
  135. buffer.WriteString("&package=")
  136. buffer.WriteString("prepay_id=" + order.PrePayID)
  137. buffer.WriteString("&signType=")
  138. buffer.WriteString(p.SignType)
  139. buffer.WriteString("&timeStamp=")
  140. buffer.WriteString(timestamp)
  141. buffer.WriteString("&key=")
  142. buffer.WriteString(o.Key)
  143. sign, err := util.CalculateSign(buffer.String(), p.SignType, o.Key)
  144. if err != nil {
  145. return
  146. }
  147. // 签名
  148. cfg.PaySign = sign
  149. cfg.NonceStr = order.NonceStr
  150. cfg.Timestamp = timestamp
  151. cfg.PrePayID = order.PrePayID
  152. cfg.SignType = p.SignType
  153. cfg.Package = "prepay_id=" + order.PrePayID
  154. return
  155. }
  156. // BridgeAppConfig get app bridge config
  157. func (o *Order) BridgeAppConfig(p *Params) (cfg ConfigForApp, err error) {
  158. var (
  159. timestamp = strconv.FormatInt(util.GetCurrTS(), 10)
  160. noncestr = util.RandomStr(32)
  161. _package = "Sign=WXPay"
  162. )
  163. order, err := o.PrePayOrder(p)
  164. if err != nil {
  165. return
  166. }
  167. result := map[string]string{
  168. "appid": order.AppID,
  169. "partnerid": order.MchID,
  170. "prepayid": order.PrePayID,
  171. "package": _package,
  172. "noncestr": noncestr,
  173. "timestamp": timestamp,
  174. }
  175. // 签名
  176. sign, err := util.ParamSign(result, o.Key)
  177. if err != nil {
  178. return
  179. }
  180. result["sign"] = sign
  181. cfg = ConfigForApp{
  182. AppID: result["appid"],
  183. MchID: result["partnerid"],
  184. PrePayID: result["prepayid"],
  185. Package: result["package"],
  186. NonceStr: result["noncestr"],
  187. Timestamp: result["timestamp"],
  188. Sign: result["sign"],
  189. }
  190. return
  191. }
  192. // PrePayOrder return data for invoke wechat payment
  193. func (o *Order) PrePayOrder(p *Params) (payOrder PreOrder, err error) {
  194. nonceStr := util.RandomStr(32)
  195. // 通知地址
  196. if len(p.NotifyURL) == 0 {
  197. p.NotifyURL = o.NotifyURL // 默认使用 order.NotifyURL
  198. }
  199. // 签名类型
  200. if p.SignType == "" {
  201. p.SignType = util.SignTypeMD5
  202. }
  203. param := map[string]string{
  204. "appid": o.AppID,
  205. "body": p.Body,
  206. "mch_id": o.MchID,
  207. "nonce_str": nonceStr,
  208. "out_trade_no": p.OutTradeNo,
  209. "spbill_create_ip": p.CreateIP,
  210. "total_fee": p.TotalFee,
  211. "trade_type": p.TradeType,
  212. "openid": p.OpenID,
  213. "sign_type": p.SignType,
  214. "detail": p.Detail,
  215. "attach": p.Attach,
  216. "goods_tag": p.GoodsTag,
  217. "notify_url": p.NotifyURL,
  218. }
  219. if p.TimeExpire != "" {
  220. // 如果有传入交易结束时间
  221. param["time_expire"] = p.TimeExpire
  222. }
  223. sign, err := util.ParamSign(param, o.Key)
  224. if err != nil {
  225. return
  226. }
  227. request := new(payRequest).BridgePayRequest(p, o.AppID, o.MchID, nonceStr, sign)
  228. if len(p.TimeExpire) > 0 {
  229. // 如果有传入交易结束时间
  230. request.TimeExpire = p.TimeExpire
  231. }
  232. rawRet, err := util.PostXML(payGateway, request)
  233. if err != nil {
  234. return
  235. }
  236. err = xml.Unmarshal(rawRet, &payOrder)
  237. if err != nil {
  238. return
  239. }
  240. if payOrder.ReturnCode == SUCCESS {
  241. // pay success
  242. if payOrder.ResultCode == SUCCESS {
  243. err = nil
  244. return
  245. }
  246. err = errors.New(payOrder.ErrCode + payOrder.ErrCodeDes)
  247. return
  248. }
  249. err = errors.New("[msg : xmlUnmarshalError] [rawReturn : " + string(rawRet) + "] [sign : " + sign + "]")
  250. return
  251. }
  252. // PrePayID will request wechat merchant api and request for a pre-payment order id
  253. func (o *Order) PrePayID(p *Params) (prePayID string, err error) {
  254. order, err := o.PrePayOrder(p)
  255. if err != nil {
  256. return
  257. }
  258. if order.PrePayID == "" {
  259. err = errors.New("empty prepayid")
  260. }
  261. prePayID = order.PrePayID
  262. return
  263. }