diff --git a/app/console/command.go b/app/console/command.go index 4ba35b5..cf36bcc 100644 --- a/app/console/command.go +++ b/app/console/command.go @@ -7,8 +7,6 @@ import ( "PaymentCenter/app/http/entities" "PaymentCenter/app/http/entities/backend" "PaymentCenter/app/models/ordersmodel" - "PaymentCenter/app/models/orderthirdpaylogmodel" - "PaymentCenter/app/models/paychannelmodel" "PaymentCenter/app/services/thirdpay/thirdpay_notify" "PaymentCenter/app/third/paymentService" "PaymentCenter/app/third/paymentService/payCommon" @@ -16,6 +14,7 @@ import ( "PaymentCenter/config" "context" "encoding/json" + "fmt" "github.com/qit-team/snow-core/command" "strconv" "sync" @@ -86,6 +85,12 @@ func closeOrder() { utils.Log(nil, "关闭订单,上游失败", response, orderInfo.Id) } orderIds = append(orderIds, orderInfo.Id) + // 回调通知下游 + notifyResult := thirdpay_notify.NewOrderNotifyWithHandle(orderInfo.Id, common.ORDER_STATUS_CLOSE, 0, "长时间未支付关闭订单") + //utils.Log(nil, "主动查询订单支付状态,回调下游", notifyResult) + if notifyResult.ErrCode != errorcode.Success { + utils.Log(nil, "关闭订单,回调下游失败", fmt.Sprintf("%#v", notifyResult)) + } } // 修改订单状态为关闭 cond = builder.NewCond() @@ -190,24 +195,7 @@ func queryOrder() { notifyResult := thirdpay_notify.NewOrderNotifyWithHandle(orderInfo.Id, status, int(result.Result.PayerTotal), msg) //utils.Log(nil, "主动查询订单支付状态,回调下游", notifyResult) if notifyResult.ErrCode != errorcode.Success { - utils.Log(nil, "主动查询订单支付状态,回调下游失败", notifyResult) - } - payCallback, _ := json.Marshal(result) - merchantCallback, _ := json.Marshal(notifyResult) - - thirdRepo := data.NewOrderThirdPayLogRepo(paychannelmodel.GetInstance().GetDb()) - log := orderthirdpaylogmodel.OrderThirdPayLog{ - OrderId: orderInfo.Id, - PayCallback: string(payCallback), - Status: 1, - PayParam: "", - MerchantCallback: string(merchantCallback), - Type: common.THIRD_ORDER_TYPE_CALL_BACK, - } - // 写日志 - _, err = thirdRepo.OrderThirdPayLogInsertOne(&log) - if err != nil { - utils.Log(nil, "主动查询订单支付状态,记录回调日志失败", log.OrderId, err) + utils.Log(nil, "主动查询订单支付状态,回调下游失败", fmt.Sprintf("%#v", notifyResult)) } } }(orderInfo) @@ -312,23 +300,6 @@ func queryRefundOrder() { if notifyResult.ErrCode != errorcode.Success { utils.Log(nil, "查询退款订单状态,回调下游失败", notifyResult) } - payCallback, _ := json.Marshal(result) - merchantCallback, _ := json.Marshal(notifyResult) - - thirdRepo := data.NewOrderThirdPayLogRepo(paychannelmodel.GetInstance().GetDb()) - log := orderthirdpaylogmodel.OrderThirdPayLog{ - OrderId: orderInfo.Id, - PayCallback: string(payCallback), - Status: 1, - PayParam: "", - MerchantCallback: string(merchantCallback), - Type: common.THIRD_ORDER_TYPE_CALL_BACK, - } - // 写日志 - _, err = thirdRepo.OrderThirdPayLogInsertOne(&log) - if err != nil { - utils.Log(nil, "查询退款订单状态,记录回调日志失败", log.OrderId, err) - } } }(orderInfo) } diff --git a/app/data/order_callback_log.go b/app/data/order_callback_log.go new file mode 100644 index 0000000..e8fa2f2 --- /dev/null +++ b/app/data/order_callback_log.go @@ -0,0 +1,39 @@ +package data + +import ( + "PaymentCenter/app/http/entities" + "PaymentCenter/app/models/ordercallbacklogmodel" + "xorm.io/builder" + "xorm.io/xorm" +) + +type OrderCallbackLogRepo struct { + repo xorm.Interface +} + +func NewOrderCallbackLogRepo(repo xorm.Interface) *OrderCallbackLogRepo { + return &OrderCallbackLogRepo{ + repo: repo, + } +} + +func (m *OrderCallbackLogRepo) OrderCallbackLogList(conn builder.Cond, pageFilter entities.PageRequest, orderLogList *[]ordercallbacklogmodel.OrderCallbackLog) (int64, error) { + repo := m.repo.Where(conn) + if pageFilter.Page > 0 { + repo = repo.Limit(pageFilter.PageSize, pageFilter.PageSize*(pageFilter.Page-1)) + } + return repo.Desc("create_time").FindAndCount(orderLogList) +} + +func (m *OrderCallbackLogRepo) OrderCallbackLogInsertOne(orderLog *ordercallbacklogmodel.OrderCallbackLog) (int64, error) { + return m.repo.InsertOne(orderLog) +} + +func (m *OrderCallbackLogRepo) OrderCallbackLogDelete(orderLog *ordercallbacklogmodel.OrderCallbackLog, conn builder.Cond) (int64, error) { + return m.repo.Where(conn).Delete(orderLog) +} + +// columns 参数为要更新的字段 +func (m *OrderCallbackLogRepo) OrderCallbackLogUpdate(orderLog *ordercallbacklogmodel.OrderCallbackLog, conn builder.Cond, columns ...string) (int64, error) { + return m.repo.Where(conn).MustCols(columns...).Update(orderLog) +} diff --git a/app/models/ordercallbacklogmodel/order_callback_log.go b/app/models/ordercallbacklogmodel/order_callback_log.go new file mode 100644 index 0000000..c736749 --- /dev/null +++ b/app/models/ordercallbacklogmodel/order_callback_log.go @@ -0,0 +1,41 @@ +package ordercallbacklogmodel + +import ( + "github.com/qit-team/snow-core/db" + "sync" + "time" +) + +var ( + once sync.Once + m *OrderCallbackLogModel +) + +// 实体 +type OrderCallbackLog struct { + Id int64 `xorm:"'id' bigint(20) pk autoincr"` + OrderId int64 `xorm:"'order_id' bigint(20)"` + MerchantRequest string `xorm:"'merchant_request' JSON"` + Status int `xorm:"'status' int(11)"` + MerchantResponse string `xorm:"'merchant_response' JSON"` + CreateTime time.Time `xorm:"'create_time' datetime created"` +} + +// 表名 +func (m *OrderCallbackLog) TableName() string { + return "order_callback_log" +} + +// 私有化,防止被外部new +type OrderCallbackLogModel struct { + db.Model //组合基础Model,集成基础Model的属性和方法 +} + +// 单例模式 +func GetInstance() *OrderCallbackLogModel { + once.Do(func() { + m = new(OrderCallbackLogModel) + //m.DiName = "" //设置数据库实例连接,默认db.SingletonMain + }) + return m +} diff --git a/app/services/order.go b/app/services/order.go index ff46a96..90da97c 100644 --- a/app/services/order.go +++ b/app/services/order.go @@ -103,7 +103,7 @@ func OrderFindOne(order *ordersmodel.Orders, conn builder.Cond, col ...string) ( } return nil, errorcode.SystemError } - return orderInfo, errorcode.OrdersExist + return orderInfo, errorcode.Success } func PayOrderCheckRepeat(order *ordersmodel.Orders, conn builder.Cond) (exist bool, code int) { diff --git a/app/services/thirdpay/thirdpay_notify/notify.go b/app/services/thirdpay/thirdpay_notify/notify.go index 70e0798..0a8e752 100644 --- a/app/services/thirdpay/thirdpay_notify/notify.go +++ b/app/services/thirdpay/thirdpay_notify/notify.go @@ -3,10 +3,13 @@ package thirdpay_notify import ( "PaymentCenter/app/constants/common" "PaymentCenter/app/constants/errorcode" + "PaymentCenter/app/data" "PaymentCenter/app/http/entities" "PaymentCenter/app/models/appmodel" + "PaymentCenter/app/models/ordercallbacklogmodel" "PaymentCenter/app/models/ordersmodel" "PaymentCenter/app/services" + "PaymentCenter/app/utils" "PaymentCenter/app/utils/httpclient" "github.com/bytedance/sonic" "time" @@ -91,7 +94,10 @@ func (o *OrderNotify) Handle() (res *OrderNotifyResp) { } } +// 发送下游回调通知 func (o *OrderNotify) sendNotify(body *OrderNotifySendContent) { + var callbackStatus = common.STATUS_ENABLE + var response string bodyByte, _ := sonic.Marshal(&body) headers := make(map[string]string, 1) @@ -99,7 +105,24 @@ func (o *OrderNotify) sendNotify(body *OrderNotifySendContent) { resByte, err := httpclient.FastHttpPost(o.app.NotifyUrl, headers, bodyByte, 0) if err != nil || string(resByte) != "success" { o.Code = errorcode.NotifySendFail + callbackStatus = common.STATUS_DISABLED + response = "url=" + o.app.NotifyUrl + "|error=" + err.Error() } + response = string(resByte) + response + // 记录回调日志 + go func(orderId int64, status int, request, response string) { + repo := data.NewOrderCallbackLogRepo(ordercallbacklogmodel.GetInstance().GetDb()) + log := ordercallbacklogmodel.OrderCallbackLog{ + OrderId: orderId, + MerchantRequest: string(bodyByte), + Status: status, + MerchantResponse: response, + } + _, insertErr := repo.OrderCallbackLogInsertOne(&log) + if insertErr != nil { + utils.Log(nil, "回调写入日志error", insertErr) + } + }(o.OrderId, callbackStatus, string(bodyByte), response) return } diff --git a/app/third/market/config.go b/app/third/market/config.go deleted file mode 100644 index ef5449a..0000000 --- a/app/third/market/config.go +++ /dev/null @@ -1,90 +0,0 @@ -package market - -import ( - "PaymentCenter/config" - "bytes" - "encoding/json" - "github.com/pkg/errors" - "io/ioutil" - "net/http" -) - -type MarketClient struct { - cfg config.MarketConfig -} - -type MarketSendRequest struct { - AppId string `json:"app_id"` //APP ID - Sign string `json:"sign"` //签名 - ReqCode string `json:"req_code"` //固定值:voucher.create - MemId string `json:"mem_id"` //商户号 - ReqSerialNo string `json:"req_serial_no"` //请求唯一流水号 最大32位 - TimeTamp string `json:"timestamp"` //时间戳 yyyyMMddHHmmss - PosId string `json:"pos_id"` //商户方平台号 - VoucherId string `json:"voucher_id"` //制码批次号 - VoucherNum int `json:"voucher_num"` //请券数量,默认是 1 - MobileNo string `json:"mobile_no"` //11 手机号,可传空字符串 - SendMsg string `json:"send_msg"` //是否发送短信:2- 发送 1-不发送 -} - -type MarketSenResponse struct { - VoucherId string `json:"voucher_id"` //制码批次号 - VoucherCode string `json:"voucher_code"` //券码 - ShortUrl string `json:"short_url"` //含二维码、条码的短链接 - VoucherSdate string `json:"voucher_sdate"` //有效期起 - VoucherEdate string `json:"voucher_edate"` //有效期止 - CodeType string `json:"code_type"` //码类型: 00- 代金券 01- 满减券 -} - -type MarketResponse struct { - ErrCode string `json:"errCode"` //00-成功 其他:失败 - Msg string `json:"msg"` //描 述 (失败时必填) - Data MarketSenResponse `json:"data"` -} - -func (this *MarketSendRequest) toMap() (resultMap map[string]interface{}) { - // Marshal the struct to JSON, ignoring omitempty fields. - jsonBytes, err := json.Marshal(this) - if err != nil { - return - } - // Unmarshal the JSON into a map to get the final result. - err = json.Unmarshal(jsonBytes, &resultMap) - if err != nil { - return - } - - return resultMap -} - -func (this *MarketClient) doPost(url string, jsonBytes []byte) (body []byte, err error) { - // 创建POST请求 - url = this.cfg.Host + url - req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBytes)) - if err != nil { - return - } - - // 设置Content-Type头 - req.Header.Set("Content-Type", "application/json") - - // 创建HTTP客户端 - client := &http.Client{} - - // 发送请求并处理响应 - resp, err := client.Do(req) - if err != nil { - return - } - defer resp.Body.Close() - // 读取响应体 - if resp.StatusCode != http.StatusOK { - err = errors.New("HTTP request failed: " + resp.Status) - return - } - body, err = ioutil.ReadAll(resp.Body) - if err != nil { - return - } - return -} diff --git a/app/third/market/market_api.go b/app/third/market/market_api.go deleted file mode 100644 index 840b6b2..0000000 --- a/app/third/market/market_api.go +++ /dev/null @@ -1,62 +0,0 @@ -package market - -import ( - "PaymentCenter/app/utils/encrypt" - "PaymentCenter/config" - "encoding/json" - "time" -) - -func NewMarketClient(cfg config.MarketConfig) *MarketClient { - cfg.Sign = "-----BEGIN RSA PRIVATE KEY-----\n" + cfg.Sign + "\n-----END RSA PRIVATE KEY-----" - return &MarketClient{ - cfg: cfg, - } -} - -/* -MarketSend -券码生成接口 -- 请求地址:/openApi/v1/market/key/send -- 说明:发券接口应支持使用同一流水号进行重复请求,即:当调用该接口失败时,可 以使用同一流水号进行再次请求,接口需要根据请求的流水号进行判断,若无该流水 号的券码信息则新生成后返回,若有该流水号的券码信息则直接返回该券码的信息 -orderNo: 订单号 -VoucherId: 制码批次号 -MobileNo: 11 手机号,可传空字符串 -SendMsg: 是否发送短信:2- 发送 1-不发送 -*/ -func (this *MarketClient) MarketSend(orderNo, VoucherId, MobileNo, SendMsg string) (res MarketResponse, err error) { - url := "/openApi/v1/market/key/send" - request := MarketSendRequest{ - AppId: this.cfg.AppId, - ReqCode: this.cfg.ReqCode, - MemId: this.cfg.MemId, - PosId: this.cfg.PosId, - TimeTamp: time.Now().Format("20060102150405"), - VoucherId: VoucherId, - ReqSerialNo: orderNo, - VoucherNum: 1, - MobileNo: MobileNo, - SendMsg: SendMsg, - } - - request.Sign, err = MakeRsaSign(this.cfg.Sign, request.toMap()) - if err != nil { - return res, err - } - - bytes, err := json.Marshal(request) - if err != nil { - return res, err - } - - data, err := this.doPost(url, bytes) - if err != nil { - return res, err - } - err = json.Unmarshal(data, &res) - // 加密 - if len(res.Data.ShortUrl) > 0 { - res.Data.ShortUrl = encrypt.AesEncryptCBC([]byte(res.Data.ShortUrl), []byte(this.cfg.SecretKey)) - } - return res, err -} diff --git a/app/third/market/market_api_test.go b/app/third/market/market_api_test.go deleted file mode 100644 index 8654769..0000000 --- a/app/third/market/market_api_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package market - -import ( - "PaymentCenter/app/utils" - "PaymentCenter/config" - "fmt" - "github.com/qit-team/snow-core/kernel/server" - "os" - "testing" -) - -func TestMarketSendRequest_Market(t *testing.T) { - opts := config.GetOptions() - if opts.ShowVersion { - fmt.Printf("%s\ncommit %s\nbuilt on %s\n", server.Version, server.BuildCommit, server.BuildDate) - os.Exit(0) - } - - //加载配置 - conf, err := config.Load(opts.ConfFile) - if err != nil { - utils.Log(nil, "err", err.Error()) - return - } - client := NewMarketClient(conf.OpenApiMarketConfig) - - //data, err := client.MarketSend("123456789111", "1717567048171", "", "2") - data, err := client.MarketSend("123111", "1717", "", "2") - - if err != nil { - t.Error(err) - } - t.Log(data) -} diff --git a/app/third/market/rsa.go b/app/third/market/rsa.go deleted file mode 100644 index 1389433..0000000 --- a/app/third/market/rsa.go +++ /dev/null @@ -1,137 +0,0 @@ -package market - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "errors" - "fmt" - "sort" -) - -// getSignString 使用 xx=aa&yy=bb 的字符串拼接 -func getSignString(data map[string]interface{}) string { - keys := make([]string, 0, len(data)) - for key := range data { - keys = append(keys, key) - } - sort.Strings(keys) - - signString := "" - separator := "" - for _, key := range keys { - value := data[key] - if key == "sign" || value == nil { - continue - } - signString += fmt.Sprintf("%s%s=%v", separator, key, value) - separator = "&" - } - return signString -} - -// VerifyRsaSign 签名验证 -func VerifyRsaSign(publicKey string, data map[string]interface{}) (map[string]interface{}, error) { - // 对 sign nonce timestamp appId 升序排序 - // 使用 xx=aa&yy=bb 的字符串拼接 - // 商户的公钥验签 RSA2验签 - signString := getSignString(data) - - rsaPubKey, err := parseRSAPublicKeyFromPEM([]byte(publicKey)) - if err != nil { - return nil, err - } - - signature, err := base64.StdEncoding.DecodeString(data["sign"].(string)) - if err != nil { - return nil, err - } - - hashed := sha256.Sum256([]byte(signString)) - err = rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, hashed[:], signature) - if err != nil { - return nil, errors.New("签名验证失败") - } - - return data, nil -} - -// MakeRsaSign 生成签名 -func MakeRsaSign(privateKey string, data map[string]interface{}) (string, error) { - // 对 sign nonce timestamp appId 升序排序 - // 使用 xx=aa&yy=bb 的字符串拼接 - // 营销系统生成的私钥生成签名 RSA2加签 - signString := getSignString(data) - - privKey, err := parseRSAPrivateKeyFromPEM([]byte(privateKey)) - if err != nil { - return "", errors.New("私钥解析失败") - } - - hashed := sha256.Sum256([]byte(signString)) - signature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA256, hashed[:]) - if err != nil { - return "", errors.New("签名失败") - } - - return base64.StdEncoding.EncodeToString(signature), nil -} - -// ParseRSAPrivateKeyFromPEM 解析私钥 -func parseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, errors.New("私钥解析失败: 无效的PEM格式") - } - - var parsedKey interface{} - if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { - if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { - return nil, err - } - } - - var pkey *rsa.PrivateKey - var ok bool - if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { - return nil, errors.New("密钥不是有效的RSA私钥") - } - - return pkey, nil -} - -// parseRSAPublicKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 public key -func parseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { - var err error - - // Parse PEM block - var block *pem.Block - if block, _ = pem.Decode(key); block == nil { - return nil, errors.New("公钥解析失败: 无效的PEM格式") - } - - // Parse the key - var parsedKey interface{} - if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { - if cert, err := x509.ParseCertificate(block.Bytes); err == nil { - parsedKey = cert.PublicKey - } else { - return nil, err - } - } - - var pkey *rsa.PublicKey - var ok bool - if pkey, ok = parsedKey.(*rsa.PublicKey); !ok { - return nil, errors.New("密钥不是有效的RSA公钥") - } - - return pkey, nil -}