From 47990473982b4baf3e2347e3d384717e018c28e0 Mon Sep 17 00:00:00 2001 From: wangp <wangpei@gmail.com> Date: Mon, 26 Jun 2023 18:10:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=89=E5=8D=A1=E6=8B=89=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=BE=AE=E4=BF=A1=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/api/v1/pay/notice.go | 38 ++++++ controller/base/response.go | 12 ++ models/notice.go | 26 +++++ repository/pay/business.go | 93 +++++++++++++++ repository/pay/notice.go | 149 +++++++++++++++++++++++ repository/pay/pay.go | 201 ++++++++++++++++++++++++++++++-- 6 files changed, 512 insertions(+), 7 deletions(-) create mode 100755 controller/api/v1/pay/notice.go create mode 100755 models/notice.go create mode 100644 repository/pay/business.go create mode 100755 repository/pay/notice.go diff --git a/controller/api/v1/pay/notice.go b/controller/api/v1/pay/notice.go new file mode 100755 index 0000000..910901c --- /dev/null +++ b/controller/api/v1/pay/notice.go @@ -0,0 +1,38 @@ +package pay + +import ( + "github.com/gin-gonic/gin" + "system_pay/controller/base" + "system_pay/models" + "system_pay/repository/pay" +) + +// 塿‹‰å¡ç»Ÿä¸€æ”¯ä»˜å›žè°ƒ +type NoticeController struct { +} + +// WxNotice æ‹‰å¡æ‹‰ç»Ÿä¸€æ”¯ä»˜å¾®ä¿¡å›žè°ƒ +// @Summary æ‹‰å¡æ‹‰ç»Ÿä¸€æ”¯ä»˜å¾®ä¿¡å›žè°ƒ +// @Description æ‹‰å¡æ‹‰ç»Ÿä¸€æ”¯ä»˜å¾®ä¿¡å›žè°ƒ +// @Tags æ‹‰å¡æ‹‰ç»Ÿä¸€æ”¯ä»˜å›žè°ƒ +// @Accept application/json +// @Produce application/json +// @Param language header string ture "è¯è¨€ç±»åž‹ zh-CNç®€ä½“ä¸æ–‡ en-US英文 ja 日文 é»˜è®¤ä¸æ–‡" +// @Success 200 +// @router /api/v1/pay/wx_notice [post] +func (l *PayController) WxNotice(c *gin.Context) { + + ph := new(models.WxNoticeInput) + err := c.ShouldBind(ph) + if err != nil { + response := new(base.ResponseDataWxNotice) + response.Code = "FAIL" + response.Message = "执行失败" + base.ResponseWxNotice(c, response) + return + } + + // 绑定 + response, err := pay.WxNotice(ph) + base.ResponseWxNotice(c, response) +} diff --git a/controller/base/response.go b/controller/base/response.go index 97061f5..a36d86d 100755 --- a/controller/base/response.go +++ b/controller/base/response.go @@ -72,4 +72,16 @@ func ResponseErrorMsg(c *gin.Context, msg string) { Message: msg, Data: nil, }) +} + +// ResponseDataWxNotice å¾®ä¿¡å›žè°ƒåŽ å¤„ç†å®Œé€»è¾‘è¿”å›žç»™å¾®ä¿¡çš„ä¿¡æ¯ +type ResponseDataWxNotice struct { + Code string `json:"code"` //SUCCESS/FAIL + Message string `json:"message"` //执行æˆåŠŸ/é”™è¯¯åŽŸå› +} +func ResponseWxNotice(c *gin.Context, data *ResponseDataWxNotice) { + c.JSON(http.StatusOK, &ResponseDataWxNotice{ + Code: data.Code, + Message: data.Message, + }) } \ No newline at end of file diff --git a/models/notice.go b/models/notice.go new file mode 100755 index 0000000..b485126 --- /dev/null +++ b/models/notice.go @@ -0,0 +1,26 @@ +package models + +// WxNoticeInput å¾®ä¿¡å›žè°ƒè¾“å…¥å‚æ•° +type WxNoticeInput struct { + ChannelId uint8 `json:"channel_id" description:"å¹³å°ç±»åž‹ 1: saas 2: shop 3: shop mobile 4: æ”¶é“¶å°"` + MerchantNo string `json:"merchant_no" description:"å¹³å°ä¿¡æ¯"` + OrderCreateTime string `json:"order_create_time" description:"å•†å“æè¿°"` + OrderEfficientTime string `json:"order_efficient_time" description:"商å“详情"` + OrderInfo string `json:"order_info" description:"é™„åŠ ä¿¡æ¯"` + OrderStatus float64 `json:"order_status" description:"商å“金é¢,个ä½ä¸ºåˆ†"` + OutOrderNo string `json:"out_order_no" description:"客户端回调的url"` + PayOrderNo int `json:"pay_order_no" description:"1: 微信,2: 支付å®, 3: æ‹‰å¡æ‹‰ 4: æ”¶é’±å§"` + TermNo uint8 `json:"term_no" description:"1: 微信 Native 2:微信å°ç¨‹åº 3:微信内支付 4:h5 跳微信 5:支付å®(web)-æ‰«ç æˆ–登录支付å®è´¦æˆ· 6:alipay(mobile) 7:alipay(app) 9: B2C 10:bk支付å®web 11:bk æ”¯ä»˜å®æ‰‹æœº"` + TotalAmount string `json:"total_amount" description:"æ¤å‚æ•° 支付类型是 JS API 的时候 å¿…ä¼ "` + TransMerchantNo string `json:"trans_merchant_no" description:"WAP网站URL地å€, 支付方å¼ä¸ºå¾®ä¿¡MWEBæ—¶ å¿…ä¼ "` + TransTermNo string `json:"trans_term_no" description:"WAP网站åç§°, 支付方å¼ä¸ºå¾®ä¿¡MWEBæ—¶ å¿…ä¼ "` +} + +// CallbackResponse is å›žè°ƒç»™ä¸šåŠ¡æ–¹çš„ä¿¡æ¯ +type CallbackResponse struct { + OutTradeNo string `json:"out_trade_no"` // 订å•å· + ReturnMsg string `json:"return_msg"` // æ˜¯å¦æˆåŠŸ + AttachInfo string `json:"attach_info"` // é™„åŠ ä¿¡æ¯ + TransactionID string `json:"transaction_id"` // 微信支付订å•å· + TradeNo string `json:"trade_no"` // 支付å®äº¤æ˜“æµæ°´å· +} diff --git a/repository/pay/business.go b/repository/pay/business.go new file mode 100644 index 0000000..df05570 --- /dev/null +++ b/repository/pay/business.go @@ -0,0 +1,93 @@ +package pay + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/astaxie/beego" +) + +var ( + callBackNumMap = map[int]int64{ + 1: 1, + 2: 2, + 3: 4, + 4: 8, + 5: 16, + 6: 32, + 7: 64, + 8: 128, + 9: 256, + 10: 512, + 11: 1024, + 12: 2048, + } +) + +func callBackBusinessService(outTradeNo, url string, data interface{}) { + + body, _ := json.Marshal(data) + + // callBackResponseData is 业务方回调过æ¥çš„æ•°æ® + type callBackResponseData struct { + Code int + Message string + Data interface{} + } + + number := 1 + for { + client := &http.Client{} + req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(string(body))) + req.Header.Set("Content-Type", "application/json") + resp, err := client.Do(req) + if err != nil { + beego.Error("网络请求错误") + second := callBackNumMap[number] + timeSleep := float64(time.Second) * float64(second) + number++ + beego.Info("当å‰ç‰å¾…: ", number, "订å•å·: ", outTradeNo) + time.Sleep(time.Duration(int64(timeSleep))) + continue + } + + bodyResp, err := ioutil.ReadAll(resp.Body) + beego.Info("bodyResp: ", string(bodyResp)) + if err != nil { + beego.Error("从 业务方 response sbodyä¸å–æ•°æ®å¤±è´¥") + second := callBackNumMap[number] + timeSleep := float64(time.Second) * float64(second) + number++ + beego.Info("当å‰ç‰å¾…: ", number, "订å•å·: ", outTradeNo) + time.Sleep(time.Duration(int64(timeSleep))) + continue + } + resp.Body.Close() + + // è§£æžä¸šåŠ¡æ–¹æ•°æ® + businessResp := new(callBackResponseData) + if err := json.Unmarshal(bodyResp, businessResp); err != nil { + beego.Error("è§£æžä¸šåŠ¡æ–¹è¿”å›žçš„æ•°æ®å¤±è´¥", string(bodyResp)) + return + } + + if businessResp.Code == 0 || businessResp.Message == "SUCCESS" { + beego.Info("回调业务方æˆåŠŸ: ", "订å•å·: ", outTradeNo, "回调了%v次", number) + return + } + + if number >= 12 { + beego.Info("当å‰ç‰å¾…: ", number, "订å•å·: ", outTradeNo, "结æŸå›žè°ƒ") + return + } + + second := callBackNumMap[number] + timeSleep := float64(time.Second) * float64(second) + beego.Info("当å‰ç‰å¾…: ", number, "订å•å·: ", outTradeNo) + number++ + time.Sleep(time.Duration(int64(timeSleep))) + } +} diff --git a/repository/pay/notice.go b/repository/pay/notice.go new file mode 100755 index 0000000..4c0c04e --- /dev/null +++ b/repository/pay/notice.go @@ -0,0 +1,149 @@ +package pay + +import ( + "encoding/json" + "fmt" + "strconv" + "system_pay/controller/base" + "system_pay/models" + "system_pay/mysql" +) + +// æ‹‰å¡æ‹‰å¾®ä¿¡æ”¯ä»˜å›žè°ƒ - AliPayNotice +func WxNotice(input *models.WxNoticeInput) (*base.ResponseDataWxNotice, error) { + + fmt.Println("接å£è¾“入傿•°") + fmt.Println(input) + + response := new(base.ResponseDataWxNotice) + + db, err := mysql.NewShopConn() + if err != nil { + response.Code = "FAIL" + response.Message = "傿•°æ ¼å¼æ ¡éªŒé”™è¯¯" + return response, err + } + + fmt.Println("111") + + //1.订å•åˆæ³•check + var billID int64 + var attach, url string + err = db.QueryRow("select ifnull(id, 0), attach, notify_pay_url from pay_bill where payment_order_code = ?", + input.OutOrderNo).Scan(&billID, &attach, &url) + if err != nil || billID == 0 || billID == 2 { + response.Code = "FAIL" + response.Message = "db operation fail" + return response, err + } + + fmt.Println("222") + + //2.å˜å…¥ notice_request_body + err = InsertPayBillDetailNoticeRequestBody(billID, input) + if err != nil { + //beego.Error("微信回调, æ ¹æ®è®¢å•id æ’入回调Request傿•° 失败: ", err) + response.Code = "FAIL" + response.Message = "db operation fail" + InsertPayBillDetailNoticeResponseBody(billID, response) + return response, err + } + + fmt.Println("333") + + //3.å˜å…¥ notice_response_body + //err = InsertPayBillDetailNoticeResponseBody(billID, response) + //if err != nil { + // beego.Error("微信回调, æ ¹æ®è®¢å•id æ’入回调Response傿•° 失败: ", err) + // response.ReturnCode = "FAIL" + // response.ReturnMsg = "db operation fail" + // return err + //} + + //beego.Info("回调æˆåŠŸ è®¢å•æˆåŠŸ") + + //type CallbackResponse struct { + // OutTradeNo string `json:"out_trade_no"` // 订å•å· + // ReturnMsg string `json:"return_msg"` // æ˜¯å¦æˆåŠŸ + // AttachInfo string `json:"attach_info"` // é™„åŠ ä¿¡æ¯ + // TransactionID string `json:"transaction_id"` // 微信支付订å•å· + // TradeNo string `json:"trade_no"` // 支付å®äº¤æ˜“æµæ°´å· + //} + + if url != "" { + //4.回调业务方「回调函数〠+ callbackResponse := new(models.CallbackResponse) + callbackResponse.ReturnMsg = "SUCCESS" + //callbackResponse.OutTradeNo = input.OutTradeNo + //callbackResponse.TransactionID = input.TransactionId + callbackResponse.OutTradeNo = strconv.Itoa(input.PayOrderNo) //äº¤æ˜“å‡æ®å•å· todo + callbackResponse.TransactionID = strconv.Itoa(input.PayOrderNo) //äº¤æ˜“å‡æ®å•å· todo + + attachMap := make(map[string]interface{}, 0) + _ = json.Unmarshal([]byte(attach), &attachMap) + if attachMap["store_sn"].(string) == "" { + callbackResponse.AttachInfo = attachMap["old_attach"].(string) + } else { + callbackResponse.AttachInfo = attach //商户订å•å·ï¼ˆè°›å®å¤šå¤šï¼‰ + } + + go callBackBusinessService(input.OutOrderNo, url, callbackResponse) + fmt.Println("444") + } + + response.Code = "SUCCESS" + response.Message = "执行æˆåŠŸ" + InsertPayBillDetailNoticeResponseBody(billID, response) + return response, nil +} + +// InsertPayBillDetailNoticeRequestBody is æ’入支付订å•详情表ä¸çš„ 下å•傿•°å—段 notice_request_body +func InsertPayBillDetailNoticeRequestBody(billID int64, noticeRequestBody interface{}) error { + + db, err := mysql.NewShopConn() + if err != nil { + return err + } + + body, err := json.Marshal(noticeRequestBody) + if err != nil { + return err + } + insertPayBillDetailSQL := `update pay_bill_detail set notice_request_body = ? where pay_bill_id = ?` + result, err := db.Exec(insertPayBillDetailSQL, string(body), billID) + if err != nil { + return err + } + + _, err = result.RowsAffected() + if err != nil { + return err + } + return nil +} + +// InsertPayBillDetailNoticeResponseBody is æ’入支付订å•详情表ä¸çš„ 下å•傿•°å—段 notice_response_body +func InsertPayBillDetailNoticeResponseBody(billID int64, noticeResponseBody interface{}) error { + + db, err := mysql.NewShopConn() + if err != nil { + return err + } + + body, err := json.Marshal(noticeResponseBody) + if err != nil { + return err + } + insertPayBillDetailSQL := `update pay_bill_detail set notice_response_body = ? where pay_bill_id = ?` + result, err := db.Exec(insertPayBillDetailSQL, string(body), billID) + if err != nil { + return err + } + + _, err = result.RowsAffected() + if err != nil { + return err + } + return nil +} + diff --git a/repository/pay/pay.go b/repository/pay/pay.go index a07433d..2628b7a 100755 --- a/repository/pay/pay.go +++ b/repository/pay/pay.go @@ -11,10 +11,12 @@ import ( "encoding/pem" "errors" "fmt" + "github.com/astaxie/beego" "github.com/astaxie/beego/httplib" rand2 "math/rand" "strings" "system_pay/models" + "system_pay/mysql" "time" ) @@ -31,11 +33,11 @@ func UnifiedOrder(input *models.PlaceAnOrderParamInput) (interface{}, error) { //} //rtn := make(map[string]string) - fmt.Println("接å£è¾“入傿•°") + fmt.Println("æ‹‰å¡æ‹‰æ”¯ä»˜æŽ¥å£è¾“入傿•°") fmt.Println(input) data := make(map[string]interface{}) - data["req_time"] = "20220714160009" + data["req_time"] = "20230626100000" data["version"] = "3.0" data["out_org_code"] = "OP00000003" @@ -57,22 +59,29 @@ func UnifiedOrder(input *models.PlaceAnOrderParamInput) (interface{}, error) { // "extend_info": "自动化测试", // "callback_url": "" - input.NoticeURL = "https://test.pet-dbc.cn/uni/api/repayment/PayCallBack" + //input.NoticeURL = "https://test.pet-dbc.cn/uni/api/repayment/PayCallBack" + //input.NoticeURL = "https://test.pet-dbc.cn/mobile/notifywx.php" input.ReturnURL = "https://test.pet-dbc.cn" + // æž„é€ å›žè°ƒurl + input.NoticeURL = GetNoticeURL(input.SourceCode) + // ä¼ é€’ç»™æ”¯ä»˜æ¸ é“çš„ + //p.ServeNoticeUrl = noticeURLx + data2 := make(map[string]interface{}) //data2["out_trade_no"] = "FD660E1FAA3A4470933CDEDAE1EC1DUU" data2["out_trade_no"] = RandomString(32) - data2["merchant_no"] = "822290070111135" + //data2["merchant_no"] = "822290070111135" + data2["merchant_no"] = "8222900701107M5" data2["term_no"] = "29034705" //data2["merchant_no"] = "8222900581201QB" //data2["term_no"] = "D0027598" data2["auth_code"] = "135178236713755038" - data2["total_amount"] = "123" + data2["total_amount"] = "1" //data2["out_order_no"] = "08F4542EEC6A4497BC419161747A92UU" data2["out_order_no"] = RandomString(32) - data2["order_efficient_time"] = "20230620130000" //è®¢å•æœ‰æ•ˆæœŸ æ ¼å¼yyyyMMddHHmmss,最大支æŒä¸‹å•æ—¶é—´+2天 + data2["order_efficient_time"] = "20230630235959" //è®¢å•æœ‰æ•ˆæœŸ æ ¼å¼yyyyMMddHHmmss,最大支æŒä¸‹å•æ—¶é—´+2天 data2["notify_url"] = input.NoticeURL //è®¢å•æ”¯ä»˜æˆåŠŸåŽå•†æˆ·æŽ¥æ”¶è®¢å•é€šçŸ¥çš„åœ°å€ http://xxx.xxx.com data2["callback_url"] = input.ReturnURL //客户端下å•å®Œæˆæ”¯ä»˜åŽè¿”å›žçš„å•†æˆ·ç½‘é¡µè·³è½¬åœ°å€ @@ -502,4 +511,182 @@ func RandomString(n int) string { result[i] = letters[rand2.Intn(len(letters))] } return string(result) -} \ No newline at end of file +} + +// GetNoticeURL is 获å–å›žè°ƒåœ°å€ +func GetNoticeURL(sourceCode uint8) string { + + domainName := beego.AppConfig.String("DomainName") + + // <= 5 是微信 + if sourceCode < 5 { + return domainName + "/v1/pay/wx_notice" + } + + // > 5 æ˜¯æ”¯ä»˜å® + return domainName + "/v1/pay/alipay_notice" + // + //if isServe == 0 { + // // payType 1: 原生 2: paymax + // if payType == 1 { + // + // // <= 5 是微信 > 5 æ˜¯æ”¯ä»˜å® + // if sourceCode < 5 { + // return domainName + "/v1/notice/wx" + // } + // + // return domainName + "/v1/notice/ali" + // } + // + // return domainName + "/v1/notice/paymax" + //} + // + //// <= 5 是微信 > 5 æ˜¯æ”¯ä»˜å® + //if sourceCode < 5 { + // return domainName + "/v1/notice/serve_wx" + //} + // + //return domainName + "/v1/notice/serve_ali" +} + + +// BillPayStateSuccess is å¾®ä¿¡å›žè°ƒåŽ æŠŠè®¢å•çŠ¶æ€ ç½®ä¸ºç»“ç®—æˆåŠŸ +func BillPayStateSuccess(billID int64) error { + + db, err := mysql.NewShopConn() + if err != nil { + return err + } + + insertPayBillDetailSQL := `update pay_bill set result_code = 1 where id = ?` + result, err := db.Exec(insertPayBillDetailSQL, billID) + if err != nil { + return err + } + + _, err = result.RowsAffected() + if err != nil { + return err + } + return nil +} + +// BillPayStateFail is å¾®ä¿¡å›žè°ƒåŽ æŠŠè®¢å•çŠ¶æ€ ç½®ä¸ºç»“ç®—å¤±è´¥ +func BillPayStateFail(billID int64) error { + + db, err := mysql.NewShopConn() + if err != nil { + return err + } + + insertPayBillDetailSQL := `update pay_bill set result_code = 2 where id = ?` + result, err := db.Exec(insertPayBillDetailSQL, billID) + if err != nil { + return err + } + + _, err = result.RowsAffected() + if err != nil { + return err + } + return nil +} + + + +//InsertPayBill is æ’å…¥ 支付订å•è¡¨ä¸ +func InsertPayBill(p *models.PlaceAnOrderParamInput, orderID string) (int64, error) { + + db, err := mysql.NewShopConn() + if err != nil { + return 0, err + } + + var billID int64 + + insertSQL := `insert pay_bill set platform_type = ?, platform_info = ?, +source_code = ?, payment_order_code = ?, paymoney = ?*1000, commodity_describe = ?, +commodity_detail = ?, attach = ?, notify_pay_url = ?, pay_type = ?, is_serve = ?` + + result, err := db.Exec(insertSQL, p.PlatformType, p.PlatformInfo, p.SourceCode, + orderID, p.GoodsPrice, p.GoodsDes, p.GoodsDetail, p.AttachInfo, p.NoticeURL, p.PayType, p.IsServe) + if err != nil { + return billID, err + } + + billID, err = result.LastInsertId() + if err != nil { + return billID, err + } + return billID, nil +} + +// InsertPayBillDetailRequestBody is æ’入支付订å•详情表ä¸çš„ 下å•傿•°å—段 request_body +func InsertPayBillDetailRequestBody(billID int64, requestBody interface{}) error { + + db, err := mysql.NewShopConn() + if err != nil { + return err + } + + body, err := json.Marshal(requestBody) + if err != nil { + return err + } + insertPayBillDetailSQL := `insert pay_bill_detail set pay_bill_id = ?, request_body = ?` + result, err := db.Exec(insertPayBillDetailSQL, billID, string(body)) + if err != nil { + return err + } + + _, err = result.LastInsertId() + if err != nil { + return err + } + return nil +} + +// InsertPayBillDetailResponseBody is æ’入支付订å•详情表ä¸çš„ 下å•傿•°å—段 response_body +func InsertPayBillDetailResponseBody(billID int64, responseBody interface{}) error { + + db, err := mysql.NewShopConn() + if err != nil { + return err + } + + body, err := json.Marshal(responseBody) + if err != nil { + return err + } + insertPayBillDetailSQL := `update pay_bill_detail set response_body = ? where pay_bill_id = ?` + result, err := db.Exec(insertPayBillDetailSQL, string(body), billID) + if err != nil { + return err + } + + _, err = result.RowsAffected() + if err != nil { + return err + } + return nil +} + +func InsertPayBillDetailResponseBodyString(billID int64, responseBody string) error { + + db, err := mysql.NewShopConn() + if err != nil { + return err + } + + insertPayBillDetailSQL := `update pay_bill_detail set response_body = ? where pay_bill_id = ?` + result, err := db.Exec(insertPayBillDetailSQL, responseBody, billID) + if err != nil { + return err + } + + _, err = result.RowsAffected() + if err != nil { + return err + } + return nil +} -- 2.18.1