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