diff --git a/app/console/command.go b/app/console/command.go index 6b36b6d..86c3659 100644 --- a/app/console/command.go +++ b/app/console/command.go @@ -60,7 +60,8 @@ func closeOrder() { PrivateKey: wx.PrivateKey, } case common.PAY_CHANNLE_TYPE_ZFB: - ali := backend.AliPayChannel{} + var ali backend.AliPayChannel + req.PayChannel = payCommon.PAY_CHANNLE_TYPE_ZFB _ = json.Unmarshal([]byte(orderInfo.ExtJson), &ali) req.Ali = paymentService.AliPay{ diff --git a/app/constants/common/common.go b/app/constants/common/common.go index dee65f3..a738cc4 100644 --- a/app/constants/common/common.go +++ b/app/constants/common/common.go @@ -29,6 +29,12 @@ const ( ORDER_STATUS_FAILED = 4 ORDER_STATUS_CLOSE = 5 + STATUS_ENABLE = 1 + STATUS_DISABLED = 2 + + ORDER_TYPE_PAY = 1 + ORDER_TYPE_REFUND = 2 + PAY_CHANNLE_TYPE_WECHAT = 1 // 支付类型: 微信 PAY_CHANNLE_TYPE_ZFB = 2 // 支付类型:支付宝 @@ -39,3 +45,19 @@ const ( THIRD_ORDER_TYPE_CLOSE = 5 // 关闭订单 THIRD_ORDER_TYPE_CALL_BACK = 6 // 支付回调 ) + +var PayChannelList = map[int]string{ + PAY_CHANNEL_WECHAT_JSAPI: "微信JSAPI", + PAY_CHANNEL_WECHAT_H5: "微信H5", + PAY_CHANNEL_WECHAT_APP: "微信app", + PAY_CHANNEL_WECHAT_NATIVE: "微信Native", + PAY_CHANNEL_WECHAT_MINI: "微信小程序", + PAY_CHANNEL_ALIPAY_WEB: "支付宝网页&移动应用", + PAY_CHANNEL_ALIPAY_MINI: "支付宝小程序", + PAY_CHANNEL_ALIPAY_JSAPI: "支付宝JSAPI", +} + +var OrderTypeList = map[int]string{ + ORDER_TYPE_PAY: "付款", + ORDER_TYPE_REFUND: "退款", +} diff --git a/app/constants/errorcode/error_code.go b/app/constants/errorcode/error_code.go index 2514e5c..2f1a82c 100644 --- a/app/constants/errorcode/error_code.go +++ b/app/constants/errorcode/error_code.go @@ -29,12 +29,44 @@ const ( MerchantNotFound = 1100 // app - AppNotFound = 1200 - AppDisabled = 1201 - AppIpNotAllow = 1202 + + AppNotFound = 1200 + AppDisabled = 1201 + AppIpNotAllow = 1202 + AppRsaDecryptKeyNotFound = 1203 + AppDecryptDataDiscrepancy = 1204 + AppNotifyUrlNotFound = 1205 + AppRsaDecryptFail = 1210 + AppRsaEncryptKeyNotFound = 1211 + AppRsaEncryptFail = 1212 + AppSM2DecryptKeyNotFound = 1220 + AppSM2DecryptFail = 1221 + AppSM2EncryptKeyNotFound = 1222 + AppSM2EncryptFail = 1223 + AppSM4DecryptKeyNotFound = 1230 + AppSM4DecryptFail = 1231 + AppSM4EncryptKeyNotFound = 1232 + AppSM4EncryptFail = 1233 //渠道 - PayChannelNotFound = 1300 + PayChannelNotFound = 1300 + PayChannelNotBuild = 1301 + PayChannelExtJsonError = 1302 + + //订单 + OrdersNotFound = 1401 + OrdersExist = 1402 + OrderTypeNotFount = 1403 + OrderIsDelete = 1405 + OrderStatusErr = 1406 + + //请求日志 + RequestLogErrors = 1500 + RequestLogNotFound = 1501 + RequestResponseValid = 1502 + + //回调 + NotifySendFail = 1600 ) var MsgEN = map[int]string{ @@ -47,18 +79,50 @@ var MsgEN = map[int]string{ } var MsgZH = map[int]string{ - Success: "请求成功", - ParamError: "参数错误", - NotFound: "数据不存在", - NotAuth: "未经授权", - NotLogin: "未登录", - RequestTimeOut: "请求超时", - MerchantNotFound: "商户不存在", - AppNotFound: "app_id未找到", - AppDisabled: "app通道关闭", - AppIpNotAllow: "ip不在白名单内", - SystemError: "系统错误", - PayChannelNotFound: "支付方式不存在", + Success: "请求成功", + ParamError: "参数错误", + NotFound: "数据不存在", + NotAuth: "未经授权", + NotLogin: "未登录", + RequestTimeOut: "请求超时", + MerchantNotFound: "商户不存在", + AppNotFound: "app_id未找到", + AppDisabled: "app通道关闭", + AppIpNotAllow: "ip不在白名单内", + AppDecryptDataDiscrepancy: "解密数据不一致", + SystemError: "系统错误", + AppNotifyUrlNotFound: "未设置回调地址", + + AppRsaDecryptKeyNotFound: "密匙缺失,无法进行Rsa解密", + AppRsaDecryptFail: "Rsa解密失败", + AppRsaEncryptKeyNotFound: "密匙缺失,无法进行Rsa加密", + AppRsaEncryptFail: "Rsa加密失败", + + AppSM2DecryptKeyNotFound: "密匙缺失,无法进行sm2解密", + AppSM2DecryptFail: "sm2解密失败", + AppSM2EncryptKeyNotFound: "密匙缺失,无法进行sm2加密", + AppSM2EncryptFail: "sm2加密失败", + + AppSM4DecryptKeyNotFound: "密匙缺失,无法进行sm4解密", + AppSM4DecryptFail: "sm4解密失败", + AppSM4EncryptKeyNotFound: "密匙缺失,无法进行sm4加密", + AppSM4EncryptFail: "sm4加密失败", + + PayChannelNotFound: "支付方式不存在", + PayChannelNotBuild: "支付方式尚未开通", + PayChannelExtJsonError: "支付方式扩展参数错误", + + RequestLogErrors: "请求日志错误", + RequestLogNotFound: "未找到日志信息", + RequestResponseValid: "上游返回格式无效", + + OrdersNotFound: "未找到订单", + OrdersExist: "订单已存在", + OrderTypeNotFount: "未知的支付方式", + OrderIsDelete: "订单已删除", + OrderStatusErr: "订单状态错误", + + NotifySendFail: "回调发送失败", } var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH} diff --git a/app/constants/pojo/app.go b/app/constants/pojo/app.go index 29f0239..8132200 100644 --- a/app/constants/pojo/app.go +++ b/app/constants/pojo/app.go @@ -1,7 +1,7 @@ package pojo const ( - Rsa int32 = iota + 1 - Sm2 - Sm4 + RSA int32 = iota + 1 + SM2 + SM4 ) diff --git a/app/constants/pojo/common.go b/app/constants/pojo/common.go deleted file mode 100644 index 1e58d13..0000000 --- a/app/constants/pojo/common.go +++ /dev/null @@ -1,6 +0,0 @@ -package pojo - -const ( - STATUS_ENABLE int32 = 1 - STATUS_DISABLED int32 = 2 -) diff --git a/app/data/app.go b/app/data/app.go index f355d0f..475d0ce 100644 --- a/app/data/app.go +++ b/app/data/app.go @@ -45,5 +45,4 @@ func (m *AppRepo) AppFindOne(app *appmodel.App, conn builder.Cond, columns ...st return nil, sql.ErrNoRows } return app, err - } diff --git a/app/data/merchat.go b/app/data/merchat.go index 763758c..3b85a0b 100644 --- a/app/data/merchat.go +++ b/app/data/merchat.go @@ -3,6 +3,7 @@ package data import ( "PaymentCenter/app/http/entities" "PaymentCenter/app/models/merchantmodel" + "database/sql" "xorm.io/builder" "xorm.io/xorm" ) @@ -41,3 +42,11 @@ func (m *MerchantRepo) MerchantUpdate(merchant *merchantmodel.Merchant, conn bui func (m *MerchantRepo) MerchantDetail(merchant *merchantmodel.Merchant, conn builder.Cond) (bool, error) { return m.repo.Where(conn).Get(merchant) } + +func (m *MerchantRepo) MerchantFindOne(merchant *merchantmodel.Merchant, conn builder.Cond, columns ...string) (*merchantmodel.Merchant, error) { + has, err := m.repo.Where(conn).Get(merchant) + if !has { + return nil, sql.ErrNoRows + } + return merchant, err +} diff --git a/app/data/orders.go b/app/data/orders.go index ff7fd6c..d5923aa 100644 --- a/app/data/orders.go +++ b/app/data/orders.go @@ -3,6 +3,7 @@ package data import ( "PaymentCenter/app/http/entities" "PaymentCenter/app/models/ordersmodel" + "database/sql" "xorm.io/builder" "xorm.io/xorm" ) @@ -59,3 +60,11 @@ func (m *OrderRepo) OrdersLeftPayChannelList(conn builder.Cond, pageFilter entit repo = repo.Join("left", "pay_channel", "pay_channel.id = orders.pay_channel_id") return repo.Find(orderList) } + +func (m *OrderRepo) OrderFindOne(order *ordersmodel.Orders, conn builder.Cond, columns ...string) (*ordersmodel.Orders, error) { + has, err := m.repo.Where(conn).Get(order) + if !has { + return nil, sql.ErrNoRows + } + return order, err +} diff --git a/app/data/pay_channel.go b/app/data/pay_channel.go index 8647765..b6438c8 100644 --- a/app/data/pay_channel.go +++ b/app/data/pay_channel.go @@ -3,6 +3,7 @@ package data import ( "PaymentCenter/app/http/entities" "PaymentCenter/app/models/paychannelmodel" + "database/sql" "xorm.io/builder" "xorm.io/xorm" ) @@ -41,3 +42,11 @@ func (m *PayChannelRepo) PayChannelUpdate(payChannel *paychannelmodel.PayChannel func (m *PayChannelRepo) PayChannelGet(payChannel *paychannelmodel.PayChannel, conn builder.Cond) (bool, error) { return m.repo.Where(conn).Get(payChannel) } + +func (m *PayChannelRepo) PayChannelFindOne(payChannel *paychannelmodel.PayChannel, conn builder.Cond, columns ...string) (*paychannelmodel.PayChannel, error) { + has, err := m.repo.Where(conn).Get(payChannel) + if !has { + return nil, sql.ErrNoRows + } + return payChannel, err +} diff --git a/app/http/controllers/api.go b/app/http/controllers/api.go new file mode 100644 index 0000000..ec1e2eb --- /dev/null +++ b/app/http/controllers/api.go @@ -0,0 +1,102 @@ +package controllers + +import ( + "PaymentCenter/app/constants/common" + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/models/orderrequestlogmodel" + "PaymentCenter/app/services" + "PaymentCenter/app/utils" + "PaymentCenter/config" + "encoding/json" + "github.com/gin-gonic/gin" + "net/http" +) + +func ApiRes(c *gin.Context, data interface{}, code int, msg ...string) { + var log_id int64 + message := "" + if utils.IsNil(data) { + data = struct{}{} + } + if len(msg) > 0 { + message = msg[0] + } else { + message = errorcode.GetMsg(code, "") + } + log, exists := GetApiLogId(c) + if exists { + log_id = log.(int64) + dataByte, _ := json.Marshal(data) + status := common.STATUS_ENABLE + if code == errorcode.Success { + status = common.STATUS_DISABLED + } + services.RequestLogUpdate(&orderrequestlogmodel.OrderRequestLog{ + Id: log_id, + MerchantResponse: string(dataByte), + Status: status, + }) + } + + if code == errorcode.Success { + ApiSuccess(c, data, log_id, message) + } else { + ApiError(c, code, log_id, message) + } +} + +func ApiSuccess(c *gin.Context, data interface{}, log_id int64, messageSlice ...string) { + var message string + if len(messageSlice) > 0 { + message = messageSlice[0] + } + if message == "" { + message = errorcode.GetMsg(errorcode.Success, c.GetHeader("local")) + } + if config.GetConf().Env == "production" { + c.String(http.StatusOK, EncriptJson(gin.H{ + "code": errorcode.Success, + "message": message, + "data": data, + "trace_id": log_id, + })) + } else { + c.JSON(http.StatusOK, gin.H{ + "code": errorcode.Success, + "message": message, + "data": data, + "trace_id": log_id, + }) + } + + c.Abort() +} + +/** + * 失败时返回 + */ +func ApiError(c *gin.Context, code int, log_id int64, msg ...string) { + message := "" + if len(msg) > 0 { + message = msg[0] + } else { + message = errorcode.GetMsg(code, "") + } + if config.GetConf().Env == "production" { + c.String(http.StatusOK, EncriptJson(gin.H{ + "code": code, + "message": message, + "data": make(map[string]string), + "trace_id": log_id, + })) + } else { + c.JSON(http.StatusOK, gin.H{ + "code": code, + "message": message, + "data": make(map[string]string), + "trace_id": log_id, + }) + } + + c.Abort() +} diff --git a/app/http/controllers/base.go b/app/http/controllers/base.go index bd68940..b42fd4d 100644 --- a/app/http/controllers/base.go +++ b/app/http/controllers/base.go @@ -149,6 +149,34 @@ func GenRequest(c *gin.Context, request interface{}) (msgs []string, err error) return } +func ValidApiData(dataByte []byte, validStruct interface{}) (msgs []string, err error) { + validate := validator.New() + err = json.Unmarshal(dataByte, validStruct) + if err != nil { + err = errors.New(errorcode.GetMsg(errorcode.ParamError, "")) + } + zh_ch := zh.New() + uni := ut.New(zh_ch) + trans, _ := uni.GetTranslator("zh") + //注册一个函数,获取struct tag里自定义的label作为字段名 + validate.RegisterTagNameFunc(func(fld reflect.StructField) string { + name := fld.Tag.Get("label") + return name + }) + //验证器注册翻译器 + _ = zh_translations.RegisterDefaultTranslations(validate, trans) + errValidate := validate.Struct(validStruct) + if errValidate != nil { + // 如果有错误,打印出来 + for _, v := range errValidate.(validator.ValidationErrors) { + msgs = append(msgs, v.Translate(trans)) + } + err = errors.New(errorcode.GetMsg(errorcode.ParamError, "")) + return + } + return +} + // 重复读取body func ReadBody(c *gin.Context) (body []byte, err error) { body, err = ioutil.ReadAll(c.Request.Body) @@ -164,6 +192,19 @@ func GetRequest(c *gin.Context) interface{} { return request } +func GetAppCheckInfo(c *gin.Context) interface{} { + request, exists := c.Get("appCheckInfo") + if !exists { + return nil + } + return request +} + +func GetApiLogId(c *gin.Context) (interface{}, bool) { + request, exists := c.Get("log") + return request, exists +} + func HandRes(c *gin.Context, data interface{}, err error) { if err == nil { Success(c, data, "") diff --git a/app/http/controllers/front/api.go b/app/http/controllers/front/api.go new file mode 100644 index 0000000..fd7f2da --- /dev/null +++ b/app/http/controllers/front/api.go @@ -0,0 +1,32 @@ +package front + +import ( + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/http/controllers" + "PaymentCenter/app/http/entities/front" + "PaymentCenter/app/services" + "PaymentCenter/app/services/thirdpay" + "github.com/gin-gonic/gin" +) + +func Pay(c *gin.Context) { + req := controllers.GetRequest(c).(*front.PayReqs) + appCheckInfo := controllers.GetAppCheckInfo(c).(*services.AppCheck) + check := thirdpay.ThirdPayCheck(c.Request.Context(), req, appCheckInfo, c.ClientIP()) + if check.CheckCode != errorcode.Success { + if check.CheckCode == errorcode.OrdersExist { + //订单已存在,直接返回订单信息 + controllers.ApiRes(c, thirdpay.PayCallBack(check.OldOrder, false), errorcode.Success) + return + } + controllers.ApiRes(c, nil, check.CheckCode) + return + } + payInfo := thirdpay.ThirdPayWeb(check) + if payInfo.PayCode != errorcode.Success { + controllers.ApiRes(c, nil, payInfo.PayCode) + return + } + controllers.ApiRes(c, thirdpay.PayCallBack(payInfo.Order, true), errorcode.Success) + return +} diff --git a/app/http/entities/backend/order.go b/app/http/entities/backend/order.go index f5ef40b..f898bf2 100644 --- a/app/http/entities/backend/order.go +++ b/app/http/entities/backend/order.go @@ -108,7 +108,7 @@ type OrderLogsListRequest struct { type OrderRequestLogResponse struct { Id int64 `json:"id"` IpAddress string `json:"ip_address"` - OrderId int64 `json:"order_id"` + Url string `json:"url"` MerchantRequest string `json:"merchant_request"` MerchantResponse string `json:"merchant_response"` CreateTime string `json:"create_time"` @@ -117,8 +117,9 @@ type OrderRequestLogResponse struct { func (o *OrderRequestLogResponse) ResponseFromDb(db orderrequestlogmodel.OrderRequestLog) { o.Id = db.Id - o.OrderId = db.OrderId + o.IpAddress = db.IpAddress + o.Url = db.URL o.Status = db.Status o.MerchantRequest = db.MerchantRequest o.MerchantResponse = db.MerchantResponse @@ -126,12 +127,13 @@ func (o *OrderRequestLogResponse) ResponseFromDb(db orderrequestlogmodel.OrderRe } type OrderThirdLogResponse struct { - Id int64 `json:"id"` - OrderId int64 `json:"order_id"` + Id int64 `json:"id"` + OrderId int64 `json:"order_id"` + CreateTime string `json:"create_time"` PayCallback string `json:"pay_callback"` Status int `json:"status"` - MerchantParam string `json:"merchant_param"` + PayParam string `json:"pay_param"` MerchantCallback string `json:"merchant_callback"` } @@ -140,7 +142,7 @@ func (o *OrderThirdLogResponse) ResponseFromDb(db orderthirdpaylogmodel.OrderThi o.OrderId = db.OrderId o.PayCallback = db.PayCallback o.Status = db.Status - o.MerchantParam = db.PayParam + o.PayParam = db.PayParam o.MerchantCallback = db.MerchantCallback o.CreateTime = db.CreateTime.Format("2006-01-02 15:04:05") } diff --git a/app/http/entities/front/pay.go b/app/http/entities/front/pay.go index f3dca7a..413970a 100644 --- a/app/http/entities/front/pay.go +++ b/app/http/entities/front/pay.go @@ -1,12 +1,28 @@ package front -type PayCommonBody struct { +type ApiCommonBody struct { AppId int64 `json:"app_id" validate:"required"` Timestamp int64 `json:"timestamp" validate:"required"` } -type PayWeb struct { - PayCommonBody - PayChannel int64 `json:"private_key_path"` - Delay int64 `json:"delay"` //延时时间 +type RequestBody struct { + AppId int64 `json:"app_id" validate:"required"` + Timestamp int64 `json:"timestamp" validate:"required"` + Data string `json:"data" validate:"required"` +} + +type PayReqs struct { + ApiCommonBody + 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:"支付金额,单位分"` + ExtJson string `json:"ext_json" label:"扩展参数"` + Desc string `json:"desc" validate:"max=100" label:"商品描述"` +} + +type OrderApiResp struct { + Code int + Msg string + Data interface{} } diff --git a/app/http/middlewares/base.go b/app/http/middlewares/base.go index aa2b217..0bdf33e 100644 --- a/app/http/middlewares/base.go +++ b/app/http/middlewares/base.go @@ -4,12 +4,13 @@ import ( "PaymentCenter/app/constants/common" "PaymentCenter/app/constants/errorcode" "PaymentCenter/app/http/controllers" - "PaymentCenter/app/http/entities" "PaymentCenter/app/http/entities/front" "PaymentCenter/app/http/requestmapping" "PaymentCenter/app/services" + "PaymentCenter/app/services/apicrypt" "PaymentCenter/app/utils" "PaymentCenter/config" + "encoding/json" "github.com/gin-gonic/gin" "strings" ) @@ -104,10 +105,11 @@ func ValidateRequest() gin.HandlerFunc { return func(c *gin.Context) { var path = c.FullPath() var handler func() interface{} + if strings.Index(path, "admin") >= 0 { handler = requestmapping.BackendRequestMap[path] } else { - handler = requestmapping.FrontRequestMap[path] + handler = requestmapping.FrontRequestMapBeforeDecrypt[path] } if handler == nil { utils.Log(c, "path", path) @@ -129,35 +131,67 @@ func ValidateRequest() gin.HandlerFunc { func ValidatePayRequest() gin.HandlerFunc { return func(c *gin.Context) { - com, err := utils.SonicApiDataToStruct(controllers.GetRequest(c), &front.PayCommonBody{}) + var path = c.FullPath() + var handler func() interface{} + requestData, err := utils.SonicApiDataToStruct(controllers.GetRequest(c), &front.RequestBody{}) if err != nil { - controllers.ErrWithCode(c, errorcode.ParamError) + controllers.ApiRes(c, nil, errorcode.ParamError) + return } - comStruct := com.(*front.PayCommonBody) + requestDataStruct := requestData.(*front.RequestBody) + //判断时间 //now := time.Now().UnixNano() / 1000000 - //if comStruct.Timestamp > now || (config.GetConf().TimeOut != 0 && (now-comStruct.Timestamp) > config.GetConf().TimeOut) { - // controllers.ErrWithCode(c, errorcode.RequestTimeOut) + //if requestDataStruct.Timestamp > now || (config.GetConf().TimeOut != 0 && (now-requestDataStruct.Timestamp) > config.GetConf().TimeOut) { + // controllers.ApiRes(c, nil, errorcode.RequestTimeOut) // return //} //获取app信息 - app, errCode := services.AppFindOne(entities.IdRequest{Id: comStruct.AppId}) - if errCode != errorcode.Success { - controllers.ErrWithCode(c, errCode) - return - } - //检查app可用性 - appCheck := services.NewAppCheck(app).Check() - if appCheck.GetCode() != errorcode.Success { - controllers.ErrWithCode(c, appCheck.GetCode()) - return - } - //检查白名单 - if !appCheck.IpCheck(c.ClientIP()) { - controllers.ErrWithCode(c, appCheck.GetCode()) - return - } + appCheck := services.GetAppCheck(requestDataStruct.AppId, c.ClientIP()) + //存入请求记录 + if appCheck.Code != errorcode.Success { + controllers.ApiRes(c, nil, appCheck.Code) + return + } + //解密 + cryptFunc := appCheck.Crypt() + if cryptFunc == nil { + controllers.ApiRes(c, nil, appCheck.GetCode()) + } + dataByte, errCode := cryptFunc(appCheck.App).Decrypt(requestDataStruct.Data) + if errCode != apicrypt.CryptNotError { + controllers.ApiRes(c, nil, errCode) + return + } + //记录请求日志 + id, code := services.AddRequestLog(dataByte, c.ClientIP(), path) + if code != errorcode.Success { + controllers.ApiRes(c, nil, errCode) + } + c.Set("log", id) + //检查解密后的数据是否与请求一致 + reCheck := appCheck.ReCheckAfterDecrypt(dataByte, requestDataStruct) + if !reCheck { + controllers.ApiRes(c, nil, appCheck.GetCode()) + return + } + //表单验证 + handler = requestmapping.FrontRequestMap[path] + v := handler() + msg, err := controllers.ValidApiData(dataByte, v) + if err != nil { + utils.Log(c, "参数错误", "path=", path, "err=", err.Error(), "msg=", msg) + controllers.ApiRes(c, nil, errorcode.ParamError, msg...) + c.Abort() + } + err = json.Unmarshal(dataByte, &v) + if err != nil { + controllers.ApiRes(c, nil, errorcode.Forbidden) + return + } + c.Set("request", v) + c.Set("appCheckInfo", appCheck) c.Next() } } diff --git a/app/http/requestmapping/front.go b/app/http/requestmapping/front.go index 5660770..bf8173d 100644 --- a/app/http/requestmapping/front.go +++ b/app/http/requestmapping/front.go @@ -6,5 +6,9 @@ import ( ) var FrontRequestMap = map[string]func() interface{}{ - common.FRONT_V1 + "/pay/web": func() interface{} { return new(front.PayWeb) }, + common.FRONT_V1 + "/pay/do": func() interface{} { return new(front.PayReqs) }, +} + +var FrontRequestMapBeforeDecrypt = map[string]func() interface{}{ + common.FRONT_V1 + "/pay/do": func() interface{} { return new(front.RequestBody) }, } diff --git a/app/http/routes/route.go b/app/http/routes/route.go index 4761514..65deec4 100644 --- a/app/http/routes/route.go +++ b/app/http/routes/route.go @@ -6,7 +6,6 @@ package routes import ( "PaymentCenter/app/constants/common" "PaymentCenter/app/http/controllers" - "PaymentCenter/app/http/controllers/backend" "PaymentCenter/app/http/controllers/front" "PaymentCenter/app/http/middlewares" "PaymentCenter/app/http/trace" @@ -56,9 +55,13 @@ func RegisterRoute(router *gin.Engine) { notify.POST("/wx/:payChannelId", front.WxCallback) notify.POST("/ali/:payChannelId", front.AliCallback) } + + pay := v1.Group("/pay", middlewares.ValidateRequest(), middlewares.ValidatePayRequest()) + { + pay.POST("/do", front.Pay) + } } router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) - pay := v1.Group("pay", middlewares.ValidatePayRequest()) - pay.POST("web", backend.MerchantList) // 商户列表 + } diff --git a/app/models/orderrequestlogmodel/order_request_log.go b/app/models/orderrequestlogmodel/order_request_log.go index 961b6e6..236c759 100644 --- a/app/models/orderrequestlogmodel/order_request_log.go +++ b/app/models/orderrequestlogmodel/order_request_log.go @@ -15,7 +15,7 @@ var ( type OrderRequestLog struct { Id int64 IpAddress string `xorm:"'ip_address' varchar(16)"` - OrderId int64 `xorm:"'order_id' bigint(20)"` + URL string `xorm:"'url' varchar(20)"` MerchantRequest string `xorm:"'merchant_request' JSON"` MerchantResponse string `xorm:"'merchant_response' JSON"` CreateTime time.Time `xorm:"'create_time' datetime created"` diff --git a/app/models/ordersmodel/orders.go b/app/models/ordersmodel/orders.go index 85cb91a..ed8219a 100644 --- a/app/models/ordersmodel/orders.go +++ b/app/models/ordersmodel/orders.go @@ -22,6 +22,7 @@ type Orders struct { Amount int `xorm:"'amount' int(11)"` PayerTotal int `xorm:"'payer_total' int(11)"` ExtJson string `xorm:"'ext_json' varchar(1024)"` + Desc string `xorm:"'desc' varchar(100)"` CreateTime time.Time `xorm:"'create_time' datetime created"` UpdateTime time.Time `xorm:"'update_time' timestamp updated"` Status int `xorm:"'status' TINYINT"` diff --git a/app/models/paychannelmodel/pay_channel.go b/app/models/paychannelmodel/pay_channel.go index 7b65f10..c99448b 100644 --- a/app/models/paychannelmodel/pay_channel.go +++ b/app/models/paychannelmodel/pay_channel.go @@ -16,7 +16,7 @@ type PayChannel struct { Id int64 PayName string `xorm:"'pay_name' varchar(128)"` MerchantId int64 `xorm:"'merchant_id' bigint(20)"` - ChannelType int `xorm:"'channel_type' int(11)"` + ChannelType int `xorm:"'channel_type' tinyint(2)"` AppId string `xorm:"'app_id' varchar(255)"` ExtJson string `xorm:"'ext_json' JSON"` ExpireTime time.Time `xorm:"'expire_time' datetime"` diff --git a/app/services/api_request_valid.go b/app/services/api_request_valid.go index 16cb09e..ca007ce 100644 --- a/app/services/api_request_valid.go +++ b/app/services/api_request_valid.go @@ -1,16 +1,21 @@ package services import ( + "PaymentCenter/app/constants/common" "PaymentCenter/app/constants/errorcode" - "PaymentCenter/app/constants/pojo" + "PaymentCenter/app/http/entities/front" "PaymentCenter/app/models/appmodel" + "PaymentCenter/app/services/apicrypt" "PaymentCenter/app/utils" + "github.com/bytedance/sonic" "strings" ) type AppCheck struct { - App *appmodel.App - Code int + AppId int64 + Ip string + App *appmodel.App + Code int } func NewAppCheck(app *appmodel.App) *AppCheck { @@ -33,11 +38,13 @@ func (a *AppCheck) IpCheck(ip string) bool { func (a *AppCheck) Check() *AppCheck { - if a.App.Status == pojo.STATUS_DISABLED { + if a.App.Status == common.STATUS_DISABLED { a.Code = errorcode.AppDisabled + return a } - if a.App.DeleteTime.Location() == nil { + if !a.App.DeleteTime.IsZero() { a.Code = errorcode.AppNotFound + return a } return a } @@ -45,3 +52,27 @@ func (a *AppCheck) Check() *AppCheck { func (a *AppCheck) GetCode() int { return a.Code } + +func (a *AppCheck) Crypt() (cryptFunc func(app *appmodel.App) apicrypt.ApiCrypt) { + var ( + ok bool + ) + if cryptFunc, ok = apicrypt.ApiCryptMap[a.App.KeyType]; !ok { + a.Code = errorcode.AppNotFound + return nil + } + return cryptFunc +} + +func (a *AppCheck) ReCheckAfterDecrypt(data []byte, requestData *front.RequestBody) bool { + var requestCommonData front.ApiCommonBody + if err := sonic.Unmarshal(data, &requestCommonData); err != nil { + a.Code = errorcode.ParamError + return false + } + if requestCommonData.AppId != requestData.AppId || requestCommonData.Timestamp != requestData.Timestamp { + a.Code = errorcode.AppDecryptDataDiscrepancy + return false + } + return true +} diff --git a/app/services/apicrypt/rsa.go b/app/services/apicrypt/rsa.go index 171271d..69012ac 100644 --- a/app/services/apicrypt/rsa.go +++ b/app/services/apicrypt/rsa.go @@ -1,17 +1,43 @@ package apicrypt -import "PaymentCenter/app/models/appmodel" +import ( + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/models/appmodel" + "PaymentCenter/app/utils/encrypt/rsa" +) -func NewRsa(app *appmodel.App) ApiDecrypt { +func NewRsa(app *appmodel.App) ApiCrypt { return &Rsa{ App: app, } } -func (r *Rsa) Encrypt(decryptData interface{}) (encryptData string, err error) { +func (r *Rsa) Encrypt(decryptData string) (encryptData []byte, errCode int) { + if r.App.PublicKey == "" { + return nil, errorcode.AppRsaEncryptKeyNotFound + } + publicKeyPEM := `-----BEGIN PUBLIC KEY----- +` + r.App.PublicKey + ` +-----END PUBLIC KEY-----` + encryptData, err := rsa.Encrypt(publicKeyPEM, []byte(decryptData)) + if err != nil { + return nil, errorcode.AppRsaEncryptFail + } + return } -func (r *Rsa) Decrypt(encryptData string) (decryptData map[string]interface{}, err error) { +func (r *Rsa) Decrypt(encryptData string) (decryptData []byte, errCode int) { + + if r.App.PrivateKey == "" { + return nil, errorcode.AppRsaDecryptKeyNotFound + } + privateKeyPEM := `-----BEGIN RSA PRIVATE KEY----- +` + r.App.PrivateKey + ` +-----END RSA PRIVATE KEY-----` + decryptData, err := rsa.Decrypt(privateKeyPEM, encryptData) + if err != nil || decryptData == nil { + return nil, errorcode.AppRsaDecryptFail + } return } diff --git a/app/services/apicrypt/sm2.go b/app/services/apicrypt/sm2.go index 2a18c77..7aee726 100644 --- a/app/services/apicrypt/sm2.go +++ b/app/services/apicrypt/sm2.go @@ -1,9 +1,39 @@ package apicrypt -func (r *SM2) Encrypt(decryptData interface{}) (encryptData string, err error) { +import ( + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/models/appmodel" + "PaymentCenter/app/utils/encrypt/sm2" +) + +func NewSm2(app *appmodel.App) ApiCrypt { + return &SM2{ + App: app, + } +} + +func (r *SM2) Encrypt(decryptData string) (encryptData []byte, errCode int) { + if r.App.MerchantPublicKey == "" { + return nil, errorcode.AppSM2EncryptKeyNotFound + } + // + encryptDataString, err := sm2.SM2Encrypt(decryptData, r.App.PrivateKey) + if err != nil { + return nil, errorcode.AppSM2EncryptFail + } + encryptData = []byte(encryptDataString) return } -func (r *SM2) Decrypt(encryptData string) (decryptData map[string]interface{}, err error) { +func (r *SM2) Decrypt(encryptData string) (decryptData []byte, errCode int) { + if r.App.PrivateKey == "" || r.App.PublicKey == "" { + return nil, errorcode.AppSM2DecryptKeyNotFound + } + + decryptDataString, err := sm2.SM2Decrypt(encryptData, r.App.PublicKey, r.App.PrivateKey) + if err != nil { + return nil, errorcode.AppSM2DecryptFail + } + decryptData = []byte(decryptDataString) return } diff --git a/app/services/apicrypt/sm4.go b/app/services/apicrypt/sm4.go index 4e16213..9c8dcfe 100644 --- a/app/services/apicrypt/sm4.go +++ b/app/services/apicrypt/sm4.go @@ -1,9 +1,41 @@ package apicrypt -func (r *SM4) Encrypt(decryptData interface{}) (encryptData string, err error) { +import ( + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/models/appmodel" + "PaymentCenter/app/utils/encrypt/sm4" + "strconv" +) + +func NewSm4(app *appmodel.App) ApiCrypt { + return &SM4{ + App: app, + } +} + +func (r *SM4) Encrypt(decryptData string) (encryptData []byte, errCode int) { + if r.App.MerchantPublicKey == "" || r.App.PrivateKey == "" { + return nil, errorcode.AppSM4DecryptKeyNotFound + } + + encryptDataString, err := sm4.Sm4Encrypt(strconv.FormatInt(r.App.Id, 10), r.App.PrivateKey, r.App.MerchantPublicKey, decryptData, "", true) + if err != nil { + return nil, errorcode.AppSM4EncryptFail + } + encryptData = []byte(encryptDataString) return } -func (r *SM4) Decrypt(encryptData string) (decryptData map[string]interface{}, err error) { +func (r *SM4) Decrypt(encryptData string) (decryptData []byte, errCode int) { + if r.App.PrivateKey == "" || r.App.MerchantPublicKey == "" { + return nil, errorcode.AppSM4DecryptKeyNotFound + } + + decryptDataString, err := sm4.Sm4Decrypt(strconv.FormatInt(r.App.Id, 10), r.App.PrivateKey, r.App.MerchantPublicKey, encryptData, true) + if err != nil { + return nil, errorcode.AppSM4DecryptFail + } + decryptData = []byte(decryptDataString) return + } diff --git a/app/services/apicrypt/types.go b/app/services/apicrypt/types.go index 2e6e8ec..533de39 100644 --- a/app/services/apicrypt/types.go +++ b/app/services/apicrypt/types.go @@ -1,11 +1,14 @@ package apicrypt -import "PaymentCenter/app/models/appmodel" +import ( + "PaymentCenter/app/constants/pojo" + "PaymentCenter/app/models/appmodel" +) type ( - ApiDecrypt interface { - Encrypt(decryptData interface{}) (encryptData string, err error) - Decrypt(encryptData string) (decryptData map[string]interface{}, err error) + ApiCrypt interface { + Encrypt(decryptData string) (encryptData []byte, errCode int) + Decrypt(encryptData string) (decryptData []byte, errCode int) } Rsa struct { @@ -20,3 +23,11 @@ type ( App *appmodel.App } ) + +var ApiCryptMap = map[int32]func(app *appmodel.App) ApiCrypt{ + pojo.RSA: NewRsa, + pojo.SM2: NewSm2, + pojo.SM4: NewSm4, +} + +const CryptNotError = 0 diff --git a/app/services/app.go b/app/services/app.go index a161494..371ecce 100644 --- a/app/services/app.go +++ b/app/services/app.go @@ -65,7 +65,7 @@ func AppUpdate(app *appmodel.App) (code int) { } else { _, err = repo.AppUpdate(app, conn) } - + code = handErr(err) return } @@ -99,3 +99,32 @@ func AppFindOne(req entities.IdRequest, col ...string) (row *appmodel.App, code } return row, errorcode.Success } + +func CheckApp(appCheckIn *AppCheck) { + errCode := errorcode.Success + appCheckIn.App, errCode = AppFindOne(entities.IdRequest{Id: appCheckIn.AppId}) + if errCode != errorcode.Success { + appCheckIn.Code = errCode + } + //检查app可用性 + appCheckIn.Check() + if appCheckIn.GetCode() != errorcode.Success { + return + } + //检查白名单 + if appCheckIn.Ip != "" && !appCheckIn.IpCheck(appCheckIn.Ip) { + return + } + + return +} + +func GetAppCheck(appId int64, ip string) *AppCheck { + appCheck := &AppCheck{ + AppId: appId, + Ip: ip, + Code: errorcode.Success, + } + CheckApp(appCheck) + return appCheck +} diff --git a/app/services/merchant.go b/app/services/merchant.go index 99d80f1..abba737 100644 --- a/app/services/merchant.go +++ b/app/services/merchant.go @@ -1,10 +1,12 @@ package services import ( + "PaymentCenter/app/constants/errorcode" "PaymentCenter/app/data" "PaymentCenter/app/http/entities" "PaymentCenter/app/http/entities/backend" "PaymentCenter/app/models/merchantmodel" + "database/sql" "strings" "xorm.io/builder" ) @@ -65,3 +67,28 @@ func MerchantDelete(req entities.IdRequest) (code int) { code = handErr(err) return } + +func MerchantFindOne(merchant *merchantmodel.Merchant, conn builder.Cond, col ...string) (merchantInfo *merchantmodel.Merchant, code int) { + repo := data.NewMerchantRepo(merchantmodel.GetInstance().GetDb()) + // 拼接查询条件 + merchantInfo, err := repo.MerchantFindOne(merchant, conn, col...) + if err != nil { + if err == sql.ErrNoRows { + return nil, errorcode.MerchantNotFound + } + return nil, errorcode.SystemError + } + return merchantInfo, errorcode.Success +} + +func GetAndCheckMerchant(merchant *merchantmodel.Merchant, conn builder.Cond, col ...string) (merchantInfo *merchantmodel.Merchant, code int) { + merchantInfo, code = MerchantFindOne(merchant, conn, col...) + if code != errorcode.Success { + return nil, code + } + + if !merchantInfo.DeleteTime.IsZero() { + return nil, errorcode.MerchantNotFound + } + return +} diff --git a/app/services/order.go b/app/services/order.go index fa7e3ca..18c43a5 100644 --- a/app/services/order.go +++ b/app/services/order.go @@ -1,12 +1,14 @@ package services import ( + "PaymentCenter/app/constants/errorcode" "PaymentCenter/app/data" "PaymentCenter/app/http/entities/backend" "PaymentCenter/app/models/orderrequestlogmodel" "PaymentCenter/app/models/ordersmodel" "PaymentCenter/app/models/orderthirdpaylogmodel" "PaymentCenter/app/models/paychannelmodel" + "database/sql" "xorm.io/builder" ) @@ -72,3 +74,42 @@ func OrderLogsList(req backend.OrderLogsListRequest) (requestLog []orderrequestl return orderLogList, thirdLogList, code } + +func OrderCreate(orderIn *ordersmodel.Orders) (orderOut *ordersmodel.Orders, code int) { + repo := data.NewOrderRepo(ordersmodel.GetInstance().GetDb()) + _, err := repo.OrderInsertOne(orderIn) + code = handErr(err) + return orderIn, code +} + +func OrderUpdate(order *ordersmodel.Orders, col ...string) (code int) { + repo := data.NewOrderRepo(ordersmodel.GetInstance().GetDb()) + // 拼接查询条件 + conn := builder.NewCond() + conn = conn.And(builder.Eq{"Id": order.Id}) + _, err := repo.OrderUpdate(order, conn, col...) + + code = handErr(err) + return +} + +func OrderFindOne(order *ordersmodel.Orders, conn builder.Cond, col ...string) (merchantInfo *ordersmodel.Orders, code int) { + repo := data.NewOrderRepo(ordersmodel.GetInstance().GetDb()) + // 拼接查询条件 + orderInfo, err := repo.OrderFindOne(order, conn, col...) + if err != nil { + if err == sql.ErrNoRows { + return nil, errorcode.OrdersNotFound + } + return nil, errorcode.SystemError + } + return orderInfo, errorcode.OrdersExist +} + +func PayOrderCheckRepeat(order *ordersmodel.Orders, conn builder.Cond) (exist bool, code int) { + _, code = OrderFindOne(order, conn) + if code != errorcode.OrdersNotFound { + return false, errorcode.Success + } + return true, code +} diff --git a/app/services/pay_channel.go b/app/services/pay_channel.go index 6cfe4f6..fd88eb6 100644 --- a/app/services/pay_channel.go +++ b/app/services/pay_channel.go @@ -7,6 +7,7 @@ import ( "PaymentCenter/app/http/entities/backend" "PaymentCenter/app/models/merchantmodel" "PaymentCenter/app/models/paychannelmodel" + "database/sql" "xorm.io/builder" ) @@ -92,3 +93,27 @@ func PayChannelGet(payChannel *paychannelmodel.PayChannel) (code int) { code = errorcode.Success return } + +func PayChannelFindOne(channel *paychannelmodel.PayChannel, conn builder.Cond, col ...string) (merchantInfo *paychannelmodel.PayChannel, code int) { + repo := data.NewPayChannelRepo(paychannelmodel.GetInstance().GetDb()) + // 拼接查询条件 + channelInfo, err := repo.PayChannelFindOne(channel, conn, col...) + if err != nil { + if err == sql.ErrNoRows { + return nil, errorcode.MerchantNotFound + } + return nil, errorcode.SystemError + } + return channelInfo, errorcode.Success +} + +func GetAndCheckPayChannel(channel *paychannelmodel.PayChannel, conn builder.Cond, col ...string) (channelInfo *paychannelmodel.PayChannel, code int) { + channelInfo, code = PayChannelFindOne(channel, conn, col...) + if code != errorcode.Success { + return nil, code + } + if !channelInfo.DeleteTime.IsZero() { + return nil, errorcode.PayChannelNotFound + } + return +} diff --git a/app/services/request_log.go b/app/services/request_log.go new file mode 100644 index 0000000..f84bdda --- /dev/null +++ b/app/services/request_log.go @@ -0,0 +1,43 @@ +package services + +import ( + "PaymentCenter/app/constants/common" + "PaymentCenter/app/data" + "PaymentCenter/app/models/orderrequestlogmodel" + "xorm.io/builder" +) + +func RequestLogCreate(log *orderrequestlogmodel.OrderRequestLog) (*orderrequestlogmodel.OrderRequestLog, int) { + db := orderrequestlogmodel.GetInstance().GetDb() + repo := data.NewOrderRequestLogRepo(db) + id, err := repo.OrderRequestLogInsertOne(log) + code := handErr(err) + if err != nil { + log.Id = id + } + return log, code +} + +func RequestLogUpdate(log *orderrequestlogmodel.OrderRequestLog) (logOut *orderrequestlogmodel.OrderRequestLog, code int) { + db := orderrequestlogmodel.GetInstance().GetDb() + repo := data.NewOrderRequestLogRepo(db) + conn := builder.NewCond() + conn = conn.And(builder.Eq{"Id": log.Id}) + id, err := repo.OrderRequestLogUpdate(log, conn) + code = handErr(err) + if err != nil { + log.Id = id + } + return log, code +} + +func AddRequestLog(requestDataByte []byte, ip string, url string) (int64, int) { + requestLog, checkCode := RequestLogCreate(&orderrequestlogmodel.OrderRequestLog{ + IpAddress: ip, + MerchantRequest: string(requestDataByte), + URL: url, + MerchantResponse: "{}", + Status: common.STATUS_ENABLE, + }) + return requestLog.Id, checkCode +} diff --git a/app/services/thirdpay/do/pay.go b/app/services/thirdpay/do/pay.go new file mode 100644 index 0000000..184a946 --- /dev/null +++ b/app/services/thirdpay/do/pay.go @@ -0,0 +1,64 @@ +package do + +import ( + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/services" + + "PaymentCenter/app/models/ordersmodel" + "PaymentCenter/app/third/paymentService" +) + +type Pay struct { + paycheck *PayCheck + Order *ordersmodel.Orders + PayCode int +} + +func NewPay(paycheck *PayCheck) *Pay { + return &Pay{ + paycheck: paycheck, + PayCode: errorcode.Success, + } +} + +func (w *Pay) CreateOrder() { + w.Order, w.PayCode = services.OrderCreate(&ordersmodel.Orders{ + MerchantId: w.paycheck.Merchant.Id, + PayChannelId: w.paycheck.Channel.Id, + AppId: w.paycheck.Channel.Id, + OutTreadNo: w.paycheck.WebPayReqs.OutTradeNo, + OrderType: w.paycheck.WebPayReqs.OrderType, + Amount: w.paycheck.WebPayReqs.Amount, + ExtJson: w.paycheck.WebPayReqs.ExtJson, + Desc: w.paycheck.WebPayReqs.Desc, + }, + ) +} + +func (w *Pay) Pay() { + var ( + payFunc func(commonPayInfo *paymentService.PayOrderRequest, ExtJson string) error + ok bool + ) + thirdPay := &paymentService.PayOrderRequest{ + PayChannelId: w.paycheck.WebPayReqs.PayChannelId, + OrderId: w.Order.Id, + ChannelType: w.paycheck.Channel.ChannelType, + Description: w.Order.Desc, + Amount: w.Order.Amount, + PayerClientIp: w.paycheck.AppCheck.Ip, + } + if payFunc, ok = PayWayList[w.paycheck.Channel.ChannelType]; !ok { + w.PayCode = errorcode.PayChannelNotBuild + return + } + err := payFunc(thirdPay, w.paycheck.Channel.ExtJson) + if err != nil { + w.PayCode = errorcode.PayChannelExtJsonError + return + } + res := paymentService.PaymentService(*w.paycheck.ctx, *thirdPay) + + w.PayCode = res.Code + return +} diff --git a/app/services/thirdpay/do/pay_check.go b/app/services/thirdpay/do/pay_check.go new file mode 100644 index 0000000..e3228e4 --- /dev/null +++ b/app/services/thirdpay/do/pay_check.go @@ -0,0 +1,69 @@ +package do + +import ( + "PaymentCenter/app/constants/common" + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/services" + "xorm.io/builder" + + "PaymentCenter/app/http/entities/front" + "PaymentCenter/app/models/merchantmodel" + "PaymentCenter/app/models/orderrequestlogmodel" + "PaymentCenter/app/models/ordersmodel" + "PaymentCenter/app/models/paychannelmodel" + "context" +) + +type PayCheck struct { + ctx *context.Context + WebPayReqs *front.PayReqs + AppCheck *services.AppCheck + RequestLog *orderrequestlogmodel.OrderRequestLog + Channel *paychannelmodel.PayChannel + Merchant *merchantmodel.Merchant + OldOrder *ordersmodel.Orders + CheckCode int +} + +func NewPayCheck(ctx *context.Context, reqs *front.PayReqs, appCheck *services.AppCheck, ip string) *PayCheck { + if appCheck == nil { + appCheck = services.GetAppCheck(reqs.AppId, ip) + } + return &PayCheck{ + ctx: ctx, + WebPayReqs: reqs, + AppCheck: appCheck, + CheckCode: appCheck.Code, + } +} + +func (w *PayCheck) CheckForm() { + if _, ok := common.OrderTypeList[w.WebPayReqs.OrderType]; !ok { //判断是否是支持的支付渠道 + w.CheckCode = errorcode.PayChannelNotFound + return + } + +} + +func (w *PayCheck) CheckPayChannel() { + conn := builder.NewCond() + conn = conn.And(builder.Eq{"id": w.WebPayReqs.PayChannelId}) + w.Channel, w.CheckCode = services.GetAndCheckPayChannel(&paychannelmodel.PayChannel{}, conn) +} + +func (w *PayCheck) CheckMerchant() { + conn := builder.NewCond() + conn = conn.And(builder.Eq{"id": w.AppCheck.App.MerchantId}) + w.Merchant, w.CheckCode = services.GetAndCheckMerchant(&merchantmodel.Merchant{}, conn) +} + +func (w *PayCheck) CheckOrder() { + cond := builder.NewCond() + cond = cond.And(builder.Eq{"out_tread_no": w.WebPayReqs.OutTradeNo}, builder.Eq{"app_id": w.AppCheck.AppId}, builder.Neq{"status": common.ORDER_STATUS_CLOSE}) + order, code := services.OrderFindOne(&ordersmodel.Orders{}, cond) + if code == errorcode.OrdersExist { + w.OldOrder = order + w.CheckCode = code + } + return +} diff --git a/app/services/thirdpay/do/pay_way.go b/app/services/thirdpay/do/pay_way.go new file mode 100644 index 0000000..b367964 --- /dev/null +++ b/app/services/thirdpay/do/pay_way.go @@ -0,0 +1,28 @@ +package do + +import ( + "PaymentCenter/app/constants/common" + "PaymentCenter/app/third/paymentService" + "github.com/bytedance/sonic" +) + +var PayWayList = map[int]func(commonPayInfo *paymentService.PayOrderRequest, ExtJson string) error{ + common.PAY_CHANNEL_WECHAT_H5: WechatH5, + common.PAY_CHANNEL_ALIPAY_WEB: AlipayWeb, +} + +func WechatH5(commonPayInfo *paymentService.PayOrderRequest, ExtJson string) error { + err := sonic.Unmarshal([]byte(ExtJson), &commonPayInfo.Wx) + if err != nil { + return err + } + return nil +} + +func AlipayWeb(commonPayInfo *paymentService.PayOrderRequest, ExtJson string) error { + err := sonic.Unmarshal([]byte(ExtJson), &commonPayInfo.Ali) + if err != nil { + return err + } + return nil +} diff --git a/app/services/thirdpay/notify/notify.go b/app/services/thirdpay/notify/notify.go new file mode 100644 index 0000000..8cad419 --- /dev/null +++ b/app/services/thirdpay/notify/notify.go @@ -0,0 +1,164 @@ +package notify + +import ( + "PaymentCenter/app/constants/common" + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/http/entities" + "PaymentCenter/app/models/appmodel" + "PaymentCenter/app/models/ordersmodel" + "PaymentCenter/app/services" + "PaymentCenter/app/utils/httpclient" + "github.com/bytedance/sonic" + "time" + "xorm.io/builder" +) + +type OrderNotify struct { + order *ordersmodel.Orders + code int + app *appmodel.App + + OrderId int64 + ErrCode int + ActualAmount int + Msg string + CompleteTime time.Time +} + +type OrderNotifyResp struct { + OrderId int64 + Send bool + ErrCode int + SendTime time.Time + Content *OrderNotify +} + +type OrderNotifySendContent struct { + OrderId int64 `json:"order_id"` + OutTreadNo string `json:"out_tread_no"` + CompleteTime time.Time `json:"complete_time"` + Status int `json:"status"` + Msg string `json:"msg"` + ErrCode int `json:"err_code"` + AppId int64 `json:"app_id"` + ChannelId int64 `json:"channel_id"` + MerchantId int64 `json:"merchant_id"` +} + +func NewOrderNotifyWithHandle(orderId int64, errCode int, actualAmount int, msg string) *OrderNotifyResp { + orderNotify := &OrderNotify{ + OrderId: orderId, + ErrCode: errCode, + ActualAmount: actualAmount, + Msg: msg, + code: errorcode.Success, + } + return orderNotify.handle() +} + +func (o *OrderNotify) NotifyRespFail(errorCode int) *OrderNotifyResp { + return &OrderNotifyResp{ + OrderId: o.OrderId, + Send: false, + ErrCode: errorCode, + Content: o, + } +} + +func (o *OrderNotify) handle() (res *OrderNotifyResp) { + o.checkOrder() + if o.code != errorcode.Success { + return o.NotifyRespFail(o.code) + } + o.checkApp() + if o.code != errorcode.Success { + return o.NotifyRespFail(o.code) + } + o.updateOrder() + if o.code != errorcode.Success { + return o.NotifyRespFail(o.code) + } + o.sendNotify(o.setBody()) + if o.code != errorcode.Success { + return o.NotifyRespFail(o.code) + } + return &OrderNotifyResp{ + OrderId: o.OrderId, + Send: true, + ErrCode: o.ErrCode, + Content: o, + } +} + +func (o *OrderNotify) sendNotify(body *OrderNotifySendContent) { + + bodyByte, _ := sonic.Marshal(&body) + + headers := make(map[string]string, 1) + headers["Content-Type"] = "application/json" + resByte, err := httpclient.FastHttpPost(o.app.NotifyUrl, headers, bodyByte, 0) + if err != nil || string(resByte) != "success" { + o.code = errorcode.NotifySendFail + } + return +} + +func (o *OrderNotify) setBody() *OrderNotifySendContent { + return &OrderNotifySendContent{ + OrderId: o.OrderId, + OutTreadNo: o.order.OutTreadNo, + CompleteTime: o.CompleteTime, + Status: o.order.Status, + Msg: o.Msg, + ErrCode: o.ErrCode, + AppId: o.order.AppId, + ChannelId: o.order.PayChannelId, + MerchantId: o.order.MerchantId, + } +} + +func (o *OrderNotify) updateOrder() { + if o.ErrCode == errorcode.Success { + o.order.Status = common.ORDER_STATUS_FAILED + } else { + o.order.Status = common.ORDER_STATUS_PAYED + } + o.code = services.OrderUpdate(o.order, "status") + return +} + +func (o *OrderNotify) checkApp() { + o.app, o.code = services.AppFindOne(entities.IdRequest{Id: o.order.AppId}) + if o.code != errorcode.Success { + return + } + if o.app.DeleteTime.IsZero() { + o.code = errorcode.AppDisabled + return + } + if o.app.NotifyUrl == "" { + o.code = errorcode.AppNotifyUrlNotFound + return + } + + return +} + +func (o *OrderNotify) checkOrder() { + cond := builder.NewCond() + cond = cond.And(builder.Eq{"id": o.OrderId}) + + o.order, o.code = services.OrderFindOne(&ordersmodel.Orders{}, cond) + if o.code != errorcode.Success { + return + } + if o.order.DeleteTime.IsZero() { + o.code = errorcode.OrderIsDelete + return + } + if o.order.Status != common.ORDER_STATUS_PAYING { + o.code = errorcode.OrderStatusErr + return + } + return +} diff --git a/app/services/thirdpay/pay.go b/app/services/thirdpay/pay.go new file mode 100644 index 0000000..2396c00 --- /dev/null +++ b/app/services/thirdpay/pay.go @@ -0,0 +1,62 @@ +package thirdpay + +import ( + "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/http/entities/front" + "PaymentCenter/app/models/ordersmodel" + "PaymentCenter/app/services" + thirdpay "PaymentCenter/app/services/thirdpay/do" + "PaymentCenter/app/services/thirdpay/types" + "context" +) + +func ThirdPayWeb(check *thirdpay.PayCheck) *thirdpay.Pay { + pay := thirdpay.NewPay(check) + // 创建订单 + pay.CreateOrder() + if pay.PayCode != errorcode.Success { + return pay + } + // 支付 + pay.Pay() + return pay +} + +func ThirdPayCheck(ctx context.Context, req *front.PayReqs, appCheck *services.AppCheck, ip string) (check *thirdpay.PayCheck) { + check = thirdpay.NewPayCheck(&ctx, req, appCheck, ip) + // 校验表单 + check.CheckForm() + if check.CheckCode != errorcode.Success { + return + } + // 校验订单 + check.CheckOrder() + if check.CheckCode != errorcode.Success { + return + } + // 校验商户 + check.CheckMerchant() + if check.CheckCode != errorcode.Success { + return + } + // 校验支付通道 + check.CheckPayChannel() + if check.CheckCode != errorcode.Success { + return + } + + return +} + +func PayCallBack(order *ordersmodel.Orders, isNew bool) *types.PayResp { + res := &types.PayResp{ + OrderNo: order.Id, + OrderType: order.OrderType, + Amount: order.Amount, + Desc: order.Desc, + IsNewCreate: isNew, + Status: order.Status, + } + + return res +} diff --git a/app/services/thirdpay/types/callback.go b/app/services/thirdpay/types/callback.go new file mode 100644 index 0000000..aab4b4a --- /dev/null +++ b/app/services/thirdpay/types/callback.go @@ -0,0 +1,10 @@ +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"` +} diff --git a/app/third/paymentService/payment_service.go b/app/third/paymentService/payment_service.go index 266f549..2ba409a 100644 --- a/app/third/paymentService/payment_service.go +++ b/app/third/paymentService/payment_service.go @@ -39,6 +39,14 @@ type AliPay struct { 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 { Code int `json:"code"` ErrorMsg string `json:"error_msg"` diff --git a/app/utils/encrypt/rsa/rsa_test.go b/app/utils/encrypt/rsa/rsa_test.go index 5425c75..b3547b8 100644 --- a/app/utils/encrypt/rsa/rsa_test.go +++ b/app/utils/encrypt/rsa/rsa_test.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "fmt" "testing" - "time" ) const ( @@ -14,8 +13,8 @@ const ( ) func TestRsaEncrypt(t *testing.T) { - fmt.Println(time.Now().UnixNano() / (1000000)) - //fmt.Printf("%s\n", encrypt()) + + fmt.Printf("%s\n", encrypt()) } func TestRsaDecrypt(t *testing.T) { @@ -28,7 +27,7 @@ func TestRsaDecrypt(t *testing.T) { } func encrypt() string { - data := "{\"name\":\"张三\",\"sex\":1,\"is_human\":true}" + 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) pub := `-----BEGIN PUBLIC KEY----- ` + PUB + `