wxpay_handler.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. package talent_service
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/gogf/gf/frame/g"
  6. "github.com/gogf/gf/net/ghttp"
  7. "github.com/gogf/gf/os/glog"
  8. "github.com/gogf/gf/os/gtime"
  9. "github.com/wechatpay-apiv3/wechatpay-go/core"
  10. "github.com/wechatpay-apiv3/wechatpay-go/core/option"
  11. "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
  12. wxUtils "github.com/wechatpay-apiv3/wechatpay-go/utils"
  13. "math/rand"
  14. "strconv"
  15. "time"
  16. "youngmini_server/app/dao"
  17. "youngmini_server/app/model"
  18. "youngmini_server/app/model/talent_model"
  19. "youngmini_server/app/utils"
  20. )
  21. var (
  22. mchID string = "1615933939" // 商户号
  23. mchCertificateSerialNumber string = "636DC3B258E436FAD123DAC6453E485CCDBF8770" // 商户证书序列号
  24. mchAPIv3Key string = "zwbx8iae5llhpwj48guterlpodt3xngo" // 商户APIv3密钥
  25. keyFilePath string = "./config/secret/apiclient_key.pem" // 密钥文件路径
  26. )
  27. func generatePayOrderNumber(talentId int) string {
  28. orderNo := gtime.Now().Format("YmdHis")
  29. rand.Seed(time.Now().UnixNano())
  30. r := rand.Intn(899)
  31. orderNo += strconv.Itoa(r + 100)
  32. orderNo += fmt.Sprintf("%04d", talentId)
  33. return orderNo
  34. }
  35. func WxPayOnTalentRequestPay(r *ghttp.Request) *TalentHttpResult {
  36. tid, err := utils.SessionTalentInfo.GetTalentIdFromSession(r)
  37. if err != nil {
  38. return &TalentHttpResult{Code: -1, Msg: "Get talent id failed"}
  39. }
  40. // 获取上传参数
  41. var payInfo *talent_model.WxPrepayRequestData
  42. err = r.ParseForm(&payInfo)
  43. if err != nil {
  44. return &TalentHttpResult{Code: -2, Msg: "param error"}
  45. }
  46. // 获取达人信息
  47. var talentInfo *model.TalentInfo
  48. err = g.DB().Model(dao.TalentInfo.Table).Scan(&talentInfo, dao.TalentInfo.Columns.Id, tid)
  49. if err != nil {
  50. return &TalentHttpResult{Code: -3, Msg: "query talent info failed"}
  51. }
  52. if talentInfo.InBlacklist >= 1 {
  53. return &TalentHttpResult{Code: -4, Msg: "talent in blacklist"}
  54. }
  55. // 获取任务信息
  56. var taskInfo *talent_model.TaskDetail
  57. err = g.DB().Model(dao.TaskBaseInfo.Table).WithAll().Where(dao.TaskBaseInfo.Columns.TaskId, payInfo.TaskId).Scan(&taskInfo)
  58. if err != nil || taskInfo == nil {
  59. return &TalentHttpResult{Code: -5, Msg: "query task info failed"}
  60. }
  61. // 任务当前不是执行状态,返回
  62. if taskInfo.TaskStatus != 2 {
  63. return &TalentHttpResult{Code: -6, Msg: "task can not sign up"}
  64. }
  65. // 任务已截止报名
  66. if taskInfo.DeadlineTime.Before(gtime.Now()) {
  67. return &TalentHttpResult{Code: -7, Msg: "task sign up has finished"}
  68. }
  69. // 如果任务不需要拍单,或拍单方式为不拍单则返回
  70. if taskInfo.IsBuySamples == nil || taskInfo.IsBuySamples.BuySamplesType != 1 {
  71. return &TalentHttpResult{Code: -8, Msg: "task do not need buy samples"}
  72. }
  73. // 任务拍单费用和上传的拍单费用不符,返回
  74. if taskInfo.IsBuySamples.BuySamplesCost != payInfo.BuySamplesCost {
  75. return &TalentHttpResult{Code: -9, Msg: "task buy samples cost != input cost"}
  76. }
  77. mchPrivateKey, err := wxUtils.LoadPrivateKeyWithPath(keyFilePath)
  78. if err != nil {
  79. glog.Error("load merchant private key error")
  80. return &TalentHttpResult{Code: -10, Msg: "load client key failed"}
  81. }
  82. ctx := context.Background()
  83. // 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
  84. opts := []core.ClientOption{
  85. option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
  86. }
  87. client, err := core.NewClient(ctx, opts...)
  88. if err != nil {
  89. glog.Error("new wechat pay client err:", err)
  90. return &TalentHttpResult{Code: -11, Msg: "create client failed"}
  91. }
  92. orderNumber := generatePayOrderNumber(talentInfo.Id)
  93. svc := jsapi.JsapiApiService{Client: client}
  94. // 得到prepay_id,以及调起支付所需的参数和签名
  95. resp, result, err := svc.PrepayWithRequestPayment(ctx,
  96. jsapi.PrepayRequest{
  97. Appid: core.String(g.Config().GetString("miniapp.appid")),
  98. Mchid: core.String(mchID),
  99. Description: core.String("拍单费用_" + strconv.Itoa(taskInfo.TaskId) + "_" +taskInfo.TaskName + "_" + payInfo.PlatformNickname),
  100. OutTradeNo: core.String(orderNumber),
  101. Attach: core.String("报名任务拍单费用"),
  102. NotifyUrl: core.String("https://www.weixin.qq.com/wxpay/pay.php"),
  103. Amount: &jsapi.Amount{
  104. Total: core.Int64(payInfo.BuySamplesCost),
  105. },
  106. Payer: &jsapi.Payer{
  107. Openid: core.String(talentInfo.TalentWxOpenid),
  108. },
  109. },
  110. )
  111. if err != nil {
  112. // 处理错误
  113. glog.Error("call Prepay err:%s", err)
  114. return &TalentHttpResult{Code: -12, Msg: "request prepay info failed"}
  115. }
  116. // 处理返回结果
  117. glog.Println("status=%d resp=%s", result.Response.StatusCode, resp)
  118. if result.Response.StatusCode != 200 {
  119. glog.Println(result.Response)
  120. }
  121. // 将支付记录写入数据库
  122. _, err = g.DB().Model(dao.WxPayOrder.Table).Insert(model.WxPayOrder{
  123. OutTradeNo: orderNumber,
  124. WxOrderNo: "",
  125. PayAmount: payInfo.BuySamplesCost,
  126. TaskName: taskInfo.TaskName,
  127. PayReason: 1,
  128. TalentId: talentInfo.Id,
  129. TalentWxNickname: talentInfo.TalentWxNickname,
  130. TalentPlatformAccountName: payInfo.PlatformNickname,
  131. TaskId: taskInfo.TaskId,
  132. TaskPlatform: taskInfo.TaskPlatform,
  133. Success: 0,
  134. CreateAt: gtime.Now(),
  135. Desc: "",
  136. })
  137. if err != nil {
  138. return &TalentHttpResult{Code: -13, Msg: "insert wx pay order failed"}
  139. }
  140. return &TalentHttpResult{Code: 0, Msg: "success", Data: talent_model.WxPrepayResponseData{
  141. OutTradeNo: orderNumber,
  142. WxResp: resp,
  143. }}
  144. }
  145. func WxPayQueryOrderById(r *ghttp.Request) *TalentHttpResult {
  146. transactionId := r.GetRequestString("transaction_id", "nil")
  147. if transactionId == "nil" {
  148. glog.Error("not input transaction_id param")
  149. return &TalentHttpResult{Code: -2, Msg: "input transaction_id param"}
  150. }
  151. // 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
  152. mchPrivateKey, err := wxUtils.LoadPrivateKeyWithPath(keyFilePath)
  153. if err != nil {
  154. glog.Error("load merchant private key error")
  155. return &TalentHttpResult{Code: -3, Msg: "load merchant private key error"}
  156. }
  157. ctx := context.Background()
  158. // 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
  159. opts := []core.ClientOption{
  160. option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
  161. }
  162. client, err := core.NewClient(ctx, opts...)
  163. if err != nil {
  164. glog.Error("new wechat pay client err:%s", err)
  165. return &TalentHttpResult{Code: -4, Msg: "load merchant private key error"}
  166. }
  167. svc := jsapi.JsapiApiService{Client: client}
  168. resp, _, err := svc.QueryOrderById(ctx,
  169. jsapi.QueryOrderByIdRequest{
  170. TransactionId: core.String(transactionId),
  171. Mchid: core.String(mchID),
  172. },
  173. )
  174. if err != nil {
  175. // 处理错误
  176. glog.Error("call WxPayQueryOrderById err:%s", err)
  177. return &TalentHttpResult{Code: -5, Msg: "call WxPayQueryOrderById failed"}
  178. }
  179. // TODO:支付结果写入数据库
  180. _, err = g.DB().Model(dao.WxPayOrder.Table).Update(g.Map{
  181. dao.WxPayOrder.Columns.WxOrderNo: resp.TransactionId,
  182. dao.WxPayOrder.Columns.Success: 1,
  183. dao.WxPayOrder.Columns.Desc: resp.TradeStateDesc,
  184. }, dao.WxPayOrder.Columns.OutTradeNo, resp.OutTradeNo)
  185. if err != nil {
  186. glog.Error("wxpay_handler.WxPayQueryOrderById Update database failed, order number = ", resp.OutTradeNo)
  187. return &TalentHttpResult{Code: -6, Msg: "Update database failed"}
  188. }
  189. return &TalentHttpResult{Code: 0, Msg: "success"}
  190. }
  191. func QueryOrderByOutTradeNo(r *ghttp.Request) *TalentHttpResult {
  192. outTradeNo := r.GetRequestString("out_trade_no", "nil")
  193. if outTradeNo == "nil" {
  194. glog.Error("not input out_trade_no param")
  195. return &TalentHttpResult{Code: -2, Msg: "input transaction_id param"}
  196. }
  197. g.DB().Model()
  198. // 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
  199. mchPrivateKey, err := wxUtils.LoadPrivateKeyWithPath(keyFilePath)
  200. if err != nil {
  201. glog.Error("load merchant private key error")
  202. return &TalentHttpResult{Code: -3, Msg: "load merchant private key error"}
  203. }
  204. ctx := context.Background()
  205. // 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
  206. opts := []core.ClientOption{
  207. option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
  208. }
  209. client, err := core.NewClient(ctx, opts...)
  210. if err != nil {
  211. glog.Error("new wechat pay client err:%s", err)
  212. return &TalentHttpResult{Code: -4, Msg: "load merchant private key error"}
  213. }
  214. svc := jsapi.JsapiApiService{Client: client}
  215. resp, _, err := svc.QueryOrderByOutTradeNo(ctx,
  216. jsapi.QueryOrderByOutTradeNoRequest{
  217. OutTradeNo: core.String(outTradeNo),
  218. Mchid: core.String(mchID),
  219. },
  220. )
  221. if err != nil {
  222. // 处理错误
  223. glog.Error("call QueryOrderByOutTradeNo err:%s", err)
  224. return &TalentHttpResult{Code: -5, Msg: "call QueryOrderByOutTradeNo failed"}
  225. }
  226. // TODO:支付结果写入数据库
  227. _, err = g.DB().Model(dao.WxPayOrder.Table).Update(g.Map{
  228. dao.WxPayOrder.Columns.WxOrderNo: resp.TransactionId,
  229. dao.WxPayOrder.Columns.Success: 1,
  230. dao.WxPayOrder.Columns.Desc: resp.TradeStateDesc,
  231. }, dao.WxPayOrder.Columns.OutTradeNo, resp.OutTradeNo)
  232. if err != nil {
  233. glog.Error("wxpay_handler.WxPayQueryOrderById Update database failed, order number = ", resp.OutTradeNo)
  234. return &TalentHttpResult{Code: -6, Msg: "Update database failed"}
  235. }
  236. return &TalentHttpResult{Code: 0, Msg: "success"}
  237. }
  238. func OnWxPayNotify(r *ghttp.Request) {
  239. wxSignature := r.GetHeader("Wechatpay-Signature")
  240. if wxSignature == "" {
  241. glog.Error("OnWxPayNotify header not contain Wechatpay-Signature property")
  242. r.Response.WriteJsonExit(g.Map{
  243. "code": "FAILED",
  244. "message": "header中不包括Wechatpay-Signature",
  245. })
  246. }
  247. }