Compare commits

...

22 Commits

Author SHA1 Message Date
Rzy ee912a204d <feat>退款 2024-08-08 14:52:30 +08:00
Rzy aa43040aa2 <feat>调整代码结构 2024-08-08 11:15:50 +08:00
Rzy cb14c35aa9 <feat>调整代码结构 2024-08-08 10:48:25 +08:00
Rzy ebea5c52e0 <fix>处理bug 2024-08-08 09:52:05 +08:00
Rzy 40dfffe31a <fix>bug修改 2024-08-08 09:49:27 +08:00
陈俊宏 58f8cedb85 Merge branch 'dev/dev1.0' into feature_413_cjh 2024-08-08 09:43:02 +08:00
陈俊宏 d6bf4dd952 回调log记录 2024-08-08 09:42:46 +08:00
Rzy 7ed1ca131b Merge branch 'feature/rzy/api_1.0' into dev/dev1.0 2024-08-08 09:30:39 +08:00
陈俊宏 30b83b3645 Merge branch 'feature_413_cjh' into dev/dev1.0 2024-08-08 09:20:49 +08:00
陈俊宏 62d050c02f 回调log 2024-08-08 09:20:25 +08:00
wolter 3061987dae Merge branch 'refs/heads/feature/zxp' into dev/dev1.0 2024-08-08 09:18:49 +08:00
wolter 3d5e33fed0 Merge remote-tracking branch 'refs/remotes/origin/feature/rzy/api_1.0' into dev/dev1.0
# Conflicts:
#	app/constants/errorcode/error_code.go
#	app/services/thirdpay/notify/notify.go
2024-08-08 09:14:08 +08:00
wolter bf1bc4b1a2 后台,密钥对生成 2024-08-07 17:14:03 +08:00
wolter 514104a391 定时查询退款单 2024-08-07 10:49:06 +08:00
陈俊宏 ad56b5f0e5 Merge branch 'dev/dev1.0' into feature_413_cjh 2024-08-07 10:32:37 +08:00
陈俊宏 82ef285e7d 订单查询 2024-08-07 10:32:15 +08:00
陈俊宏 ca42c43bd2 回调 触发下游回调 2024-08-07 10:22:40 +08:00
陈俊宏 6ab982e874 Merge remote-tracking branch 'origin/feature/rzy/api_1.0' into feature_413_cjh 2024-08-07 10:16:13 +08:00
wolter c79e316233 Merge remote-tracking branch 'refs/remotes/origin/feature/rzy/api_1.0' into feature/zxp 2024-08-07 10:03:42 +08:00
陈俊宏 75d68a3032 Merge remote-tracking branch 'origin/feature/rzy/api_1.0' into feature_413_cjh 2024-08-07 09:00:49 +08:00
wolter e261a39cf5 Merge remote-tracking branch 'refs/remotes/origin/feature/rzy/api_1.0' into feature/zxp 2024-08-07 08:48:28 +08:00
陈俊宏 68b9e16864 支付回调处理 2024-08-06 18:00:49 +08:00
34 changed files with 578 additions and 416 deletions

View File

@ -6,14 +6,13 @@ import (
"PaymentCenter/app/http/entities" "PaymentCenter/app/http/entities"
"PaymentCenter/app/http/entities/backend" "PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/ordersmodel" "PaymentCenter/app/models/ordersmodel"
"PaymentCenter/app/models/orderthirdpaylogmodel" "PaymentCenter/app/services/thirdpay/thirdpay_notify"
"PaymentCenter/app/third/paymentService" "PaymentCenter/app/third/paymentService"
"PaymentCenter/app/third/paymentService/payCommon" "PaymentCenter/app/third/paymentService/payCommon"
"PaymentCenter/app/utils" "PaymentCenter/app/utils"
"PaymentCenter/config" "PaymentCenter/config"
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"github.com/qit-team/snow-core/command" "github.com/qit-team/snow-core/command"
"strconv" "strconv"
"sync" "sync"
@ -83,7 +82,7 @@ func closeOrder() {
if response.Code == payCommon.PAY_SUCCESS_CODE { if response.Code == payCommon.PAY_SUCCESS_CODE {
orderIds = append(orderIds, orderInfo.Id) orderIds = append(orderIds, orderInfo.Id)
} else { } else {
utils.Log(nil, "关闭订单,上游支付失败", response) utils.Log(nil, "关闭订单,上游失败", response)
} }
} }
// 修改订单状态为关闭 // 修改订单状态为关闭
@ -172,64 +171,115 @@ func queryOrder() {
// 成功 // 成功
status = common.ORDER_STATUS_PAYED status = common.ORDER_STATUS_PAYED
case "REFUND": case "REFUND":
// 退款 // 退款 订单支付完成才能退款,所以支付单的状态是支付完成
status = common.ORDER_STATUS_REFUND status = common.ORDER_STATUS_PAYED
case "NOTPAY": case "NOTPAY":
// 未支付 // 未支付
return return
case "CLOSED": case "CLOSED":
// 关闭 // 关闭
status = common.ORDER_STATUS_CLOSE status = common.ORDER_STATUS_CLOSE
} }
// 回调通知下游 todo // 回调通知下游 todo
notifyResult := thirdpay_notify.NewOrderNotifyWithHandle(orderInfo.Id, status, int(result.Result.PayerTotal), "")
utils.Log(nil, "主动查询订单支付状态,回调下游", notifyResult)
}
}(orderInfo)
}
wg.Wait()
}
}
// 更新订单状态 todo // 主动查询退款订单状态
orderUpdate := ordersmodel.Orders{ func queryRefundOrder() {
Id: orderInfo.Id, var now = time.Now().Format(time.DateTime)
Status: status, utils.Log(nil, "主动查询退款订单状态", now)
PayerTotal: int(result.Result.PayerTotal), ctx := context.Background()
// 查询退款中的订单
repo := data.NewOrderRepo(ordersmodel.GetInstance().GetDb())
// 拼接条件
cond := builder.NewCond()
cond = cond.And(builder.Eq{"status": common.ORDER_STATUS_PAYING}, builder.Gt{"orders.create_time": time.Now().Add(-time.Second * time.Duration(config.GetConf().CronConfig.QueryOrderTime))})
cond = cond.And(builder.Eq{"order_type": common.ORDER_TYPE_REFUND})
order := make([]ordersmodel.OrdersLeftPayChannelList, 0)
err := repo.OrdersLeftPayChannelList(cond, entities.PageRequest{}, &order)
if err != nil {
utils.Log(nil, "主动查询退款订单状态,查询退款中订单失败", err)
return
} else if len(order) > 0 {
ch := make(chan struct{}, config.GetConf().CronConfig.ConcurrentNumber)
wg := sync.WaitGroup{}
for index := range order {
ch <- struct{}{}
wg.Add(1)
orderInfo := order[index]
// 发起查询上游
go func(orderInfo ordersmodel.OrdersLeftPayChannelList) {
defer func() {
<-ch
wg.Done()
}()
query := paymentService.OrderRefundQueryRequest{
RefundOrderId: orderInfo.Id,
OrderId: orderInfo.RefundOrderId,
}
switch utils.PayType(orderInfo.ChannelType) {
case common.PAY_CHANNLE_TYPE_WECHAT:
wx := backend.WechatPayChannel{}
_ = json.Unmarshal([]byte(orderInfo.ExtJson), &wx)
query.PayChannel = payCommon.PAY_CHANNLE_TYPE_WECHAT
query.Wx = paymentService.WxPay{
AppId: orderInfo.AppId,
MchId: wx.MchId,
SerialNo: wx.SerialNo,
ApiV3Key: wx.ApiV3Key,
PrivateKey: wx.PrivateKey,
} }
case common.PAY_CHANNLE_TYPE_ZFB:
ali := backend.AliPayChannel{}
query.PayChannel = payCommon.PAY_CHANNLE_TYPE_ZFB
_ = json.Unmarshal([]byte(orderInfo.ExtJson), &ali)
query.Ali = paymentService.AliPay{
AppId: orderInfo.AppId,
PrivateKey: ali.PrivateKey,
AppPublicCert: ali.AppPublicCert,
AlipayRootCert: ali.AlipayRootCert,
AlipayPublicCert: ali.AlipayPublicCert,
}
default:
utils.Log(nil, "查询订单,支付渠道不支持", orderInfo.ChannelType)
return
}
session := ordersmodel.GetInstance().GetDb().NewSession() // 发起查询
if err = session.Begin(); err != nil { result := paymentService.OrderRefundQuery(ctx, query)
utils.Log(nil, "主动查询订单支付状态,更新订单状态失败", err) utils.Log(nil, "主动查询退款订单状态,上游返回数据", result)
// 查询成功,校验状态
var status int
if result.Code == payCommon.PAY_SUCCESS_CODE {
// 退款状态 0未申请1退款中2退款成功3退款失败
switch result.Result.RefundStatus {
case 0:
// 未申请
utils.Log(nil, "主动查询退款订单状态,未申请", status)
return return
} case 1:
defer func() { // 退款中
if err != nil { status = common.ORDER_STATUS_PAYING
session.Rollback()
} else {
err = session.Commit()
}
}()
orderLogRepo := data.NewOrderThirdPayLogRepo(session)
orderRepo := data.NewOrderRepo(session)
conn := builder.NewCond()
conn = conn.And(builder.Eq{"id": orderInfo.Id})
_, err = orderRepo.OrderUpdate(&orderUpdate, conn)
if err != nil {
utils.Log(nil, "主动查询订单支付状态,更新订单状态失败", err)
return return
case 2:
// 退款成功
status = common.ORDER_STATUS_PAYED
case 3:
// 退款失败
status = common.ORDER_STATUS_FAILED
} }
// 回调通知下游 todo
// 写入日志 notifyResult := thirdpay_notify.NewOrderNotifyWithHandle(orderInfo.Id, status, int(result.Result.RefundFee), "")
body, _ := json.Marshal(result) utils.Log(nil, "主动查询退款订单状态,回调下游", notifyResult)
log := orderthirdpaylogmodel.OrderThirdPayLog{
OrderId: orderInfo.Id,
PayCallback: string(body),
Status: 0,
PayParam: fmt.Sprintf(`{"pay_channel_id":%d}`, orderInfo.PayChannelId),
Type: common.THIRD_ORDER_TYPE_ORDER_QUERY,
MerchantCallback: "{}",
}
_, err = orderLogRepo.OrderThirdPayLogInsertOne(&log)
if err != nil {
utils.Log(nil, "主动查询订单支付状态,写入支付日志失败", err)
}
} }
}(orderInfo) }(orderInfo)
} }

