Compare commits
No commits in common. "b3d8acba936d56deec907b7f427ba178d5089828" and "f6bdfdf84c64750a8da2dccfad2ae322b69c3759" have entirely different histories.
b3d8acba93
...
f6bdfdf84c
|
@ -7,6 +7,8 @@ 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/models/paychannelmodel"
|
||||||
"PaymentCenter/app/services/thirdpay/thirdpay_notify"
|
"PaymentCenter/app/services/thirdpay/thirdpay_notify"
|
||||||
"PaymentCenter/app/third/paymentService"
|
"PaymentCenter/app/third/paymentService"
|
||||||
"PaymentCenter/app/third/paymentService/payCommon"
|
"PaymentCenter/app/third/paymentService/payCommon"
|
||||||
|
@ -14,7 +16,6 @@ import (
|
||||||
"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"
|
||||||
|
@ -85,12 +86,6 @@ func closeOrder() {
|
||||||
utils.Log(nil, "关闭订单,上游失败", response, orderInfo.Id)
|
utils.Log(nil, "关闭订单,上游失败", response, orderInfo.Id)
|
||||||
}
|
}
|
||||||
orderIds = append(orderIds, 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()
|
cond = builder.NewCond()
|
||||||
|
@ -195,7 +190,24 @@ func queryOrder() {
|
||||||
notifyResult := thirdpay_notify.NewOrderNotifyWithHandle(orderInfo.Id, status, int(result.Result.PayerTotal), msg)
|
notifyResult := thirdpay_notify.NewOrderNotifyWithHandle(orderInfo.Id, status, int(result.Result.PayerTotal), msg)
|
||||||
//utils.Log(nil, "主动查询订单支付状态,回调下游", notifyResult)
|
//utils.Log(nil, "主动查询订单支付状态,回调下游", notifyResult)
|
||||||
if notifyResult.ErrCode != errorcode.Success {
|
if notifyResult.ErrCode != errorcode.Success {
|
||||||
utils.Log(nil, "主动查询订单支付状态,回调下游失败", fmt.Sprintf("%+v", notifyResult))
|
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)
|
}(orderInfo)
|
||||||
|
@ -300,6 +312,23 @@ func queryRefundOrder() {
|
||||||
if notifyResult.ErrCode != errorcode.Success {
|
if notifyResult.ErrCode != errorcode.Success {
|
||||||
utils.Log(nil, "查询退款订单状态,回调下游失败", notifyResult)
|
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)
|
}(orderInfo)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
|
@ -34,11 +34,7 @@ func AppList(c *gin.Context) {
|
||||||
|
|
||||||
func AppCreate(c *gin.Context) {
|
func AppCreate(c *gin.Context) {
|
||||||
req, _ := controllers.GetRequest(c).(*backend.AppCreateRequest)
|
req, _ := controllers.GetRequest(c).(*backend.AppCreateRequest)
|
||||||
payChannel, err := req.RequestToDb()
|
payChannel := req.RequestToDb()
|
||||||
if err != nil {
|
|
||||||
controllers.Error(c, errorcode.ParamError, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
code := services.AppCreate(&payChannel)
|
code := services.AppCreate(&payChannel)
|
||||||
|
|
||||||
data := backend.AppResponse{}
|
data := backend.AppResponse{}
|
||||||
|
@ -48,11 +44,7 @@ func AppCreate(c *gin.Context) {
|
||||||
|
|
||||||
func AppUpdate(c *gin.Context) {
|
func AppUpdate(c *gin.Context) {
|
||||||
req, _ := controllers.GetRequest(c).(*backend.AppUpdateRequest)
|
req, _ := controllers.GetRequest(c).(*backend.AppUpdateRequest)
|
||||||
payChannel, err := req.RequestToDb()
|
payChannel := req.RequestToDb()
|
||||||
if err != nil {
|
|
||||||
controllers.Error(c, errorcode.ParamError, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
code := services.AppUpdate(&payChannel)
|
code := services.AppUpdate(&payChannel)
|
||||||
|
|
||||||
data := backend.AppResponse{}
|
data := backend.AppResponse{}
|
||||||
|
|
|
@ -3,8 +3,6 @@ package backend
|
||||||
import (
|
import (
|
||||||
"PaymentCenter/app/http/entities"
|
"PaymentCenter/app/http/entities"
|
||||||
"PaymentCenter/app/models/appmodel"
|
"PaymentCenter/app/models/appmodel"
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppListRequest struct {
|
type AppListRequest struct {
|
||||||
|
@ -56,9 +54,7 @@ type AppCreateRequest struct {
|
||||||
NotifyUrl string `json:"notify_url" validate:"required" label:"通知地址"`
|
NotifyUrl string `json:"notify_url" validate:"required" label:"通知地址"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AppCreateRequest) RequestToDb() (db appmodel.App, err error) {
|
func (a *AppCreateRequest) RequestToDb() (db appmodel.App) {
|
||||||
// 判断通知地址url是否合法
|
|
||||||
if a.NotifyUrl == "" || strings.HasPrefix(a.NotifyUrl, "http://") || strings.HasPrefix(a.NotifyUrl, "https://") {
|
|
||||||
db.MerchantId = a.MerchantId
|
db.MerchantId = a.MerchantId
|
||||||
db.AppName = a.AppName
|
db.AppName = a.AppName
|
||||||
db.AppRemark = a.AppRemark
|
db.AppRemark = a.AppRemark
|
||||||
|
@ -69,11 +65,6 @@ func (a *AppCreateRequest) RequestToDb() (db appmodel.App, err error) {
|
||||||
db.MerchantPublicKey = a.MerchantPublicKey
|
db.MerchantPublicKey = a.MerchantPublicKey
|
||||||
db.WhiteIp = a.WhiteIp
|
db.WhiteIp = a.WhiteIp
|
||||||
db.NotifyUrl = a.NotifyUrl
|
db.NotifyUrl = a.NotifyUrl
|
||||||
} else {
|
|
||||||
err = errors.New("通知地址格式不正确")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,9 +81,7 @@ type AppUpdateRequest struct {
|
||||||
NotifyUrl string `json:"notify_url"`
|
NotifyUrl string `json:"notify_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AppUpdateRequest) RequestToDb() (db appmodel.App, err error) {
|
func (a *AppUpdateRequest) RequestToDb() (db appmodel.App) {
|
||||||
// 判断通知地址url是否合法
|
|
||||||
if a.NotifyUrl == "" || strings.HasPrefix(a.NotifyUrl, "http://") || strings.HasPrefix(a.NotifyUrl, "https://") {
|
|
||||||
db.Id = a.Id
|
db.Id = a.Id
|
||||||
db.AppName = a.AppName
|
db.AppName = a.AppName
|
||||||
db.AppRemark = a.AppRemark
|
db.AppRemark = a.AppRemark
|
||||||
|
@ -103,10 +92,6 @@ func (a *AppUpdateRequest) RequestToDb() (db appmodel.App, err error) {
|
||||||
db.MerchantPublicKey = a.MerchantPublicKey
|
db.MerchantPublicKey = a.MerchantPublicKey
|
||||||
db.WhiteIp = a.WhiteIp
|
db.WhiteIp = a.WhiteIp
|
||||||
db.NotifyUrl = a.NotifyUrl
|
db.NotifyUrl = a.NotifyUrl
|
||||||
} else {
|
|
||||||
err = errors.New("通知地址格式不正确")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -61,7 +61,7 @@ func AppUpdate(app *appmodel.App) (code int) {
|
||||||
conn = conn.And(builder.Eq{"Id": app.Id})
|
conn = conn.And(builder.Eq{"Id": app.Id})
|
||||||
if app.AppName != "" {
|
if app.AppName != "" {
|
||||||
// 编辑页面更新,备注和白名单IP可更新为空
|
// 编辑页面更新,备注和白名单IP可更新为空
|
||||||
_, err = repo.AppUpdate(app, conn, "app_remark", "white_ip", "notify_url")
|
_, err = repo.AppUpdate(app, conn, "app_remark", "white_ip")
|
||||||
} else {
|
} else {
|
||||||
_, err = repo.AppUpdate(app, conn)
|
_, err = repo.AppUpdate(app, conn)
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ func OrderFindOne(order *ordersmodel.Orders, conn builder.Cond, col ...string) (
|
||||||
}
|
}
|
||||||
return nil, errorcode.SystemError
|
return nil, errorcode.SystemError
|
||||||
}
|
}
|
||||||
return orderInfo, errorcode.Success
|
return orderInfo, errorcode.OrdersExist
|
||||||
}
|
}
|
||||||
|
|
||||||
func PayOrderCheckRepeat(order *ordersmodel.Orders, conn builder.Cond) (exist bool, code int) {
|
func PayOrderCheckRepeat(order *ordersmodel.Orders, conn builder.Cond) (exist bool, code int) {
|
||||||
|
|
|
@ -3,13 +3,10 @@ package thirdpay_notify
|
||||||
import (
|
import (
|
||||||
"PaymentCenter/app/constants/common"
|
"PaymentCenter/app/constants/common"
|
||||||
"PaymentCenter/app/constants/errorcode"
|
"PaymentCenter/app/constants/errorcode"
|
||||||
"PaymentCenter/app/data"
|
|
||||||
"PaymentCenter/app/http/entities"
|
"PaymentCenter/app/http/entities"
|
||||||
"PaymentCenter/app/models/appmodel"
|
"PaymentCenter/app/models/appmodel"
|
||||||
"PaymentCenter/app/models/ordercallbacklogmodel"
|
|
||||||
"PaymentCenter/app/models/ordersmodel"
|
"PaymentCenter/app/models/ordersmodel"
|
||||||
"PaymentCenter/app/services"
|
"PaymentCenter/app/services"
|
||||||
"PaymentCenter/app/utils"
|
|
||||||
"PaymentCenter/app/utils/httpclient"
|
"PaymentCenter/app/utils/httpclient"
|
||||||
"github.com/bytedance/sonic"
|
"github.com/bytedance/sonic"
|
||||||
"time"
|
"time"
|
||||||
|
@ -94,14 +91,7 @@ func (o *OrderNotify) Handle() (res *OrderNotifyResp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送下游回调通知
|
|
||||||
func (o *OrderNotify) sendNotify(body *OrderNotifySendContent) {
|
func (o *OrderNotify) sendNotify(body *OrderNotifySendContent) {
|
||||||
if o.app.NotifyUrl == "" {
|
|
||||||
o.Code = errorcode.AppNotifyUrlNotFound
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var callbackStatus = common.STATUS_ENABLE
|
|
||||||
var response string
|
|
||||||
|
|
||||||
bodyByte, _ := sonic.Marshal(&body)
|
bodyByte, _ := sonic.Marshal(&body)
|
||||||
headers := make(map[string]string, 1)
|
headers := make(map[string]string, 1)
|
||||||
|
@ -109,24 +99,7 @@ func (o *OrderNotify) sendNotify(body *OrderNotifySendContent) {
|
||||||
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
|
||||||
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,10 +134,10 @@ func (o *OrderNotify) checkApp() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//if o.app.NotifyUrl == "" {
|
if o.app.NotifyUrl == "" {
|
||||||
// o.Code = errorcode.AppNotifyUrlNotFound
|
o.Code = errorcode.AppNotifyUrlNotFound
|
||||||
// return
|
return
|
||||||
//}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
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
|
||||||
|
}
|
Loading…
Reference in New Issue