View File

@ -17,4 +17,5 @@ func RegisterSchedule(c *cron.Cron) {
//c.AddFunc("@every 10s", test) //c.AddFunc("@every 10s", test)
c.AddFunc("@every 60s", closeOrder) c.AddFunc("@every 60s", closeOrder)
c.AddFunc("@every 10s", queryOrder) c.AddFunc("@every 10s", queryOrder)
c.AddFunc("@every 60s", queryRefundOrder)
} }

View File

@ -22,13 +22,12 @@ const (
ADMIN_USER_NAME = "User-Name" ADMIN_USER_NAME = "User-Name"
ADMIN_USER_INCLUDEUSERS = "Include-Users" ADMIN_USER_INCLUDEUSERS = "Include-Users"
// '订单状态: 1待支付、2支付中、3支付成功、4支付失败、5订单关闭 6支付完成后退款, // '订单状态: 1待支付、2支付中、3支付成功、4支付失败、5订单关闭
ORDER_STATUS_WAITPAY = 1 ORDER_STATUS_WAITPAY = 1
ORDER_STATUS_PAYING = 2 ORDER_STATUS_PAYING = 2
ORDER_STATUS_PAYED = 3 ORDER_STATUS_PAYED = 3
ORDER_STATUS_FAILED = 4 ORDER_STATUS_FAILED = 4
ORDER_STATUS_CLOSE = 5 ORDER_STATUS_CLOSE = 5
ORDER_STATUS_REFUND = 6
// 订单类型1支付2退款 // 订单类型1支付2退款
ORDER_TYPE_PAY = 1 ORDER_TYPE_PAY = 1

View File

@ -47,7 +47,9 @@ const (
AppSM4DecryptFail = 1231 AppSM4DecryptFail = 1231
AppSM4EncryptKeyNotFound = 1232 AppSM4EncryptKeyNotFound = 1232
AppSM4EncryptFail = 1233 AppSM4EncryptFail = 1233
AppAesEncryptFail = 1240 AppAesEncryptFail = 1234
// 加密方式不存在
EncryptTypeNotFound = 1241
//渠道 //渠道
PayChannelNotFound = 1300 PayChannelNotFound = 1300
@ -71,8 +73,9 @@ const (
//回调 //回调
NotifySendFail = 1600 NotifySendFail = 1600
//预支付 //订单结果
PrePayFail = 1701 PrePayFail = 1701
PreRefundFail = 1702
) )
var MsgEN = map[int]string{ var MsgEN = map[int]string{
@ -116,6 +119,8 @@ var MsgZH = map[int]string{
AppAesEncryptFail: "aes 加密失败", AppAesEncryptFail: "aes 加密失败",
EncryptTypeNotFound: "加密方式不存在",
PayChannelNotFound: "支付方式不存在", PayChannelNotFound: "支付方式不存在",
PayChannelNotBuild: "支付方式尚未开通", PayChannelNotBuild: "支付方式尚未开通",
PayChannelExtJsonError: "支付方式扩展参数错误", PayChannelExtJsonError: "支付方式扩展参数错误",

View File

@ -7,7 +7,9 @@ import (
"PaymentCenter/app/http/entities/backend" "PaymentCenter/app/http/entities/backend"
"PaymentCenter/app/models/appmodel" "PaymentCenter/app/models/appmodel"
"PaymentCenter/app/services" "PaymentCenter/app/services"
"PaymentCenter/app/utils/encrypt/rsa"
"PaymentCenter/app/utils/encrypt/sm2" "PaymentCenter/app/utils/encrypt/sm2"
"PaymentCenter/app/utils/encrypt/sm4"
"github.com/ahmetb/go-linq/v3" "github.com/ahmetb/go-linq/v3"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -60,12 +62,19 @@ func GenerateDecrypt(c *gin.Context) {
var publicKey, privateKey string var publicKey, privateKey string
var err error var err error
switch req.KeyType { switch req.KeyType {
default: case "sm2":
publicKey, privateKey, err = sm2.GenerateSM2Key() publicKey, privateKey, err = sm2.GenerateSM2Key()
if err != nil { case "rsa":
controllers.Error(c, errorcode.SystemError, err.Error()) publicKey, privateKey, err = rsa.GenerateKey()
return case "sm4":
} privateKey, publicKey = sm4.GenerateKey()
default:
controllers.HandCodeRes(c, "", errorcode.EncryptTypeNotFound)
return
}
if err != nil {
controllers.Error(c, errorcode.SystemError, err.Error())
return
} }
controllers.HandCodeRes(c, map[string]string{ controllers.HandCodeRes(c, map[string]string{

View File

@ -21,17 +21,17 @@ func PayUrl(c *gin.Context) {
controllers.ApiRes(c, nil, check.CheckCode) controllers.ApiRes(c, nil, check.CheckCode)
return return
} }
check.CheckOrder() check.CheckOrderPay()
if check.CheckCode != errorcode.Success { if check.CheckCode != errorcode.Success {
controllers.ApiRes(c, nil, check.CheckCode) controllers.ApiRes(c, nil, check.CheckCode)
return return
} }
pay := thirdpay.ThirdPayWeb(check) pay := thirdpay.ThirdPayUrl(check)
if pay.PayCode != errorcode.Success { if pay.PayCode != errorcode.Success {
controllers.ApiRes(c, nil, pay.PayCode) controllers.ApiRes(c, nil, pay.PayCode)
return return
} }
data := api.NewOrdersResp(pay.Order) data := thirdpay.NewOrdersResp(pay.Order)
encryptData, errCode := api.EnCrypt(appCheckInfo.App, data) encryptData, errCode := api.EnCrypt(appCheckInfo.App, data)
if errCode != errorcode.Success { if errCode != errorcode.Success {
@ -44,6 +44,24 @@ func PayUrl(c *gin.Context) {
return return
} }
func Refund(c *gin.Context) {
req := controllers.GetRequest(c).(*front.RefundReqs)
appCheckInfo := controllers.GetAppCheckInfo(c).(*services.AppCheck)
refund := thirdpay.ThirdPayRefund(c.Request.Context(), req, appCheckInfo, c.ClientIP())
if refund.PayCode != errorcode.Success {
controllers.ApiRes(c, nil, refund.PayCode)
}
data := thirdpay.NewOrdersResp(refund.Order)
encryptData, errCode := api.EnCrypt(appCheckInfo.App, data)
if errCode != errorcode.Success {
controllers.ApiRes(c, nil, refund.PayCode)
return
}
controllers.ApiRes(c, encryptData, refund.PayCode)
return
}
// 查询订单 // 查询订单
func QueryOrder(c *gin.Context) { func QueryOrder(c *gin.Context) {
req := controllers.GetRequest(c).(*front.QueryReqs) req := controllers.GetRequest(c).(*front.QueryReqs)
@ -62,7 +80,7 @@ func QueryOrder(c *gin.Context) {
controllers.ApiRes(c, nil, code) controllers.ApiRes(c, nil, code)
return return
} }
data := api.NewOrdersResp(&order) data := thirdpay.NewOrdersResp(&order)
encryptData, errCode := api.EnCrypt(appCheckInfo.App, data) encryptData, errCode := api.EnCrypt(appCheckInfo.App, data)
if errCode != errorcode.Success { if errCode != errorcode.Success {
controllers.ApiRes(c, nil, errCode) controllers.ApiRes(c, nil, errCode)

View File

@ -65,6 +65,8 @@ func WxCallback(c *gin.Context) {
return return
} }
notifyReqJson, _ := json.Marshal(notifyReq)
logger.Info(c, "WxCallback-解析微信回调请求的参数到 V3NotifyReq 结构体", string(notifyReqJson))
err = paymentService.WxPayCallBack(notifyReq, wxConfig) err = paymentService.WxPayCallBack(notifyReq, wxConfig)
if err != nil { if err != nil {
logger.Error(c, "WxCallback-回调执行失败,失败原因:", err.Error()) logger.Error(c, "WxCallback-回调执行失败,失败原因:", err.Error())
@ -72,9 +74,6 @@ func WxCallback(c *gin.Context) {
return return
} }
// ====↓↓↓====异步通知应答====↓↓↓====
// 退款通知http应答码为200且返回状态码为SUCCESS才会当做商户接收成功否则会重试。
// 注意:重试过多会导致微信支付端积压过多通知而堵塞,影响其他正常通知。
c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"}) c.JSON(http.StatusOK, &wechat.V3NotifyRsp{Code: gopay.SUCCESS, Message: "成功"})
return return
} }
@ -105,34 +104,27 @@ func AliCallback(c *gin.Context) {
} }
var aliConfig paymentService.AliPay var aliConfig paymentService.AliPay
var aliConfigModel struct { err := json.Unmarshal([]byte(payChannelModel.ExtJson), &aliConfig)
PrivateKey string `json:"private_key"` // 应用私钥
AppPublicCert string `json:"app_public_cert"` // 应用公钥
AlipayRootCert string `json:"alipay_root_cert"` // 支付宝根证书
AlipayPublicCert string `json:"alipay_public_cert"` // 支付宝公钥
}
err := json.Unmarshal([]byte(payChannelModel.ExtJson), &aliConfigModel)
if err != nil { if err != nil {
logger.Error(c, "AliCallback-回调数据解析支付配置错误", fmt.Sprintf("错误原因:%s", err.Error())) logger.Error(c, "AliCallback-回调数据解析支付配置错误", fmt.Sprintf("错误原因:%s", err.Error()))
c.String(http.StatusBadRequest, "%s", "fail") c.String(http.StatusBadRequest, "%s", "fail")
return return
} }
if aliConfigModel.AlipayPublicCert == "" || aliConfigModel.PrivateKey == "" || aliConfigModel.AppPublicCert == "" || aliConfigModel.AlipayRootCert == "" { if aliConfig.AlipayPublicCert == "" || aliConfig.PrivateKey == "" || aliConfig.AppPublicCert == "" || aliConfig.AlipayRootCert == "" {
logger.Error(c, "AliCallback-回调数据解析支付配置错误,解析出来的信息为空") logger.Error(c, "AliCallback-回调数据解析支付配置错误,解析出来的信息为空")
c.String(http.StatusBadRequest, "%s", "fail") c.String(http.StatusBadRequest, "%s", "fail")
return return
} }
aliConfig.AppId = payChannelModel.AppId aliConfig.AppId = payChannelModel.AppId
aliConfig.PrivateKey = aliConfigModel.PrivateKey
aliConfig.AppPublicCert = aliConfigModel.AppPublicCert
aliConfig.AlipayRootCert = aliConfigModel.AlipayRootCert
aliConfig.AlipayPublicCert = aliConfigModel.AlipayPublicCert
notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法 notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request) // c.Request 是 gin 框架的写法
if err != nil { if err != nil {
c.String(http.StatusBadRequest, "%s", "fail") c.String(http.StatusBadRequest, "%s", "fail")
return return
} }
notifyReqJson, _ := json.Marshal(notifyReq)
logger.Info(c, "AliCallback-解析支付宝支付异步通知的参数到BodyMap", string(notifyReqJson))
err = paymentService.ALiCallBack(notifyReq, aliConfig) err = paymentService.ALiCallBack(notifyReq, aliConfig)
if err != nil { if err != nil {
logger.Error(c, "AliCallback-回调执行失败,失败原因:", err.Error()) logger.Error(c, "AliCallback-回调执行失败,失败原因:", err.Error())

View File

@ -96,5 +96,5 @@ func (a *AppUpdateRequest) RequestToDb() (db appmodel.App) {
} }
type GenerateDecryptKeyRequest struct { type GenerateDecryptKeyRequest struct {
KeyType int `json:"key_type" label:"密钥类型"` KeyType string `json:"key_type" form:"key_type" label:"密钥类型"`
} }

View File

@ -12,16 +12,25 @@ type RequestBody struct {
Key string `json:"key" validate:"max=32"` Key string `json:"key" validate:"max=32"`
} }
type PayReqs struct { type PayCommonReqBody struct {
ApiCommonBody ApiCommonBody
PayChannelId int64 `json:"pay_channel_id" validate:"required" label:"支付渠道"` PayChannelId int64 `json:"pay_channel_id" validate:"required" label:"支付渠道"`
OutTradeNo string `json:"out_trade_no" validate:"required" label:"外侧商户订单号"`
OrderType int `json:"order_type" validate:"required" label:"订单类型,支付,退款"`
Amount int `json:"amount" validate:"required" label:"支付金额,单位分"` Amount int `json:"amount" validate:"required" label:"支付金额,单位分"`
ExtJson string `json:"ext_json" label:"扩展参数"` ExtJson string `json:"ext_json" label:"扩展参数"`
Desc string `json:"desc" validate:"max=100" label:"商品描述"` Desc string `json:"desc" validate:"max=100" label:"商品描述"`
} }
type PayReqs struct {
PayCommonReqBody
OutTradeNo string `json:"out_trade_no" validate:"required" label:"外侧商户订单号"`
}
type RefundReqs struct {
PayCommonReqBody
OutTradeNo string `json:"out_trade_no" label:"外侧商户订单号"`
OrderId string `json:"order_id" label:"平台订单号"`
}
type PayUrlResp struct { type PayUrlResp struct {
Order string `json:"order"` Order string `json:"order"`
Url string `json:"url"` Url string `json:"url"`

View File

@ -149,7 +149,6 @@ func ValidatePayRequest() gin.HandlerFunc {
//获取app信息 //获取app信息
appCheck := services.GetAppCheck(requestDataStruct.AppId, c.ClientIP()) appCheck := services.GetAppCheck(requestDataStruct.AppId, c.ClientIP())
//存入请求记录 //存入请求记录
if appCheck.Code != errorcode.Success { if appCheck.Code != errorcode.Success {
controllers.ApiRes(c, nil, appCheck.Code) controllers.ApiRes(c, nil, appCheck.Code)
return return

View File

@ -6,7 +6,8 @@ import (
) )
var FrontRequestMap = map[string]func() interface{}{ var FrontRequestMap = map[string]func() interface{}{
common.FRONT_V1 + "/pay/url": func() interface{} { return new(front.PayReqs) }, common.FRONT_V1 + "/pay/url": func() interface{} { return new(front.PayReqs) },
common.FRONT_V1 + "/pay/refund": func() interface{} { return new(front.RefundReqs) },
} }
var FrontRequestMapBeforeDecrypt = map[string]func() interface{}{ var FrontRequestMapBeforeDecrypt = map[string]func() interface{}{

View File

@ -60,6 +60,7 @@ func RegisterRoute(router *gin.Engine) {
{ {
pay.POST("/url", front.PayUrl) pay.POST("/url", front.PayUrl)
pay.POST("/query", front.QueryOrder) //查询订单 pay.POST("/query", front.QueryOrder) //查询订单
pay.POST("/refund", front.Refund)
} }
} }

View File

@ -13,20 +13,21 @@ var (
// 实体 // 实体
type Orders struct { type Orders struct {
Id int64 Id int64
MerchantId int64 `xorm:"'merchant_id' bigint(20)"` MerchantId int64 `xorm:"'merchant_id' bigint(20)"`
PayChannelId int64 `xorm:"'pay_channel_id' bigint(20)"` PayChannelId int64 `xorm:"'pay_channel_id' bigint(20)"`
AppId int64 `xorm:"'app_id' bigint(20)"` AppId int64 `xorm:"'app_id' bigint(20)"`
OutTreadNo string `xorm:"'out_tread_no' varchar(50)"` OutTreadNo string `xorm:"'out_tread_no' varchar(50)"`
OrderType int `xorm:"'order_type' TINYINT"` OrderType int `xorm:"'order_type' TINYINT"`
Amount int `xorm:"'amount' int(11)"` RefundOrderId int64 `xorm:"'refund_order_id' bigint(20)"`
PayerTotal int `xorm:"'payer_total' int(11)"` Amount int `xorm:"'amount' int(11)"`
ExtJson string `xorm:"'ext_json' varchar(1024)"` PayerTotal int `xorm:"'payer_total' int(11)"`
Desc string `xorm:"'desc' varchar(100)"` ExtJson string `xorm:"'ext_json' varchar(1024)"`
CreateTime time.Time `xorm:"'create_time' datetime created"` Desc string `xorm:"'desc' varchar(100)"`
UpdateTime time.Time `xorm:"'update_time' timestamp updated"` CreateTime time.Time `xorm:"'create_time' datetime created"`
Status int `xorm:"'status' TINYINT"` UpdateTime time.Time `xorm:"'update_time' timestamp updated"`
DeleteTime time.Time `xorm:"'delete_time' timestamp deleted"` Status int `xorm:"'status' TINYINT"`
DeleteTime time.Time `xorm:"'delete_time' timestamp deleted"`
} }
type OrdersBackendList struct { type OrdersBackendList struct {

View File

@ -18,7 +18,7 @@ func (r *SM2) Encrypt(data []byte) (encryptData []byte, errCode int) {
return nil, errorcode.AppSM2EncryptKeyNotFound return nil, errorcode.AppSM2EncryptKeyNotFound
} }
encryptDataString, err := sm2.SM2Encrypt(string(data), r.App.PrivateKey) encryptDataString, err := sm2.SM2Encrypt(string(data), r.App.MerchantPublicKey)
if err != nil { if err != nil {
return nil, errorcode.AppSM2EncryptFail return nil, errorcode.AppSM2EncryptFail
} }

View File

@ -105,6 +105,7 @@ func CheckApp(appCheckIn *AppCheck) {
appCheckIn.App, errCode = AppFindOne(entities.IdRequest{Id: appCheckIn.AppId}) appCheckIn.App, errCode = AppFindOne(entities.IdRequest{Id: appCheckIn.AppId})
if errCode != errorcode.Success { if errCode != errorcode.Success {
appCheckIn.Code = errCode appCheckIn.Code = errCode
return
} }
//检查app可用性 //检查app可用性
appCheckIn.Check() appCheckIn.Check()

View File

@ -100,7 +100,7 @@ func PayChannelFindOne(channel *paychannelmodel.PayChannel, conn builder.Cond, c
channelInfo, err := repo.PayChannelFindOne(channel, conn, col...) channelInfo, err := repo.PayChannelFindOne(channel, conn, col...)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, errorcode.MerchantNotFound return nil, errorcode.PayChannelNotFound
} }
return nil, errorcode.SystemError return nil, errorcode.SystemError
} }

View File

@ -1,9 +1,5 @@
package api package api
import (
"PaymentCenter/app/models/ordersmodel"
)
type OrdersResp struct { type OrdersResp struct {
OrderNo int64 `json:"order_no"` OrderNo int64 `json:"order_no"`
OrderType int `json:"order_type"` OrderType int `json:"order_type"`
@ -20,18 +16,6 @@ type OrdersWithUrl struct {
Url string Url string
} }
func NewOrdersResp(db *ordersmodel.Orders) *OrdersResp {
return &OrdersResp{
OrderNo: db.Id,
OutTreadNo: db.OutTreadNo,
Status: db.Status,
OrderType: db.OrderType,
Amount: db.Amount,
Desc: db.Desc,
CreateTime: db.CreateTime.Format("2006-01-02 15:04:05"),
}
}
func (o *OrdersResp) WithUrl(url string) *OrdersWithUrl { func (o *OrdersResp) WithUrl(url string) *OrdersWithUrl {
return &OrdersWithUrl{ return &OrdersWithUrl{
Order: o, Order: o,

View File

@ -3,6 +3,7 @@ package do
import ( import (
"PaymentCenter/app/constants/common" "PaymentCenter/app/constants/common"
"PaymentCenter/app/constants/errorcode" "PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/models/merchantmodel"
"PaymentCenter/app/models/paychannelmodel" "PaymentCenter/app/models/paychannelmodel"
"PaymentCenter/app/services" "PaymentCenter/app/services"
"PaymentCenter/app/third/paymentService/payCommon" "PaymentCenter/app/third/paymentService/payCommon"
@ -12,6 +13,9 @@ import (
) )
type Pay struct { type Pay struct {
Merchant *merchantmodel.Merchant
Channel *paychannelmodel.PayChannel
paycheck *PayCheck paycheck *PayCheck
Order *ordersmodel.Orders Order *ordersmodel.Orders
PayCode int PayCode int
@ -25,16 +29,20 @@ func NewPay(paycheck *PayCheck) *Pay {
} }
} }
func (w *Pay) CreateOrder() { func (w *Pay) CreateOrder(order_type int) {
if _, ok := common.OrderTypeList[order_type]; !ok { //判断是否是支持的支付渠道
w.PayCode = errorcode.PayChannelNotFound
return
}
w.Order, w.PayCode = services.OrderCreate(&ordersmodel.Orders{ w.Order, w.PayCode = services.OrderCreate(&ordersmodel.Orders{
MerchantId: w.paycheck.Merchant.Id, MerchantId: w.paycheck.Merchant.Id,
PayChannelId: w.paycheck.Channel.Id, PayChannelId: w.paycheck.Channel.Id,
AppId: w.paycheck.Channel.Id, AppId: w.paycheck.Channel.Id,
OutTreadNo: w.paycheck.WebPayReqs.OutTradeNo, OutTreadNo: w.paycheck.Reqs.OutTradeNo,
OrderType: w.paycheck.WebPayReqs.OrderType, OrderType: order_type,
Amount: w.paycheck.WebPayReqs.Amount, Amount: w.paycheck.Reqs.Amount,
ExtJson: w.paycheck.WebPayReqs.ExtJson, ExtJson: w.paycheck.Reqs.ExtJson,
Desc: w.paycheck.WebPayReqs.Desc, Desc: w.paycheck.Reqs.Desc,
Status: common.ORDER_STATUS_WAITPAY, Status: common.ORDER_STATUS_WAITPAY,
}, },
) )
@ -46,7 +54,7 @@ func (w *Pay) PayUrl() (url string) {
ok bool ok bool
) )
thirdPay := &paymentService.PayOrderRequest{ thirdPay := &paymentService.PayOrderRequest{
PayChannelId: w.paycheck.WebPayReqs.PayChannelId, PayChannelId: w.paycheck.Reqs.PayChannelId,
OrderId: w.Order.Id, OrderId: w.Order.Id,
ChannelType: w.paycheck.Channel.ChannelType, ChannelType: w.paycheck.Channel.ChannelType,
Description: w.Order.Desc, Description: w.Order.Desc,
@ -77,3 +85,37 @@ func (w *Pay) PayUrl() (url string) {
return return
} }
func (w *Pay) Refund() {
var (
refundFunc func(commonRefundInfo *paymentService.OrderRefundRequest, channel *paychannelmodel.PayChannel) error
ok bool
)
thirdPayRefund := &paymentService.OrderRefundRequest{
OrderId: w.Order.Id,
RefundOrderId: w.paycheck.OldOrder.Id,
RefundReason: w.paycheck.Reqs.Desc,
RefundAmount: int64(w.paycheck.Reqs.Amount),
PayChannel: w.paycheck.Channel.ChannelType,
}
if refundFunc, ok = RefundWayList[w.paycheck.Channel.ChannelType]; !ok {
w.PayCode = errorcode.PayChannelNotBuild
return
}
err := refundFunc(thirdPayRefund, w.paycheck.Channel)
if err != nil {
w.PayCode = errorcode.PayChannelExtJsonError
return
}
res := paymentService.OrderRefund(*w.paycheck.ctx, *thirdPayRefund)
if res.Code == payCommon.PAY_SUCCESS_CODE {
w.Order.Status = common.ORDER_STATUS_PAYING
code := services.OrderUpdate(w.Order, "status")
if code != errorcode.Success {
w.PayCode = code
}
} else {
w.PayCode = errorcode.PreRefundFail
}
return
}

View File

@ -4,9 +4,9 @@ import (
"PaymentCenter/app/constants/common" "PaymentCenter/app/constants/common"
"PaymentCenter/app/constants/errorcode" "PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/services" "PaymentCenter/app/services"
"PaymentCenter/app/services/thirdpay/types"
"xorm.io/builder" "xorm.io/builder"
"PaymentCenter/app/http/entities/front"
"PaymentCenter/app/models/merchantmodel" "PaymentCenter/app/models/merchantmodel"
"PaymentCenter/app/models/orderrequestlogmodel" "PaymentCenter/app/models/orderrequestlogmodel"
"PaymentCenter/app/models/ordersmodel" "PaymentCenter/app/models/ordersmodel"
@ -16,7 +16,7 @@ import (
type PayCheck struct { type PayCheck struct {
ctx *context.Context ctx *context.Context
WebPayReqs *front.PayReqs Reqs *types.Reqs
AppCheck *services.AppCheck AppCheck *services.AppCheck
RequestLog *orderrequestlogmodel.OrderRequestLog RequestLog *orderrequestlogmodel.OrderRequestLog
Channel *paychannelmodel.PayChannel Channel *paychannelmodel.PayChannel
@ -25,29 +25,32 @@ type PayCheck struct {
CheckCode int CheckCode int
} }
func NewPayCheck(ctx *context.Context, reqs *front.PayReqs, appCheck *services.AppCheck, ip string) *PayCheck { func NewPayCheck(ctx *context.Context, reqs *types.Reqs, appCheck *services.AppCheck, ip string) *PayCheck {
if appCheck == nil { if appCheck == nil {
appCheck = services.GetAppCheck(reqs.AppId, ip) appCheck = services.GetAppCheck(reqs.AppId, ip)
} }
return &PayCheck{ return &PayCheck{
ctx: ctx, ctx: ctx,
WebPayReqs: reqs, Reqs: reqs,
AppCheck: appCheck, AppCheck: appCheck,
CheckCode: appCheck.Code, CheckCode: appCheck.Code,
} }
} }
func (w *PayCheck) CheckForm() { func (w *PayCheck) CheckPayInfo() {
if _, ok := common.OrderTypeList[w.WebPayReqs.OrderType]; !ok { //判断是否是支持的支付渠道 w.CheckMerchant()
w.CheckCode = errorcode.PayChannelNotFound if w.CheckCode != errorcode.Success {
return
}
w.CheckPayChannel()
if w.CheckCode != errorcode.Success {
return return
} }
} }
func (w *PayCheck) CheckPayChannel() { func (w *PayCheck) CheckPayChannel() {
conn := builder.NewCond() conn := builder.NewCond()
conn = conn.And(builder.Eq{"id": w.WebPayReqs.PayChannelId}) conn = conn.And(builder.Eq{"id": w.Reqs.PayChannelId})
w.Channel, w.CheckCode = services.GetAndCheckPayChannel(&paychannelmodel.PayChannel{}, conn) w.Channel, w.CheckCode = services.GetAndCheckPayChannel(&paychannelmodel.PayChannel{}, conn)
} }
@ -57,9 +60,45 @@ func (w *PayCheck) CheckMerchant() {
w.Merchant, w.CheckCode = services.GetAndCheckMerchant(&merchantmodel.Merchant{}, conn) w.Merchant, w.CheckCode = services.GetAndCheckMerchant(&merchantmodel.Merchant{}, conn)
} }
func (w *PayCheck) CheckOrder() { func (w *PayCheck) CheckOrderPay() {
w.GetOrder()
if w.OldOrder != nil {
switch w.OldOrder.Status {
case common.ORDER_STATUS_CLOSE:
w.CheckCode = errorcode.OrderClosed
case common.ORDER_STATUS_FAILED:
w.CheckCode = errorcode.OrderFailed
case common.ORDER_STATUS_PAYED:
w.CheckCode = errorcode.OrderPayed
default:
}
}
return
}
func (w *PayCheck) CheckOrderRefund() {
w.GetOrder()
if w.OldOrder == nil {
w.CheckCode = errorcode.OrdersNotFound
}
switch w.OldOrder.Status {
case common.ORDER_STATUS_CLOSE:
w.CheckCode = errorcode.OrderClosed
case common.ORDER_STATUS_FAILED:
w.CheckCode = errorcode.OrderFailed
case common.ORDER_STATUS_WAITPAY:
w.CheckCode = errorcode.OrderStatusErr
case common.ORDER_STATUS_PAYING:
w.CheckCode = errorcode.OrderStatusErr
default:
}
return
}
func (w *PayCheck) GetOrder() {
cond := builder.NewCond() cond := builder.NewCond()
cond = cond.And(builder.Eq{"out_tread_no": w.WebPayReqs.OutTradeNo}, builder.Eq{"app_id": w.AppCheck.AppId}) cond = cond.And(builder.Eq{"out_tread_no": w.Reqs.OutTradeNo}, builder.Eq{"app_id": w.AppCheck.AppId})
order, code := services.OrderFindOne(&ordersmodel.Orders{}, cond) order, code := services.OrderFindOne(&ordersmodel.Orders{}, cond)
if code == errorcode.SystemError { if code == errorcode.SystemError {
w.CheckCode = code w.CheckCode = code
@ -67,15 +106,6 @@ func (w *PayCheck) CheckOrder() {
} }
if code == errorcode.OrdersExist { if code == errorcode.OrdersExist {
w.OldOrder = order w.OldOrder = order
switch order.Status {
case common.ORDER_STATUS_CLOSE:
w.CheckCode = errorcode.OrderClosed
case common.ORDER_STATUS_FAILED:
w.CheckCode = errorcode.OrderFailed
case common.ORDER_STATUS_PAYED:
w.CheckCode = errorcode.OrderPayed
}
} }
return
} }

View File

@ -0,0 +1,31 @@
package do
import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/models/paychannelmodel"
"PaymentCenter/app/third/paymentService"
"github.com/bytedance/sonic"
)
var RefundWayList = map[int]func(commonRefundInfo *paymentService.OrderRefundRequest, channel *paychannelmodel.PayChannel) error{
common.PAY_CHANNEL_WECHAT_H5: WechatH5Refund,
common.PAY_CHANNEL_ALIPAY_WEB: AlipayWebRefund,
}
func WechatH5Refund(commonRefundInfo *paymentService.OrderRefundRequest, channel *paychannelmodel.PayChannel) error {
err := sonic.Unmarshal([]byte(channel.ExtJson), &commonRefundInfo.Wx)
if err != nil {
return err
}
commonRefundInfo.Wx.AppId = channel.AppId
return nil
}
func AlipayWebRefund(commonRefundInfo *paymentService.OrderRefundRequest, channel *paychannelmodel.PayChannel) error {
err := sonic.Unmarshal([]byte(channel.ExtJson), &commonRefundInfo.Ali)
if err != nil {
return err
}
commonRefundInfo.Ali.AppId = channel.AppId
return nil
}

View File

@ -1,47 +1,79 @@
package thirdpay package thirdpay
import ( import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/constants/errorcode" "PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/http/entities/front" "PaymentCenter/app/http/entities/front"
"PaymentCenter/app/models/ordersmodel"
"PaymentCenter/app/services" "PaymentCenter/app/services"
"PaymentCenter/app/services/thirdpay/api"
thirdpay "PaymentCenter/app/services/thirdpay/do" thirdpay "PaymentCenter/app/services/thirdpay/do"
"PaymentCenter/app/services/thirdpay/types"
"context" "context"
"github.com/jinzhu/copier"
) )
func ThirdPayWeb(check *thirdpay.PayCheck) *thirdpay.Pay { func ThirdPayUrl(check *thirdpay.PayCheck) *thirdpay.Pay {
pay := thirdpay.NewPay(check) pay := ThirdPay(check)
// 创建订单
if &check.OldOrder != nil {
pay.CreateOrder()
if pay.PayCode != errorcode.Success {
return pay
}
} else {
pay.Order = check.OldOrder
}
// 支付 // 支付
pay.PayUrl() pay.PayUrl()
return pay return pay
} }
func ThirdPayInfoCheck(ctx context.Context, req *front.PayReqs, appCheck *services.AppCheck, ip string) (check *thirdpay.PayCheck) { func NewOrdersResp(db *ordersmodel.Orders) *api.OrdersResp {
check = thirdpay.NewPayCheck(&ctx, req, appCheck, ip) return &api.OrdersResp{
// 校验表单 OrderNo: db.Id,
check.CheckForm() OutTreadNo: db.OutTreadNo,
if check.CheckCode != errorcode.Success { Status: db.Status,
return OrderType: db.OrderType,
Amount: db.Amount,
Desc: db.Desc,
CreateTime: db.CreateTime.Format("2006-01-02 15:04:05"),
} }
}
// 校验商户 func ThirdPayInfoCheck(ctx context.Context, payReq *front.PayReqs, appCheck *services.AppCheck, ip string) (check *thirdpay.PayCheck) {
check.CheckMerchant() var req types.Reqs
if check.CheckCode != errorcode.Success { copier.Copy(&req, payReq)
return check = thirdpay.NewPayCheck(&ctx, &req, appCheck, ip)
} // 校验表单
// 校验支付通道 check.CheckPayInfo()
check.CheckPayChannel()
if check.CheckCode != errorcode.Success { if check.CheckCode != errorcode.Success {
return return
} }
return return
} }
func ThirdPayRefund(ctx context.Context, refundReq *front.RefundReqs, appCheck *services.AppCheck, ip string) (refund *thirdpay.Pay) {
var req types.Reqs
copier.Copy(req, refundReq)
check := thirdpay.NewPayCheck(&ctx, &req, appCheck, ip)
// 校验表单
check.CheckPayInfo()
if check.CheckCode != errorcode.Success {
return
}
check.CheckOrderRefund()
if check.CheckCode != errorcode.Success {
return
}
refund = thirdpay.NewPay(check)
refund.CreateOrder(common.ORDER_TYPE_REFUND)
refund.Refund()
return
}
func ThirdPay(check *thirdpay.PayCheck) (pay *thirdpay.Pay) {
pay = thirdpay.NewPay(check)
// 创建订单
if &check.OldOrder != nil {
pay.CreateOrder(common.ORDER_TYPE_PAY)
if pay.PayCode != errorcode.Success {
return pay
}
} else {
pay.Order = check.OldOrder
}
return
}

View File

@ -1,4 +1,4 @@
package notify package thirdpay_notify
import ( import (
"PaymentCenter/app/constants/common" "PaymentCenter/app/constants/common"
@ -15,7 +15,7 @@ import (
type OrderNotify struct { type OrderNotify struct {
order *ordersmodel.Orders order *ordersmodel.Orders
code int Code int
app *appmodel.App app *appmodel.App
OrderId int64 OrderId int64
@ -52,9 +52,9 @@ func NewOrderNotifyWithHandle(orderId int64, Status int, actualAmount int, msg s
Status: Status, Status: Status,
ActualAmount: actualAmount, ActualAmount: actualAmount,
Msg: msg, Msg: msg,
code: errorcode.Success, Code: errorcode.Success,
} }
return orderNotify.handle() return orderNotify.Handle()
} }
func (o *OrderNotify) NotifyRespFail(errorCode int) *OrderNotifyResp { func (o *OrderNotify) NotifyRespFail(errorCode int) *OrderNotifyResp {
@ -66,27 +66,27 @@ func (o *OrderNotify) NotifyRespFail(errorCode int) *OrderNotifyResp {
} }
} }
func (o *OrderNotify) handle() (res *OrderNotifyResp) { func (o *OrderNotify) Handle() (res *OrderNotifyResp) {
o.checkOrder() o.checkOrder()
if o.code != errorcode.Success { if o.Code != errorcode.Success {
return o.NotifyRespFail(o.code) return o.NotifyRespFail(o.Code)
} }
o.checkApp() o.checkApp()
if o.code != errorcode.Success { if o.Code != errorcode.Success {
return o.NotifyRespFail(o.code) return o.NotifyRespFail(o.Code)
} }
o.updateOrder() o.updateOrder()
if o.code != errorcode.Success { if o.Code != errorcode.Success {
return o.NotifyRespFail(o.code) return o.NotifyRespFail(o.Code)
} }
o.sendNotify(o.setBody()) o.sendNotify(o.setBody())
if o.code != errorcode.Success { if o.Code != errorcode.Success {
return o.NotifyRespFail(o.code) return o.NotifyRespFail(o.Code)
} }
return &OrderNotifyResp{ return &OrderNotifyResp{
OrderId: o.OrderId, OrderId: o.OrderId,
Send: true, Send: true,
ErrCode: o.code, ErrCode: o.Code,
Content: o, Content: o,
} }
} }
@ -98,7 +98,7 @@ func (o *OrderNotify) sendNotify(body *OrderNotifySendContent) {
headers["Content-Type"] = "application/json" headers["Content-Type"] = "application/json"
resByte, err := httpclient.FastHttpPost(o.app.NotifyUrl, headers, bodyByte, 0) resByte, err := httpclient.FastHttpPost(o.app.NotifyUrl, headers, bodyByte, 0)
if err != nil || string(resByte) != "success" { if err != nil || string(resByte) != "success" {
o.code = errorcode.NotifySendFail o.Code = errorcode.NotifySendFail
} }
return return
} }
@ -120,25 +120,25 @@ func (o *OrderNotify) setBody() *OrderNotifySendContent {
func (o *OrderNotify) updateOrder() { func (o *OrderNotify) updateOrder() {
if _, ok := common.OrderStatusMap[o.Status]; !ok { if _, ok := common.OrderStatusMap[o.Status]; !ok {
o.code = errorcode.OrderStatusErr o.Code = errorcode.OrderStatusErr
return return
} }
o.order.Status = o.Status o.order.Status = o.Status
o.code = services.OrderUpdate(o.order, "status") o.Code = services.OrderUpdate(o.order, "status")
return return
} }
func (o *OrderNotify) checkApp() { func (o *OrderNotify) checkApp() {
o.app, o.code = services.AppFindOne(entities.IdRequest{Id: o.order.AppId}) o.app, o.Code = services.AppFindOne(entities.IdRequest{Id: o.order.AppId})
if o.code != errorcode.Success { if o.Code != errorcode.Success {
return return
} }
if o.app.DeleteTime.IsZero() { if o.app.DeleteTime.IsZero() {
o.code = errorcode.AppDisabled o.Code = errorcode.AppDisabled
return return
} }
if o.app.NotifyUrl == "" { if o.app.NotifyUrl == "" {
o.code = errorcode.AppNotifyUrlNotFound o.Code = errorcode.AppNotifyUrlNotFound
return return
} }
@ -149,16 +149,16 @@ func (o *OrderNotify) checkOrder() {
cond := builder.NewCond() cond := builder.NewCond()
cond = cond.And(builder.Eq{"id": o.OrderId}) cond = cond.And(builder.Eq{"id": o.OrderId})
o.order, o.code = services.OrderFindOne(&ordersmodel.Orders{}, cond) o.order, o.Code = services.OrderFindOne(&ordersmodel.Orders{}, cond)
if o.code != errorcode.Success { if o.Code != errorcode.Success {
return return
} }
if o.order.DeleteTime.IsZero() { if o.order.DeleteTime.IsZero() {
o.code = errorcode.OrderIsDelete o.Code = errorcode.OrderIsDelete
return return
} }
if o.order.Status != common.ORDER_STATUS_PAYING { if o.order.Status != common.ORDER_STATUS_PAYING {
o.code = errorcode.OrderStatusErr o.Code = errorcode.OrderStatusErr
return return
} }
return return

View File

@ -1,10 +0,0 @@
package types
type PayResp struct {
OrderNo int64 `json:"order_no"`
OrderType int `json:"out_trade_no"`
Amount int `json:"order_type"`
Desc string `json:"amount"`
IsNewCreate bool `json:"is_new_create"`
Status int `json:"status"`
}

View File

@ -0,0 +1,8 @@
package types
import "PaymentCenter/app/http/entities/front"
type Reqs struct {
front.PayCommonReqBody
OutTradeNo string `json:"out_trade_no"`
}

View File

@ -1,164 +0,0 @@
package openapiService
import (
"PaymentCenter/app/models/orderdetailsmodel"
"PaymentCenter/app/models/ordersmodel"
"PaymentCenter/app/models/usercouponmodel"
"PaymentCenter/app/utils"
"PaymentCenter/config"
"context"
"crypto/aes"
"encoding/base64"
"gitee.com/chengdu_blue_brothers/openapi-go-sdk/api"
"gitee.com/chengdu_blue_brothers/openapi-go-sdk/notify"
"net/http"
"time"
)
func CreatClient() (client *api.Client, err error) {
merchantId := config.GetConf().OpenApi.MerchantId
secretKey := config.GetConf().OpenApi.SecretKey
isProd := config.GetConf().OpenApi.IsProd
timeout := 10 * time.Second // 请求超时时间
client, err = api.NewClient(merchantId, secretKey, isProd, timeout)
if err != nil {
return nil, err
}
return client, nil
}
type RechargeOrderReq struct {
OutTradeNo string
ProductId int
RechargeAccount string
AccountType int
Number int
}
func RechargeOrder(rechargeOrderReq RechargeOrderReq) (result *api.RechargeOrderResp, err error) {
req := &api.RechargeOrderReq{
OutTradeNo: rechargeOrderReq.OutTradeNo,
ProductId: rechargeOrderReq.ProductId,
RechargeAccount: rechargeOrderReq.RechargeAccount,
AccountType: 0,
Number: 1,
NotifyUrl: config.GetConf().OpenApi.NotifyUrl,
}
client, err := CreatClient()
if err != nil {
return nil, err
}
result, err = client.RechargeOrder(context.Background(), req)
if err != nil {
return nil, err
}
if result.Code != api.CodeCreateOrderSuccess {
return
}
return
}
func ReCardOrder(CardOrderReq api.CardOrderReq) (result *api.CardOrderResp, err error) {
req := &api.CardOrderReq{
OutTradeNo: CardOrderReq.OutTradeNo,
ProductId: CardOrderReq.ProductId,
AccountType: 0,
Number: 1,
NotifyUrl: config.GetConf().OpenApi.NotifyUrl,
}
client, err := CreatClient()
if err != nil {
return nil, err
}
result, err = client.CardOrder(context.Background(), req)
if err != nil {
return nil, err
}
if result.Code != api.CodeCreateOrderSuccess {
return
}
return
}
func DecryptCard(encCode string) (decode string, err error) {
defer func() error {
if r := recover(); r != nil {
utils.Log(nil, "解密错误", err)
}
return err
}()
client, err := CreatClient()
if err != nil {
return
}
decryptedCode, err := decryptAES(encCode, client.GetSecretKey())
if err != nil {
return
}
return decryptedCode, nil
}
// AES 解密
func decryptAES(encryptedData string, secretKey string) (string, error) {
// 第一步对加密的卡密做base64 decode
encryptedBytes, err := base64.StdEncoding.DecodeString(encryptedData)
if err != nil {
return "", err
}
// 第二步使用aes-256-ecb解密
cipher, _ := aes.NewCipher([]byte(secretKey))
decrypted := make([]byte, len(encryptedBytes))
size := 16
for bs, be := 0, size; bs < len(encryptedBytes); bs, be = bs+size, be+size {
cipher.Decrypt(decrypted[bs:be], encryptedBytes[bs:be])
}
paddingSize := int(decrypted[len(decrypted)-1])
return string(decrypted[0 : len(decrypted)-paddingSize]), nil
}
func ParseAndVerify(request *http.Request) (req *notify.OrderReq, err error) {
// 第一步初使化client实例
merchantId := config.GetConf().OpenApi.MerchantId
secretKey := config.GetConf().OpenApi.SecretKey
client := notify.NewNotify(merchantId, secretKey)
// 第二步:验签并解析结果
req, err = client.ParseAndVerify(request)
if err != nil {
return nil, err
}
return
}
// NotifyOperation /**
func NotifyOperation(order ordersmodel.Orders, req *notify.OrderReq) (err error) {
var session = ordersmodel.GetInstance().GetDb().NewSession()
session.Begin()
updateOrder := ordersmodel.Orders{Status: 3}
_, err = session.Where("Id = ?", order.Id).Update(&updateOrder)
if err != nil {
session.Rollback()
return
}
//卡密
if req.CardCode != "" {
//订单详情
updateOrderDetail := orderdetailsmodel.OrderDetails{Url: req.CardCode.Value()}
_, err = session.Where("OrderId = ?", order.Id).Update(&updateOrderDetail)
if err != nil {
session.Rollback()
return
}
userCouponDetail := usercouponmodel.UserCoupon{OrderInfo: req.CardCode.Value()}
_, err = session.Where("OrderId = ?", order.Id).Update(&userCouponDetail)
if err != nil {
session.Rollback()
return
}
}
session.Commit()
return
}

View File

@ -1,9 +1,13 @@
package paymentService package paymentService
import ( import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/services/thirdpay/thirdpay_notify"
"PaymentCenter/app/third/paymentService/payCommon" "PaymentCenter/app/third/paymentService/payCommon"
"PaymentCenter/config" "PaymentCenter/config"
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/go-pay/gopay" "github.com/go-pay/gopay"
@ -99,7 +103,43 @@ func ALiCallBack(notifyReq gopay.BodyMap, aliConfig AliPay) error {
return err return err
} }
// todo 拼装数据触发下游回调,数据待定 // 拼装数据触发下游回调,触发下游回调
orderId, _ := strconv.Atoi(notifyReq.Get("out_trade_no"))
payerTotal, _ := strconv.Atoi(notifyReq.Get("buyer_pay_amount"))
// 订单状态
tradeStatus := notifyReq.Get("trade_status")
errCode := 0
msg := ""
switch tradeStatus {
case "TRADE_CLOSED":
errCode = errorcode.ParamError
msg = "未付款交易超时关闭,或支付完成后全额退款。"
case "TRADE_SUCCESS":
errCode = errorcode.Success
msg = "交易支付成功。"
case "TRADE_FINISHED":
errCode = errorcode.Success
msg = "交易结束,不可退款。"
}
if errCode == 0 {
// 等待买家付款,不走后续回调
return errors.New("订单状态异常,无法进行后续回调")
}
res := thirdpay_notify.NewOrderNotifyWithHandle(int64(orderId), errCode, payerTotal, msg)
merchantCallback, _ := json.Marshal(res)
// 记录日志
go func() {
payCallback, _ := json.Marshal(notifyReq)
payParam := ""
saveLog(int64(orderId), common.THIRD_ORDER_TYPE_CALL_BACK, string(payCallback), payParam, string(merchantCallback))
}()
if res.ErrCode != errorcode.Success {
logger.Error(context.Background(), "ALiCallBack 发生错误", fmt.Sprintf("回调时,下游处理订单失败,返回数据为:%s", string(merchantCallback)))
return errors.New("回调时,下游处理订单失败")
}
return nil return nil
} }
@ -127,7 +167,7 @@ func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOr
return PayOrderQueryInfo{}, err return PayOrderQueryInfo{}, err
} }
// 同步返回验签 // 同步返回验签
ok, err := alipay.VerifySyncSignWithCert(aliConfig.AlipayPublicCert, aliRsp.SignData, aliRsp.Sign) ok, err := alipay.VerifySyncSignWithCert([]byte(aliConfig.AlipayPublicCert), aliRsp.SignData, aliRsp.Sign)
if err != nil || !ok { if err != nil || !ok {
return PayOrderQueryInfo{}, errors.New(fmt.Sprintf("验签失败,失败原因:%s", err.Error())) return PayOrderQueryInfo{}, errors.New(fmt.Sprintf("验签失败,失败原因:%s", err.Error()))
} }
@ -141,6 +181,7 @@ func ALiOrderQuery(ctx context.Context, aliConfig AliPay, OrderNo string) (PayOr
tradeStateDesc = "交易支付成功" tradeStateDesc = "交易支付成功"
case "REFUND": case "REFUND":
tradeState = "REFUND" tradeState = "REFUND"
tradeStateDesc = "转入退款"
case "WAIT_BUYER_PAY": case "WAIT_BUYER_PAY":
tradeState = "NOTPAY" tradeState = "NOTPAY"
tradeStateDesc = "交易创建,等待买家付款" tradeStateDesc = "交易创建,等待买家付款"

View File

@ -39,14 +39,6 @@ type AliPay struct {
AlipayPublicCert string `json:"alipay_public_cert"` // 支付宝公钥 AlipayPublicCert string `json:"alipay_public_cert"` // 支付宝公钥
} }
type AliPayA struct {
AppId string `json:"app_id"` // 应用ID
PrivateKey string `json:"private_key"` // 应用私钥
AppPublicCert string `json:"app_public_cert"` // 应用公钥
AlipayRootCert string `json:"alipay_root_cert"` // 支付宝根证书
AlipayPublicCert string `json:"alipay_public_cert"` // 支付宝公钥
}
type PayOrderResponse struct { type PayOrderResponse struct {
Code int `json:"code"` Code int `json:"code"`
ErrorMsg string `json:"error_msg"` ErrorMsg string `json:"error_msg"`

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,13 @@
package paymentService package paymentService
import ( import (
"PaymentCenter/app/constants/common"
"PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/services/thirdpay/thirdpay_notify"
"PaymentCenter/app/third/paymentService/payCommon" "PaymentCenter/app/third/paymentService/payCommon"
"PaymentCenter/config" "PaymentCenter/config"
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/go-pay/gopay" "github.com/go-pay/gopay"
@ -135,7 +139,40 @@ func WxPayCallBack(notifyReq *wechat.V3NotifyReq, wxConfig WxPay) error {
if err != nil { if err != nil {
return err return err
} }
// todo 返回触发下游回调的格式
errCode := 0
msg := ""
// 订单状态
switch CallBackInfo.TradeState {
case "SUCCESS":
errCode = errorcode.Success
msg = "支付成功"
case "CLOSED":
errCode = errorcode.ParamError
msg = "已关闭"
case "REVOKED":
errCode = errorcode.ParamError
msg = "已撤销(付款码支付)"
case "PAYERROR":
errCode = errorcode.ParamError
msg = "支付失败(其他原因,如银行返回失败)"
}
// 触发下游回调的格式
orderId, _ := strconv.Atoi(CallBackInfo.OutTradeNo)
res := thirdpay_notify.NewOrderNotifyWithHandle(int64(orderId), errCode, int(CallBackInfo.Amount.PayerTotal), msg)
merchantCallback, _ := json.Marshal(res)
// 记录日志
go func() {
payCallback, _ := json.Marshal(CallBackInfo)
payParam := ""
saveLog(int64(orderId), common.THIRD_ORDER_TYPE_CALL_BACK, string(payCallback), payParam, string(merchantCallback))
}()
if res.ErrCode != errorcode.Success {
logger.Error(context.Background(), "WxPayCallBack 发生错误", fmt.Sprintf("回调时,下游处理订单失败,返回数据为:%s", string(merchantCallback)))
return errors.New("回调时,下游处理订单失败")
}
return nil return nil
} }

View File

@ -108,3 +108,33 @@ func Decrypt(privateKeyPEM string, encryptedDataBase64 string) ([]byte, error) {
return decrypted, nil return decrypted, nil
} }
// 生成密钥对
func GenerateKey() (string, string, error) {
// 生成私钥
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return "", "", err
}
// 导出私钥PKCS#1格式
privKey := x509.MarshalPKCS1PrivateKey(privateKey)
// 将私钥转换为PEM编码
var privBlock = &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privKey,
}
privPem := pem.EncodeToMemory(privBlock)
// 导出公钥
pubKey := &privateKey.PublicKey
derPkix, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return "", "", err
}
// 将公钥转换为PEM编码
var pubBlock = &pem.Block{
Type: "PUBLIC KEY",
Bytes: derPkix,
}
pubPem := pem.EncodeToMemory(pubBlock)
return string(pubPem), string(privPem), nil
}

View File

@ -41,20 +41,6 @@ func encrypt() string {
return base64.StdEncoding.EncodeToString(en) return base64.StdEncoding.EncodeToString(en)
} }
func TestRsaEncryptWithAes(t *testing.T) {
fmt.Printf("%s\n", encryptWithAes())
}
func TestRsaDecryptWithAes(t *testing.T) {
data := "cGuoR6Bmmm9fZr/OAnM54eM6Z7M4ysnRr7TV64rIK6mAkGDzJROwSOY443e7UJmLpwIEn8G5jNk6j8K1scxvIMMdSJZ0QOJREjgzbZfNfuXV0LO1lVNu3uhwVQXN/zMZxBHGIcYQnPWSaOHhNy6yMRPLFNRuIb5FuTx7E6VI5UVZpHk9VLv63QX+6+hQMOiqoif/YyXkAqi2xG+unq4MVq9w5aYMOVzHX1eyMiTeRFRB4iKhf6bCJmVvLMmvjHYKQl3/225R4uXaI8nv7y4IHnK8KVYAnuJE6SvEPeJAjpINrY2CRoNqTYkt7DRS2uz5l7aE+KX8GShV2XlDoM8KZA=="
privateKeyPEM := `-----BEGIN RSA PRIVATE KEY-----
` + PRI + `
-----END RSA PRIVATE KEY-----`
res, err := Decrypt(privateKeyPEM, data)
fmt.Println(string(res), err)
}
func encryptWithAes() string { func encryptWithAes() string {
data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}" data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}"
aes.Encrypt([]byte(data), []byte(aes.TestKey)) aes.Encrypt([]byte(data), []byte(aes.TestKey))
@ -69,3 +55,17 @@ func encryptWithAes() string {
return base64.StdEncoding.EncodeToString(en) return base64.StdEncoding.EncodeToString(en)
} }
// 测试生成密钥对
func TestGenerateRSAKey(t *testing.T) {
pub, pri, err := GenerateKey()
data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}"
dataJson := []byte(data)
en, err := Encrypt(pub, dataJson)
if err != nil {
panic(err)
}
content := base64.StdEncoding.EncodeToString(en)
res, err := Decrypt(pri, content)
fmt.Println("解密", string(res), err)
}

View File

@ -6,7 +6,7 @@ import (
) )
const ( const (
SELF_PRI = "EA7CB6F907A96264D6763F8C46AB96B476538D2ABC880A459E10BE5A1C30013D" SELF_PRI = "BD13D44C74422F80ADDF54AD2DE2888C4092AF6F61E41FABADA6C47F17574A41"
SELF_PUB = "04363DF574D4FE34EE58FB8A3F7CB08E6CA5EBB3B7335CBAE10A2900551F6450AB3AD25DBC0A76EFA9E6D44D2C51E3027483F7BFD09996457888BAFD1AF673817F" SELF_PUB = "04363DF574D4FE34EE58FB8A3F7CB08E6CA5EBB3B7335CBAE10A2900551F6450AB3AD25DBC0A76EFA9E6D44D2C51E3027483F7BFD09996457888BAFD1AF673817F"
) )
@ -17,7 +17,20 @@ func TestGenerateSM2KeyPair(t *testing.T) {
// Print the private and public keys // Print the private and public keys
fmt.Printf("Private Key: %s\n", privateKey) fmt.Printf("Private Key: %s\n", privateKey)
fmt.Printf("Public Key: %s", publicKey) fmt.Printf("Public Key: %s\n", publicKey)
data := "{\"name\":\"张三\",\"sex\":1,\"is_human\":true}"
en, err := SM2Encrypt(data, publicKey)
if err != nil {
panic(err)
}
decrypt, err := SM2Decrypt(en, publicKey, privateKey)
if err != nil {
panic(err)
}
t.Log(decrypt)
} }
func TestSM2Encrypt(t *testing.T) { func TestSM2Encrypt(t *testing.T) {
@ -34,7 +47,7 @@ func TestSM2Decrypt(t *testing.T) {
} }
func encrypt() string { func encrypt() string {
data := "{\"name\":\"张三\",\"sex\":1,\"is_human\":true}" data := "{\"order_no\":5476377146882523228,\"order_type\":1,\"out_tread_no\":\"asdadasdas\",\"amount\":1,\"desc\":\"abc\",\"status\":2,\"create_time\":\"2024-08-08 14:47:35\"}"
en, err := SM2Encrypt(data, SELF_PUB) en, err := SM2Encrypt(data, SELF_PUB)
if err != nil { if err != nil {
panic(err) panic(err)

1
go.mod
View File

@ -82,6 +82,7 @@ require (
github.com/hetiansu5/accesslog v1.0.0 // indirect github.com/hetiansu5/accesslog v1.0.0 // indirect
github.com/hetiansu5/cores v1.0.0 // indirect github.com/hetiansu5/cores v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect

2
go.sum
View File

@ -443,6 +443,8 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=