订单查询/退款
This commit is contained in:
parent
39cbe5a8ba
commit
ee7a93f73d
|
@ -5,5 +5,18 @@ const (
|
|||
TOKEN_Admin = "Admin_token_"
|
||||
ADMIN_V1 = "/admin/api/v1"
|
||||
|
||||
FRONT_API_V1 = "/api/v1"
|
||||
FRONT_API_V1 = "/api/v1"
|
||||
FRONT_API_V1_Auth = "/api/v1/auth"
|
||||
|
||||
STATUSABLED = 1
|
||||
|
||||
// 订单状态 1-待支付 2-已支付 3 充值完成 4充值异常 5卷链核销 6 退款中 7 退款完成 8 退款失败
|
||||
ORDER_STATUS_DEFAULT = 1
|
||||
ORDER_STATUS_PAY = 2
|
||||
ORDER_STATUS_FINISH = 3
|
||||
ORDER_STATUS_FAIL = 4
|
||||
ORDER_STATUS_OFFSET = 5
|
||||
ORDER_STATUS_ReFUNDING = 6
|
||||
ORDER_STATUS_ReFUNDEND = 7
|
||||
ORDER_STATUS_ReFUNDFAIL = 8
|
||||
)
|
||||
|
|
|
@ -21,6 +21,22 @@ const (
|
|||
|
||||
//未登录
|
||||
NotLogin = 1000
|
||||
|
||||
//抽奖中
|
||||
OrderLottery = 1001
|
||||
//商品不存在
|
||||
OrderProductNotExist = 1002
|
||||
//兴业下单失败
|
||||
XINYEOrderFAIL = 1003
|
||||
|
||||
//订单不允许退款
|
||||
OrderNOTAuthREFUND = 2001
|
||||
// 订单更新失败
|
||||
OrderRefundUpdateFail = 2002
|
||||
//退款失败
|
||||
OrderRefundFail = 2003
|
||||
//邮储服务异常
|
||||
YouChuOrderRefundFail = 2004
|
||||
)
|
||||
|
||||
var MsgEN = map[int]string{
|
||||
|
@ -33,11 +49,19 @@ var MsgEN = map[int]string{
|
|||
}
|
||||
|
||||
var MsgZH = map[int]string{
|
||||
Success: "请求成功",
|
||||
ParamError: "参数错误",
|
||||
NotFound: "数据不存在",
|
||||
NotAuth: "未经授权",
|
||||
NotLogin: "未登录",
|
||||
SystemError: "系统错误",
|
||||
Success: "请求成功",
|
||||
ParamError: "参数错误",
|
||||
NotFound: "数据不存在",
|
||||
NotAuth: "未经授权",
|
||||
NotLogin: "未登录",
|
||||
OrderLottery: "兑换中",
|
||||
OrderProductNotExist: "商品不存在",
|
||||
XINYEOrderFAIL: "三方下单失败",
|
||||
OrderNOTAuthREFUND: "该订单暂不支持退款",
|
||||
OrderRefundUpdateFail: "订单更新失败",
|
||||
OrderRefundFail: "退款失败",
|
||||
YouChuOrderRefundFail: "邮储服务异常",
|
||||
}
|
||||
var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH}
|
||||
|
||||
|
|
|
@ -175,3 +175,21 @@ func Frequence(key string) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func GetUserId(c *gin.Context) int {
|
||||
userIdStr, _ := c.Get("userId")
|
||||
if userIdStr != nil {
|
||||
var userId, _ = userIdStr.(int)
|
||||
return userId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func GetUserPhone(c *gin.Context) string {
|
||||
userIdStr, _ := c.Get("phone")
|
||||
if userIdStr != nil {
|
||||
var userId, _ = userIdStr.(string)
|
||||
return userId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package front
|
||||
|
||||
import (
|
||||
"github.com/ahmetb/go-linq/v3"
|
||||
"github.com/gin-gonic/gin"
|
||||
"qteam/app/constants/common"
|
||||
"qteam/app/constants/errorcode"
|
||||
"qteam/app/http/controllers"
|
||||
"qteam/app/http/entities/front"
|
||||
"qteam/app/models/ordersmodel"
|
||||
"qteam/app/services"
|
||||
"qteam/app/third/market"
|
||||
"qteam/app/utils"
|
||||
"qteam/config"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func CreateOrder(c *gin.Context) {
|
||||
var request = controllers.GetRequest(c).(*front.OrderCreateRequest)
|
||||
//userId := controllers.GetUserId(c)
|
||||
userId := 1
|
||||
code, data := services.CreateOrderService(userId, request.ProductId)
|
||||
controllers.HandCodeRes(c, data, code)
|
||||
}
|
||||
|
||||
func OrderList(c *gin.Context) {
|
||||
var request = controllers.GetRequest(c).(*front.OrderListRequest)
|
||||
//userId := controllers.GetUserId(c)
|
||||
userId := 1
|
||||
code, orderList, count := services.OrderQueryService(userId, request)
|
||||
var rsp []front.OrderQueryResponse
|
||||
if count > 0 {
|
||||
linq.From(orderList).SelectT(func(in ordersmodel.Orders) (out front.OrderQueryResponse) {
|
||||
out.ResponseFromDb(in)
|
||||
return out
|
||||
}).ToSlice(&rsp)
|
||||
}
|
||||
controllers.HandCodeRes(c, gin.H{"data": rsp, "count": count}, code)
|
||||
}
|
||||
|
||||
func OrderQuery(c *gin.Context) {
|
||||
var request = controllers.GetRequest(c).(*front.OrderQueryRequest)
|
||||
orderId, _ := strconv.Atoi(request.OrderId)
|
||||
order := ordersmodel.Orders{Id: orderId}
|
||||
var OrderQueryResponse front.OrderQueryResponse
|
||||
has, err := services.OrderDetailService(&order)
|
||||
if err != nil {
|
||||
controllers.Error(c, 500, "订单查询失败")
|
||||
return
|
||||
}
|
||||
if has {
|
||||
if order.State < common.ORDER_STATUS_PAY && utils.IsNil(order.VoucherLink) {
|
||||
_, rsp := services.YouChuOrderQuery(order)
|
||||
utils.Log(nil, "三方订单查询", rsp)
|
||||
if rsp.OrderSta == "03" {
|
||||
client := market.NewMarketClient(config.GetConf().OpenApiMarketConfig)
|
||||
data, err := client.MarketSend(order.OrderNo, strconv.Itoa(order.VoucherId), "", "1")
|
||||
if err != nil {
|
||||
controllers.Error(c, 500, "三方订单查询失败")
|
||||
return
|
||||
}
|
||||
if data.ErrCode == "00" {
|
||||
err := services.OrdersUpdateService(front.OrdersUpdateRequest{Id: order.Id, Status: common.ORDER_STATUS_PAY, VoucherLink: data.Data.ShortUrl})
|
||||
if err != nil {
|
||||
controllers.Error(c, 500, "订单更新失败")
|
||||
utils.Log(nil, "营销系统下单失败", data)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OrderQueryResponse.ResponseFromDb(order)
|
||||
controllers.Success(c, OrderQueryResponse, "请求成功")
|
||||
} else {
|
||||
controllers.HandCodeRes(c, nil, errorcode.NotFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func OrderRefund(c *gin.Context) {
|
||||
var request = controllers.GetRequest(c).(*front.OrderQueryRequest)
|
||||
//userId := controllers.GetUserId(c)
|
||||
userId := 1
|
||||
OrderId, _ := strconv.Atoi(request.OrderId)
|
||||
code := services.OrderRefundService(userId, OrderId)
|
||||
controllers.HandCodeRes(c, nil, code)
|
||||
}
|
||||
|
||||
func OrderNotify(c *gin.Context) {
|
||||
|
||||
}
|
|
@ -1 +1,14 @@
|
|||
package front
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"qteam/app/http/controllers"
|
||||
"qteam/app/http/entities/front"
|
||||
"qteam/app/services"
|
||||
)
|
||||
|
||||
func UnionLogin(c *gin.Context) {
|
||||
req := controllers.GetRequest(c).(*front.UnionLoginRequest)
|
||||
code, login := services.YouChuLogin(req)
|
||||
controllers.HandCodeRes(c, login, code)
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
package entities
|
|
@ -1 +0,0 @@
|
|||
package entities
|
|
@ -0,0 +1 @@
|
|||
package front
|
|
@ -0,0 +1,43 @@
|
|||
package front
|
||||
|
||||
import (
|
||||
"qteam/app/models/ordersmodel"
|
||||
"qteam/app/utils"
|
||||
)
|
||||
|
||||
type OrderCreateRequest struct {
|
||||
ProductId int `form:"product_id" validate:"required"`
|
||||
}
|
||||
|
||||
type OrderQueryRequest struct {
|
||||
OrderId string `form:"order_id" validate:"required"`
|
||||
}
|
||||
|
||||
type OrderListRequest struct {
|
||||
PageRequest
|
||||
}
|
||||
|
||||
type OrderQueryResponse struct {
|
||||
Id int `json:"id"`
|
||||
OrderNo string `json:"order_no"`
|
||||
UserId int `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
Mobile string `json:"mobile"`
|
||||
ProductId int `json:"product_id"`
|
||||
ProductName string `json:"product_name"`
|
||||
State int `json:"state"`
|
||||
VoucherLink string `json:"voucher_link"`
|
||||
CreateTime string `json:"create_time"`
|
||||
}
|
||||
|
||||
func (p *OrderQueryResponse) ResponseFromDb(l ordersmodel.Orders) {
|
||||
utils.EntityCopy(p, &l)
|
||||
p.CreateTime = l.CreateTime.Format("2006-01-02 15:04:05")
|
||||
return
|
||||
}
|
||||
|
||||
type OrdersUpdateRequest struct {
|
||||
Id int `json:"id" validate:"required" form:"id" validate:"required" example:"1"`
|
||||
Status int `json:"status" form:"status" validate:"oneof=1 2 3 4" example:"1"` // '状态(1/待支付,2/已支付,3/已完成4/取消5作废)'
|
||||
VoucherLink string `json:"voucher_link"`
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package front
|
|
@ -0,0 +1,16 @@
|
|||
package front
|
||||
|
||||
type UnionLoginRequest struct {
|
||||
Code string `form:"code" json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
type YouChuDecryptData struct {
|
||||
Mobile string `json:"phone"`
|
||||
CerNo string `json:"cerNo"`
|
||||
Time string `json:"time"`
|
||||
UserId string `json:"userId"`
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package entities
|
||||
package front
|
||||
|
||||
import (
|
||||
"qteam/app/models/brandmodel"
|
|
@ -0,0 +1,110 @@
|
|||
package front
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type YouChuRequest struct {
|
||||
Request string `json:"request"`
|
||||
Signature string `json:"signature"`
|
||||
AccessToken string `json:"accessToken"`
|
||||
EncryptKey string `json:"encryptKey"`
|
||||
}
|
||||
|
||||
type YouChuRequestBody struct {
|
||||
Body YouChuOrderRequest `json:"body"`
|
||||
Head YouChuRequestHeader `json:"head"`
|
||||
}
|
||||
|
||||
type YouChuRequestHeader struct {
|
||||
PartnerTxSriNo string `json:"partnerTxSriNo"`
|
||||
Method string `json:"method"`
|
||||
Version string `json:"version"`
|
||||
MerchantId string `json:"merchantId"`
|
||||
AppID string `json:"appID"`
|
||||
AccessType string `json:"accessType"`
|
||||
Reserve string `json:"reserve"`
|
||||
}
|
||||
|
||||
type YouChuOrderRequest struct {
|
||||
BusiMainId string `json:"busiMainId"`
|
||||
ReqTransTime string `json:"reqTransTime"`
|
||||
Data OrderQuery `json:"data"`
|
||||
}
|
||||
|
||||
type OrderQuery struct {
|
||||
TxnCode string `json:"txnCode"` //固定值 1004
|
||||
SourceId string `json:"sourceId"` //固定值 16
|
||||
ReqDate string `json:"reqDate"` //YYYYMMDDHHmmss
|
||||
ReqTraceId string `json:"reqTraceId"` //订单号
|
||||
MchtNo string `json:"mchtNo"` //收单商户号
|
||||
OldSeqNo string `json:"oldSeqNo"` //收单商户号
|
||||
TxnDt string `json:"txnDt"` //YYYYMMDD
|
||||
}
|
||||
|
||||
type InsertOrderResponse struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
NotifyUrl string `json:"notify_url"`
|
||||
}
|
||||
|
||||
type YouChuOrderQueryResponse struct {
|
||||
RespCode string `json:"code"`
|
||||
RespMsg string `json:"respMsg"`
|
||||
RespCd string `json:"respCd"`
|
||||
ReqTraceId string `json:"reqTraceId"` //请求方流水号或者订单号,
|
||||
OrderNo string `json:"orderNo"` //统一收单订单号
|
||||
OrderSta string `json:"orderSta"` //03-支付成功 04-支付失败 05-检查失败
|
||||
TxnFg string `json:"txnFg"` //0-正常 2-已部分退货 3- 已全部退货
|
||||
}
|
||||
|
||||
type YouChuOrderRefundResponse struct {
|
||||
RespCode string `json:"code"`
|
||||
RespMsg string `json:"respMsg"`
|
||||
RespCd string `json:"respCd"`
|
||||
TxnCode string `json:"txnCode"`
|
||||
SourceId string `json:"sourceId"`
|
||||
ReqTraceId string `json:"ReqTraceId"`
|
||||
RefundOrderNo string `json:"refundOrderNo"`
|
||||
RefundOrderSta string `json:"refundOrderSta"`
|
||||
MchtNo string `json:"mchtNo"`
|
||||
TxnAmt string `json:"txnAmt"`
|
||||
}
|
||||
|
||||
type YouChuOrderRefundRequest struct {
|
||||
Body RefundRequestBody `json:"body"`
|
||||
Head YouChuRequestHeader `json:"head"`
|
||||
}
|
||||
|
||||
type RefundRequestBody struct {
|
||||
BusiMainId string `json:"busiMainId"`
|
||||
ReqTransTime string `json:"reqTransTime"`
|
||||
Data RefundRequestData `json:"data"`
|
||||
}
|
||||
|
||||
type RefundRequestData struct {
|
||||
TxnCode string `json:"txnCode"` //固定值 1004
|
||||
SourceId string `json:"sourceId"` //固定值 16
|
||||
ReqDate string `json:"reqDate"` //YYYYMMDDHHmmss
|
||||
ReqTraceId string `json:"reqTraceId"` //订单号
|
||||
MchtNo string `json:"mchtNo"` //收单商户号
|
||||
OrgTxnSeq string `json:"orgTxnSeq"` //收单系统订单号
|
||||
OrgTxnAmt string `json:"orgTxnAmt"` //原交易金额
|
||||
TxnAmt string `json:"txnAmt"` //退货金额
|
||||
RefundDesc string `json:"refundDesc"` //退货原因
|
||||
}
|
||||
|
||||
type YouChuOrderNotifyRequest struct {
|
||||
}
|
||||
|
||||
func (this *YouChuOrderRequest) 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
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
package entities
|
||||
package front
|
||||
|
||||
type IdRequest struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
type PageRequest struct {
|
||||
Page int64 `json:"page"`
|
||||
PageSize int64 `json:"pageSize"`
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"pageSize"`
|
||||
}
|
||||
|
||||
type PageRsp struct {
|
|
@ -1,12 +1,22 @@
|
|||
package requestmapping
|
||||
|
||||
import "qteam/app/constants/common"
|
||||
import (
|
||||
"qteam/app/constants/common"
|
||||
"qteam/app/http/entities/front"
|
||||
)
|
||||
|
||||
var FrontRequestMap = map[string]func() interface{}{
|
||||
//"/v1/login": func() interface{} {
|
||||
// return new(front.LoginRequest)
|
||||
//},
|
||||
|
||||
// 奶茶专区代金券列表
|
||||
common.FRONT_API_V1 + "/VoucherList": func() interface{} { return new(struct{}) },
|
||||
//联合登录
|
||||
common.FRONT_API_V1 + "/UnionLogin": func() interface{} { return new(front.UnionLoginRequest) },
|
||||
|
||||
// 生成订单
|
||||
common.FRONT_API_V1_Auth + "/order/create": func() interface{} { return new(front.OrderCreateRequest) },
|
||||
//订单列表
|
||||
common.FRONT_API_V1_Auth + "/order/list": func() interface{} { return new(front.OrderListRequest) },
|
||||
// 查询订单
|
||||
common.FRONT_API_V1_Auth + "/order/query": func() interface{} { return new(front.OrderQueryRequest) },
|
||||
//退款订单
|
||||
common.FRONT_API_V1_Auth + "/order/refund": func() interface{} { return new(front.OrderQueryRequest) },
|
||||
}
|
||||
|
|
|
@ -48,7 +48,25 @@ func RegisterRoute(router *gin.Engine) {
|
|||
//api版本
|
||||
v1 := router.Group(common.FRONT_API_V1, middlewares.ValidateRequest())
|
||||
{
|
||||
//优惠券品牌、商品列表
|
||||
v1.POST("/VoucherList", front.VoucherList)
|
||||
// 联合登录
|
||||
v1.POST("/UnionLogin", front.UnionLogin)
|
||||
|
||||
v1.POST("/order/notify", front.OrderNotify)
|
||||
|
||||
//auth
|
||||
auth := v1.Group("auth")
|
||||
//auth := v1.Group("auth", middlewares.Auth())
|
||||
{
|
||||
order := auth.Group("/order")
|
||||
{
|
||||
order.POST("/create", front.CreateOrder)
|
||||
order.POST("/list", front.OrderList)
|
||||
order.POST("/query", front.OrderQuery)
|
||||
order.POST("/refund", front.OrderRefund)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
|
|
@ -13,23 +13,25 @@ var (
|
|||
|
||||
// 实体
|
||||
type Orders struct {
|
||||
Id string `xorm:"'id' UNSIGNED INT pk autoincr"`
|
||||
OrderNo string `xorm:"'order_no' varchar(50)"`
|
||||
UserId int `xorm:"'user_id' int(0)"`
|
||||
YouchuUserId int `xorm:"'YouChu_User_id' int(11)"`
|
||||
UserName string `xorm:"'user_name' varchar(11)"`
|
||||
Mobile string `xorm:"'mobile' varchar(13)"`
|
||||
ProductId int `xorm:"'product_id' int(0)"`
|
||||
ProductName string `xorm:"'product_name' varchar(50)"`
|
||||
Price string `xorm:"'price' decimal(10,2)"`
|
||||
ActivityId string `xorm:"'activity_id' UNSIGNED INT"`
|
||||
State string `xorm:"'state' UNSIGNED TINYINT"`
|
||||
VoucherId int `xorm:"'voucher_id' int(0)"`
|
||||
VoucherLink string `xorm:"'voucher_link' varchar(255)"`
|
||||
CreateTime time.Time `xorm:"'create_time' datetime"`
|
||||
UpdateTime time.Time `xorm:"'update_time' datetime"`
|
||||
ExchangeTime time.Time `xorm:"'exchange_time' datetime"`
|
||||
Deleted time.Time `xorm:"'Deleted' datetime"`
|
||||
Id int `xorm:"'id' UNSIGNED INT pk autoincr"`
|
||||
OrderNo string `xorm:"'order_no' varchar(50)"`
|
||||
OrgTxnSeq string `xorm:"'orgTxnSeq' varchar(255)"`
|
||||
UserId int `xorm:"'user_id' int(0)"`
|
||||
UserName string `xorm:"'user_name' varchar(11)"`
|
||||
Mobile string `xorm:"'mobile' varchar(13)"`
|
||||
ProductId int `xorm:"'product_id' int(0)"`
|
||||
ProductName string `xorm:"'product_name' varchar(50)"`
|
||||
Price string `xorm:"'price' decimal(10,2)"`
|
||||
State int `xorm:"'state' UNSIGNED TINYINT"`
|
||||
VoucherId int `xorm:"'voucher_id' int(0)"`
|
||||
VoucherLink string `xorm:"'voucher_link' varchar(255)"`
|
||||
RefundOrderNo string `xorm:"'refundOrderNo' varchar(255)"`
|
||||
RefundOrderSta string `xorm:"'refundOrderSta' varchar(20)"`
|
||||
RefundTime time.Time `xorm:"'create_time' datetime"`
|
||||
CreateTime time.Time `xorm:"'create_time' datetime"`
|
||||
UpdateTime time.Time `xorm:"'update_time' datetime"`
|
||||
ExchangeTime time.Time `xorm:"'exchange_time' datetime"`
|
||||
Deleted time.Time `xorm:"'Deleted' datetime"`
|
||||
}
|
||||
|
||||
// 表名
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"qteam/app/constants/common"
|
||||
"qteam/app/constants/errorcode"
|
||||
"qteam/app/http/entities/front"
|
||||
"qteam/app/models/ordersmodel"
|
||||
"qteam/app/models/productsmodel"
|
||||
"qteam/app/models/usersmodel"
|
||||
"qteam/app/utils"
|
||||
redis_util "qteam/app/utils/redis"
|
||||
"qteam/config"
|
||||
"strconv"
|
||||
"time"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func CreateOrderService(userId int, productId int) (code int, data front.InsertOrderResponse) {
|
||||
var err error
|
||||
{ // redis保证用户当前抽奖结束才能开始下次抽奖
|
||||
key := utils.GetRealKey("lottery_code:" + strconv.Itoa(userId) + strconv.Itoa(productId))
|
||||
ok, err := redis_util.AcquireLock(key, time.Second*10)
|
||||
if ok {
|
||||
defer redis_util.Del(key)
|
||||
} else {
|
||||
if err != nil {
|
||||
utils.Log(nil, "CreateOrderService", err.Error())
|
||||
}
|
||||
code = errorcode.OrderLottery
|
||||
return code, data
|
||||
}
|
||||
}
|
||||
session := ordersmodel.GetInstance().GetDb().NewSession()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = session.Rollback()
|
||||
return
|
||||
}
|
||||
_ = session.Close()
|
||||
}()
|
||||
var product productsmodel.Products
|
||||
has, err := productsmodel.GetInstance().GetDb().Where("id =?", productId).Get(&product)
|
||||
if err != nil {
|
||||
return errorcode.SystemError, data
|
||||
}
|
||||
if !has {
|
||||
return errorcode.OrderProductNotExist, data
|
||||
}
|
||||
var user usersmodel.Users
|
||||
_, err = usersmodel.GetInstance().GetDb().Where("id =?", userId).Get(&user)
|
||||
if err != nil {
|
||||
return errorcode.SystemError, data
|
||||
}
|
||||
order := ordersmodel.Orders{
|
||||
OrderNo: utils.GenerateOrderNumber(),
|
||||
UserId: userId,
|
||||
ProductId: productId,
|
||||
ProductName: product.Name,
|
||||
UserName: user.Name,
|
||||
Price: product.Price,
|
||||
Mobile: user.Phone,
|
||||
VoucherId: product.ThirdProductId,
|
||||
State: common.STATUSABLED,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
if err = session.Begin(); err != nil {
|
||||
return errorcode.SystemError, data
|
||||
}
|
||||
if _, err := session.Insert(order); err != nil {
|
||||
utils.Log(nil, "CreateOrderService", err.Error())
|
||||
return errorcode.SystemError, data
|
||||
} else {
|
||||
_ = session.Commit()
|
||||
}
|
||||
data.OrderNo = order.OrderNo
|
||||
data.NotifyUrl = config.GetConf().YouChu.NotifyUrl
|
||||
return errorcode.Success, data
|
||||
}
|
||||
|
||||
func OrderQueryService(userId int, OrderRequest *front.OrderListRequest) (code int, data []ordersmodel.Orders, count int64) {
|
||||
repo := ordersmodel.GetInstance().GetDb()
|
||||
count, err := repo.Where("user_id = ?", userId).
|
||||
Desc("id").Limit(OrderRequest.PageSize, (OrderRequest.Page-1)*OrderRequest.PageSize).FindAndCount(&data)
|
||||
code = handErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
func OrderDetailService(order *ordersmodel.Orders) (has bool, err error) {
|
||||
repo := ordersmodel.GetInstance().GetDb()
|
||||
conn := builder.NewCond()
|
||||
if order.Id != 0 {
|
||||
conn = conn.And(builder.Eq{"id": order.Id})
|
||||
}
|
||||
if order.OrderNo != "" {
|
||||
conn = conn.And(builder.Eq{"order_no": order.OrderNo})
|
||||
}
|
||||
return repo.Where(conn).Get(order)
|
||||
}
|
||||
|
||||
func OrdersUpdateService(req front.OrdersUpdateRequest) (err error) {
|
||||
repo := ordersmodel.GetInstance().GetDb()
|
||||
var order ordersmodel.Orders
|
||||
if req.Id != 0 {
|
||||
order.Id = req.Id
|
||||
}
|
||||
if req.Status != 0 {
|
||||
order.State = req.Status
|
||||
}
|
||||
if req.VoucherLink != "" {
|
||||
order.VoucherLink = req.VoucherLink
|
||||
}
|
||||
_, err = repo.Where("Id = ?", req.Id).Update(&order)
|
||||
return
|
||||
}
|
||||
|
||||
func OrderRefundService(userId int, orderId int) (code int) {
|
||||
{ // redis保证用户当前抽奖结束才能开始下次抽奖
|
||||
key := utils.GetRealKey("lottery_code:" + strconv.Itoa(userId) + strconv.Itoa(orderId))
|
||||
ok, err := redis_util.AcquireLock(key, time.Second*10)
|
||||
if ok {
|
||||
defer redis_util.Del(key)
|
||||
} else {
|
||||
if err != nil {
|
||||
utils.Log(nil, "CreateOrderService", err.Error())
|
||||
}
|
||||
return errorcode.SystemError
|
||||
}
|
||||
}
|
||||
order := ordersmodel.Orders{Id: orderId, UserId: userId}
|
||||
has, err := OrderDetailService(&order)
|
||||
if err != nil || !has {
|
||||
return errorcode.NotFound
|
||||
}
|
||||
if order.State >= common.ORDER_STATUS_PAY {
|
||||
return errorcode.OrderNOTAuthREFUND
|
||||
}
|
||||
code, response := YouChuOrderRefund(order)
|
||||
if code != errorcode.Success {
|
||||
return code
|
||||
} else {
|
||||
if response.RefundOrderSta != "03" {
|
||||
return errorcode.OrderRefundFail
|
||||
} else {
|
||||
order.State = common.ORDER_STATUS_ReFUNDING
|
||||
order.RefundOrderNo = response.RefundOrderNo
|
||||
order.RefundOrderSta = response.RefundOrderSta
|
||||
order.RefundTime = time.Now()
|
||||
_, err := ordersmodel.GetInstance().GetDb().Update(order)
|
||||
if err != nil {
|
||||
return errorcode.OrderRefundUpdateFail
|
||||
}
|
||||
return errorcode.Success
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/tjfoc/gmsm/sm2"
|
||||
"math/big"
|
||||
"qteam/config"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 生成公钥、私钥
|
||||
func GenerateSM2Key() (PublicKey string, PrivateKey string, err error) {
|
||||
// 生成私钥、公钥
|
||||
privKey, err := sm2.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
fmt.Println("生成密钥对失败:", err)
|
||||
return "", "", err
|
||||
}
|
||||
return PublicKeyToString(&privKey.PublicKey), PrivateKeyToString(privKey), nil
|
||||
}
|
||||
|
||||
// PublicKeyToString 公钥sm2.PublicKey转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||
func PublicKeyToString(publicKey *sm2.PublicKey) string {
|
||||
xBytes := publicKey.X.Bytes()
|
||||
yBytes := publicKey.Y.Bytes()
|
||||
|
||||
// 确保坐标字节切片长度相同
|
||||
byteLen := len(xBytes)
|
||||
if len(yBytes) > byteLen {
|
||||
byteLen = len(yBytes)
|
||||
}
|
||||
|
||||
// 为坐标补齐前导零
|
||||
xBytes = append(make([]byte, byteLen-len(xBytes)), xBytes...)
|
||||
yBytes = append(make([]byte, byteLen-len(yBytes)), yBytes...)
|
||||
|
||||
// 添加 "04" 前缀
|
||||
publicKeyBytes := append([]byte{0x04}, append(xBytes, yBytes...)...)
|
||||
|
||||
return strings.ToUpper(hex.EncodeToString(publicKeyBytes))
|
||||
}
|
||||
|
||||
// PrivateKeyToString 私钥sm2.PrivateKey 转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||
func PrivateKeyToString(privateKey *sm2.PrivateKey) string {
|
||||
return strings.ToUpper(hex.EncodeToString(privateKey.D.Bytes()))
|
||||
}
|
||||
|
||||
func SM2Decrypt(cipherText string) (string, error) {
|
||||
if cipherText == "" {
|
||||
return "", nil
|
||||
}
|
||||
decodedBytes, err := base64.StdEncoding.DecodeString(cipherText)
|
||||
if err != nil {
|
||||
fmt.Println("解码错误:", err)
|
||||
return "", nil
|
||||
}
|
||||
decrypt, err := decryptLoc(config.GetConf().Sm2.PublicKey, config.GetConf().Sm2.PrivateKey, string(decodedBytes))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return decrypt, nil
|
||||
}
|
||||
|
||||
func SM2Encrypt(cipherText string) (string, error) {
|
||||
if cipherText == "" {
|
||||
return "", nil
|
||||
}
|
||||
decrypt, err := encryptLoc(config.GetConf().Sm2.PublicKey, cipherText)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return decrypt, nil
|
||||
}
|
||||
|
||||
func encryptLoc(publicKeyStr, data string) (string, error) {
|
||||
publicKeyObj, err := StringToPublicKey(publicKeyStr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
decrypt, err := sm2.Encrypt(publicKeyObj, []byte(data), rand.Reader, sm2.C1C2C3)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
resultStr := hex.EncodeToString(decrypt)
|
||||
return base64.StdEncoding.EncodeToString([]byte(resultStr)), nil
|
||||
}
|
||||
|
||||
func decryptLoc(publicKeyStr, privateKeyStr, cipherText string) (string, error) {
|
||||
publicKeyObj, err := StringToPublicKey(publicKeyStr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
privateKeyObj, err := StringToPrivateKey(privateKeyStr, publicKeyObj)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
decodeString, err := hex.DecodeString(cipherText)
|
||||
decrypt, err := sm2.Decrypt(privateKeyObj, decodeString, sm2.C1C2C3)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
resultStr := string(decrypt)
|
||||
fmt.Println("解密后的字符串:", resultStr)
|
||||
return resultStr, nil
|
||||
}
|
||||
|
||||
// StringToPrivateKey 私钥还原为 sm2.PrivateKey对象(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||
func StringToPrivateKey(privateKeyStr string, publicKey *sm2.PublicKey) (*sm2.PrivateKey, error) {
|
||||
privateKeyBytes, err := hex.DecodeString(privateKeyStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 将字节切片转换为大整数
|
||||
d := new(big.Int).SetBytes(privateKeyBytes)
|
||||
|
||||
// 创建 sm2.PrivateKey 对象
|
||||
privateKey := &sm2.PrivateKey{
|
||||
PublicKey: *publicKey,
|
||||
D: d,
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
// StringToPublicKey 公钥字符串还原为 sm2.PublicKey 对象(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||
func StringToPublicKey(publicKeyStr string) (*sm2.PublicKey, error) {
|
||||
publicKeyBytes, err := hex.DecodeString(publicKeyStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 提取 x 和 y 坐标字节切片
|
||||
curve := sm2.P256Sm2().Params()
|
||||
byteLen := (curve.BitSize + 7) / 8
|
||||
xBytes := publicKeyBytes[1 : byteLen+1]
|
||||
yBytes := publicKeyBytes[byteLen+1 : 2*byteLen+1]
|
||||
|
||||
// 将字节切片转换为大整数
|
||||
x := new(big.Int).SetBytes(xBytes)
|
||||
y := new(big.Int).SetBytes(yBytes)
|
||||
|
||||
// 创建 sm2.PublicKey 对象
|
||||
publicKey := &sm2.PublicKey{
|
||||
Curve: curve,
|
||||
X: x,
|
||||
Y: y,
|
||||
}
|
||||
|
||||
return publicKey, nil
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
func VerSm2Sig(pub *sm2.PublicKey, msg []byte, sign []byte) bool {
|
||||
isok := pub.Verify(msg, sign)
|
||||
return isok
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"qteam/app/constants/common"
|
||||
"qteam/app/constants/errorcode"
|
||||
"qteam/app/http/entities/front"
|
||||
"qteam/app/models/usersmodel"
|
||||
"qteam/app/third/youchu"
|
||||
"qteam/app/utils"
|
||||
"qteam/config"
|
||||
)
|
||||
|
||||
func YouChuLogin(req *front.UnionLoginRequest) (code int, login front.LoginResponse) {
|
||||
client := youchu.NewYouChuClient(config.GetConf().YouChu)
|
||||
YouChuResponse, err := client.Login(req.Code)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if YouChuResponse.RespCode == "000000" {
|
||||
// 获取用户信息
|
||||
var user usersmodel.Users
|
||||
err := usersmodel.GetInstance().GetDb().Where("status = ?", common.STATUSABLED).Where("phone = ?", YouChuResponse.Phone).Find(&user)
|
||||
code = handErr(err)
|
||||
login.Token = utils.GeneratorJwtToken(utils.User{
|
||||
Id: user.Id,
|
||||
Phone: user.Phone,
|
||||
})
|
||||
return errorcode.Success, login
|
||||
}
|
||||
return errorcode.NotFound, login
|
||||
}
|
|
@ -2,20 +2,20 @@ package services
|
|||
|
||||
import (
|
||||
"github.com/ahmetb/go-linq/v3"
|
||||
"qteam/app/http/entities"
|
||||
"qteam/app/http/entities/front"
|
||||
"qteam/app/models/brandmodel"
|
||||
"qteam/app/models/productsmodel"
|
||||
"qteam/config"
|
||||
)
|
||||
|
||||
func VoucherList() (code int, VoucherListResponse entities.VoucherListResponse) {
|
||||
func VoucherList() (code int, VoucherListResponse front.VoucherListResponse) {
|
||||
VoucherListResponse.MilkUrl = config.GetConf().YouChu.MilkUrl
|
||||
var BrandData []brandmodel.Brand
|
||||
var VoucherData []productsmodel.MilkProductsList
|
||||
code, BrandData = MilkBrandList()
|
||||
if BrandData != nil {
|
||||
var MilkList []entities.MilkList
|
||||
linq.From(BrandData).SelectT(func(in brandmodel.Brand) (d entities.MilkList) {
|
||||
var MilkList []front.MilkList
|
||||
linq.From(BrandData).SelectT(func(in brandmodel.Brand) (d front.MilkList) {
|
||||
d.ResponseFromDb(in)
|
||||
return
|
||||
}).ToSlice(&MilkList)
|
||||
|
@ -23,8 +23,8 @@ func VoucherList() (code int, VoucherListResponse entities.VoucherListResponse)
|
|||
}
|
||||
code, VoucherData = MilkProductList()
|
||||
if VoucherData != nil {
|
||||
var MilkVoucherList []entities.MilkVoucherList
|
||||
linq.From(VoucherData).SelectT(func(in productsmodel.MilkProductsList) (d entities.MilkVoucherList) {
|
||||
var MilkVoucherList []front.MilkVoucherList
|
||||
linq.From(VoucherData).SelectT(func(in productsmodel.MilkProductsList) (d front.MilkVoucherList) {
|
||||
d.ResponseFromDb(in)
|
||||
return
|
||||
}).ToSlice(&MilkVoucherList)
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"qteam/app/http/entities/front"
|
||||
"qteam/app/models/ordersmodel"
|
||||
"qteam/app/third/youchu"
|
||||
"qteam/config"
|
||||
)
|
||||
|
||||
func DecryptYouChuData(data string) (info front.YouChuDecryptData) {
|
||||
// 先解Base64,再解Hex16进
|
||||
decrypt, err := SM2Decrypt(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal([]byte(decrypt), &info)
|
||||
if err != nil {
|
||||
fmt.Println("解析JSON错误:", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func YouChuOrderQuery(order ordersmodel.Orders) (code int, response front.YouChuOrderQueryResponse) {
|
||||
client := youchu.NewYouChuClient(config.GetConf().YouChu)
|
||||
code, response = client.OrderQuery(order)
|
||||
return code, response
|
||||
}
|
||||
|
||||
func YouChuOrderRefund(order ordersmodel.Orders) (code int, response front.YouChuOrderRefundResponse) {
|
||||
client := youchu.NewYouChuClient(config.GetConf().YouChu)
|
||||
code, response = client.OrderRefund(order)
|
||||
return code, response
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package youchu
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/pkg/errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"qteam/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
type YouChuClient struct {
|
||||
cfg config.YouChuConfig
|
||||
}
|
||||
|
||||
type YouChuSendRequest struct {
|
||||
CustNo string `json:"custNo"`
|
||||
ReqTransTime string `json:"reqTransTime"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type YouChuResponse struct {
|
||||
RespCode string `json:"respCode"` //000000-成功 069801-系统繁忙,请稍后重试 820007-未查询到客户号
|
||||
RespMsg string `json:"respMsg"` //描 述 (失败时必填)
|
||||
Phone string `json:"phone"`
|
||||
IdtLastFour string `json:"idtLastFour"`
|
||||
CustName string `json:"custName"`
|
||||
CustFlag string `json:"custFlag"`
|
||||
}
|
||||
|
||||
func (this *YouChuSendRequest) 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 *YouChuClient) doPost(url string, partnerTxSriNo string, method 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")
|
||||
req.Header.Set("partnerTxSriNo", partnerTxSriNo)
|
||||
req.Header.Set("method", method)
|
||||
req.Header.Set("version", "1")
|
||||
req.Header.Set("appID", this.cfg.AppID)
|
||||
req.Header.Set("merchantId", this.cfg.MerchantId)
|
||||
req.Header.Set("reqTime", time.Now().Format("20060102150405"))
|
||||
req.Header.Set("accessType", "API")
|
||||
|
||||
// 创建HTTP客户端
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second, // 设置请求超时时间为5秒
|
||||
}
|
||||
|
||||
// 发送请求并处理响应
|
||||
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,145 @@
|
|||
package youchu
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
mrand "math/rand"
|
||||
"qteam/app/constants/errorcode"
|
||||
"qteam/app/http/entities/front"
|
||||
"qteam/app/models/ordersmodel"
|
||||
"qteam/app/utils"
|
||||
"qteam/app/utils/postbank"
|
||||
"qteam/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewYouChuClient(cfg config.YouChuConfig) *YouChuClient {
|
||||
return &YouChuClient{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *YouChuClient) Login(code string) (res YouChuResponse, err error) {
|
||||
partnerTxSriNo := time.Now().Format("20060102150405") + fmt.Sprintf("%10d", mrand.Intn(10000))
|
||||
url := this.cfg.MerchantId + ".html?partnerTxSriNo=" + partnerTxSriNo
|
||||
request := YouChuSendRequest{
|
||||
CustNo: this.cfg.MerchantId,
|
||||
ReqTransTime: time.Now().Format("20060102150405"),
|
||||
Code: code,
|
||||
}
|
||||
bytes, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
data, err := this.doPost(url, partnerTxSriNo, "crecard.getCustomerInfo", bytes)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
err = json.Unmarshal(data, &res)
|
||||
return
|
||||
}
|
||||
|
||||
func (this *YouChuClient) OrderQuery(order ordersmodel.Orders) (code int, response front.YouChuOrderQueryResponse) {
|
||||
var BusiMainId = time.Now().Format("20060102150405") + utils.RandomString(10)
|
||||
request := front.YouChuRequestBody{
|
||||
Body: front.YouChuOrderRequest{
|
||||
BusiMainId: BusiMainId,
|
||||
ReqTransTime: order.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
Data: front.OrderQuery{
|
||||
TxnCode: "1004",
|
||||
SourceId: "16",
|
||||
ReqTraceId: order.OrderNo,
|
||||
ReqDate: time.Now().Format("20060102150405"),
|
||||
TxnDt: time.Now().Format("20060102"),
|
||||
MchtNo: config.GetConf().YouChu.MchtNo,
|
||||
OldSeqNo: utils.GenerateRequestNumber(),
|
||||
},
|
||||
},
|
||||
Head: front.YouChuRequestHeader{
|
||||
PartnerTxSriNo: BusiMainId,
|
||||
AccessType: "API",
|
||||
Reserve: "",
|
||||
Method: "b2c.gatewaypay.orderQuery",
|
||||
Version: "1",
|
||||
MerchantId: config.GetConf().YouChu.MerchantId,
|
||||
AppID: config.GetConf().YouChu.AppID,
|
||||
},
|
||||
}
|
||||
url := config.GetConf().YouChu.MerchantId + ".html?partnerTxSriNo=" + request.Body.BusiMainId
|
||||
requestData := EncryptRequest(request)
|
||||
bytes, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
return errorcode.SystemError, response
|
||||
}
|
||||
post, err := this.doPost(url, request.Body.BusiMainId, "b2c.gatewaypay.orderQuery", bytes)
|
||||
if err != nil {
|
||||
return errorcode.SystemError, response
|
||||
}
|
||||
err = json.Unmarshal(post, &response)
|
||||
if err != nil {
|
||||
return errorcode.SystemError, front.YouChuOrderQueryResponse{}
|
||||
}
|
||||
return errorcode.Success, response
|
||||
}
|
||||
|
||||
func (this *YouChuClient) OrderRefund(order ordersmodel.Orders) (code int, response front.YouChuOrderRefundResponse) {
|
||||
var BusiMainId = time.Now().Format("20060102150405") + utils.RandomString(10)
|
||||
request := front.YouChuOrderRefundRequest{
|
||||
Head: front.YouChuRequestHeader{
|
||||
PartnerTxSriNo: BusiMainId,
|
||||
AccessType: "API",
|
||||
Reserve: "",
|
||||
Method: "b2c.gatewaypay.orderRefund",
|
||||
Version: "1",
|
||||
MerchantId: config.GetConf().YouChu.MerchantId,
|
||||
AppID: config.GetConf().YouChu.AppID,
|
||||
},
|
||||
Body: front.RefundRequestBody{
|
||||
BusiMainId: BusiMainId,
|
||||
ReqTransTime: order.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
Data: front.RefundRequestData{
|
||||
TxnCode: "1003",
|
||||
SourceId: "16",
|
||||
ReqTraceId: order.OrderNo,
|
||||
ReqDate: time.Now().Format("20060102150405"),
|
||||
MchtNo: config.GetConf().YouChu.MchtNo,
|
||||
OrgTxnSeq: order.OrgTxnSeq,
|
||||
TxnAmt: order.Price,
|
||||
OrgTxnAmt: order.Price,
|
||||
RefundDesc: "用户主动退款",
|
||||
},
|
||||
},
|
||||
}
|
||||
url := config.GetConf().YouChu.MerchantId + ".html?partnerTxSriNo=" + request.Body.BusiMainId
|
||||
requestData := EncryptRequest(request)
|
||||
bytes, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
return errorcode.SystemError, response
|
||||
}
|
||||
post, err := this.doPost(url, request.Body.BusiMainId, "b2c.gatewaypay.orderRefund", bytes)
|
||||
utils.Log(nil, "b2c.orderRefund", post)
|
||||
if err != nil {
|
||||
return errorcode.YouChuOrderRefundFail, response
|
||||
}
|
||||
err = json.Unmarshal(post, &response)
|
||||
if err != nil {
|
||||
return errorcode.SystemError, front.YouChuOrderRefundResponse{}
|
||||
}
|
||||
return errorcode.Success, response
|
||||
}
|
||||
|
||||
func EncryptRequest(request interface{}) (RequestData front.YouChuRequest) {
|
||||
input, _ := json.Marshal(request)
|
||||
MerchantId := config.GetConf().YouChu.MerchantId
|
||||
PrivateKey := config.GetConf().Sm2.PrivateKey
|
||||
PublicKey := config.GetConf().Sm2.PublicKey
|
||||
encrypt, err := postbank.Encrypt(MerchantId, PrivateKey, PublicKey, string(input), "", true)
|
||||
if err != nil {
|
||||
return front.YouChuRequest{}
|
||||
}
|
||||
err = json.Unmarshal([]byte(encrypt), &RequestData)
|
||||
if err != nil {
|
||||
return front.YouChuRequest{}
|
||||
}
|
||||
return RequestData
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package Sm4
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
sm4 "github.com/tjfoc/gmsm/sm4"
|
||||
)
|
||||
|
||||
func EncryptSM4(plainText string, key string) ([]byte, error) {
|
||||
block, err := sm4.NewCipher([]byte(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := make([]byte, len(plainText))
|
||||
iv := []byte("UISwD9fW6cFh9SNS") // 使用16字节的初始化向量
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(ciphertext, []byte(plainText))
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func DecryptSM4(ciphertext string, key string) ([]byte, error) {
|
||||
block, err := sm4.NewCipher([]byte(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
plaintext := make([]byte, len(ciphertext))
|
||||
iv := []byte("UISwD9fW6cFh9SNS") // 使用与加密相同的初始化向量
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(plaintext, []byte(ciphertext))
|
||||
|
||||
return plaintext, nil
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
package encrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"math/rand"
|
||||
"unsafe"
|
||||
)
|
||||
|
@ -35,3 +39,41 @@ func LotteryEncryptDecode(str string) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// =================== CBC ======================
|
||||
func AesEncryptCBC(origData []byte, key []byte) (str string) {
|
||||
// 分组秘钥
|
||||
// NewCipher该函数限制了输入k的长度必须为16, 24或者32
|
||||
block, _ := aes.NewCipher(key)
|
||||
blockSize := block.BlockSize() // 获取秘钥块的长度
|
||||
origData = pkcs5Padding(origData, blockSize) // 补全码
|
||||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) // 加密模式
|
||||
encrypted := make([]byte, len(origData)) // 创建数组
|
||||
blockMode.CryptBlocks(encrypted, origData) // 加密
|
||||
|
||||
return base64.StdEncoding.EncodeToString(encrypted)
|
||||
}
|
||||
func AesDecryptCBC(data string, key []byte) (decrypted []byte) {
|
||||
encrypted, err := base64.StdEncoding.DecodeString(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key) // 分组秘钥
|
||||
blockSize := block.BlockSize() // 获取秘钥块的长度
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) // 加密模式
|
||||
decrypted = make([]byte, len(encrypted)) // 创建数组
|
||||
blockMode.CryptBlocks(decrypted, encrypted) // 解密
|
||||
decrypted = pkcs5UnPadding(decrypted) // 去除补全码
|
||||
return decrypted
|
||||
}
|
||||
func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
func pkcs5UnPadding(origData []byte) []byte {
|
||||
length := len(origData)
|
||||
unpadding := int(origData[length-1])
|
||||
return origData[:(length - unpadding)]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package postbank
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Cmd go run main.go postbank
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "postbank",
|
||||
Short: "邮储国密SM辅助工具",
|
||||
}
|
||||
|
||||
// merchantId, privateKey, sopPublicKey, inputJson, true
|
||||
const (
|
||||
merchantIdKey = "merchantId"
|
||||
privateKeyKey = "priKey"
|
||||
sopPublicKeyKey = "sopPublicKey"
|
||||
inputKey = "input"
|
||||
isRequestKey = "isRequest"
|
||||
accessTokenKey = "accessToken"
|
||||
|
||||
shortMerchantIdKey = "m"
|
||||
shortPrivateKeyKey = "p"
|
||||
shortSopPublicKeyKey = "s"
|
||||
shortInputKey = "i"
|
||||
shortIsRequestKey = "r"
|
||||
shortAccessTokenKey = "a"
|
||||
|
||||
merchantIdContextKey = "merchantId"
|
||||
privateKeyContextKey = "priKey"
|
||||
sopPublicKeyContextKey = "sopPublicKey"
|
||||
inputContextKey = "input"
|
||||
isRequestContextKey = "isRequest"
|
||||
accessTokenContextKey = "accessToken"
|
||||
)
|
||||
|
||||
func init() {
|
||||
encryptCmd.PersistentFlags().StringP(merchantIdKey, shortMerchantIdKey, "", "商户号")
|
||||
encryptCmd.PersistentFlags().StringP(privateKeyKey, shortPrivateKeyKey, "", "私钥")
|
||||
encryptCmd.PersistentFlags().StringP(sopPublicKeyKey, shortSopPublicKeyKey, "", "对方公钥")
|
||||
encryptCmd.PersistentFlags().StringP(inputKey, shortInputKey, "", "待加密的JSON字符串")
|
||||
encryptCmd.PersistentFlags().BoolP(isRequestKey, shortIsRequestKey, false, "请求加密")
|
||||
encryptCmd.PersistentFlags().StringP(accessTokenKey, shortAccessTokenKey, "", "accessToken")
|
||||
|
||||
decryptCmd.PersistentFlags().StringP(merchantIdKey, shortMerchantIdKey, "", "商户号")
|
||||
decryptCmd.PersistentFlags().StringP(privateKeyKey, shortPrivateKeyKey, "", "私钥")
|
||||
decryptCmd.PersistentFlags().StringP(sopPublicKeyKey, shortSopPublicKeyKey, "", "对方公钥")
|
||||
decryptCmd.PersistentFlags().StringP(inputKey, shortInputKey, "", "待解密的JSON字符串")
|
||||
decryptCmd.PersistentFlags().BoolP(isRequestKey, shortIsRequestKey, false, "请求加密")
|
||||
|
||||
Cmd.AddCommand(encryptCmd, decryptCmd, generateCmd)
|
||||
}
|
||||
|
||||
func forceArgsToStringContext(cmd *cobra.Command, key, ctxKey string) error {
|
||||
data, err := cmd.Flags().GetString(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("缺少参数:<%s>%w", key, err)
|
||||
}
|
||||
cmd.SetContext(context.WithValue(cmd.Context(), ctxKey, data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func argsToStringContext(cmd *cobra.Command, key, ctxKey string) error {
|
||||
data, err := cmd.Flags().GetString(key)
|
||||
if err != nil {
|
||||
data = ""
|
||||
}
|
||||
cmd.SetContext(context.WithValue(cmd.Context(), ctxKey, data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func forceArgsToBoolContext(cmd *cobra.Command, key, ctxKey string) error {
|
||||
data, err := cmd.Flags().GetBool(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("缺少参数:<%s>%w", key, err)
|
||||
}
|
||||
cmd.SetContext(context.WithValue(cmd.Context(), ctxKey, data))
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"qteam/app/utils/postbank/smtypes"
|
||||
)
|
||||
|
||||
func DeCrypt(merchantId string, priKey string, sopPublicKey string, input *smtypes.RequestData) ([]byte, error) {
|
||||
// 解密逻辑
|
||||
requestJson, err := json.Marshal(input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("请求体json编码失败:%s", err)
|
||||
}
|
||||
path, err := smv2()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解码获取当前目录失败:%s", err)
|
||||
}
|
||||
cmd := fmt.Sprintf("%s postbank decrypt --merchantId=\"%s\" --priKey=\"%s\" --sopPublicKey=\"%s\" --input=\"%s\" %s 2>&1",
|
||||
path, merchantId, priKey, sopPublicKey, base64.StdEncoding.EncodeToString(requestJson), "--isRequest")
|
||||
// 返回解密后的数据
|
||||
return toExec(cmd), nil
|
||||
}
|
||||
|
||||
func smv2() (string, error) {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s/%s/smv2", filepath.Dir(filepath.Dir(path)), "untils/sm"), nil
|
||||
}
|
||||
|
||||
func toExec(cmdString string) []byte {
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
cmd := exec.Command(cmdString)
|
||||
cmd.Stdout = &stdoutBuf
|
||||
cmd.Stderr = &stderrBuf
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("cmd.Run() failed with %s\n", err)
|
||||
}
|
||||
fmt.Printf("stdout: %s\nstderr: %s\n", stdoutBuf.String(), stderrBuf.String())
|
||||
return stdoutBuf.Bytes()
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,62 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"qteam/app/utils/postbank"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
merchantId = "lansexiongdi666"
|
||||
// 模块名称 服开门户网站-文档中心-API文档-接口方法业务前缀
|
||||
// 此处默认为普通接口取数币服务模块名称 ecny 文件接口这里传ufile
|
||||
moduleName = "ecny"
|
||||
// appID 服开门户网站-用户中心-我的应用-APP_ID
|
||||
appID = "961925472724332544001"
|
||||
// url 协议 + 定版ip:端口号 + 接口方法业务前缀 + 合作方编号 + 接口地址后缀 + 合作方交易流水号(工具类生成)
|
||||
url = "http://220.248.252.123:8443/sop-h5/biz/${moduleName}/${merchantId}.htm?partnerTxSriNo=${partnerTxSriNo}"
|
||||
// h5token h5申请token的url 协议 + 定版ip:端口号 + 接口地址业务前缀 + 合作方编号 + 合作方交易流水号(工具类生成)
|
||||
h5token = "http://220.248.252.123:8443/sop-h5/h5/authToken/apply/${merchantId}?partnerTxSriNo=${partnerTxSriNo}"
|
||||
// 合作方私钥
|
||||
privateKey = "7FE05FEB81673ABF0AC4F9EEB99FB03BFE7BE432CA474CB9AAD8E302CAF00766"
|
||||
// 合作方公钥
|
||||
publicKey = "0432F7E91033AC40472885B11EB673B1DE942FD555E95CD12E799CC50DD9E93B04A6AC254D725C22DCBD65436D97A46727B09FFFA1B1098EEA8D43F79AAB84F5B1"
|
||||
// 服开与合作方配对公钥 服开门户网站-用户中心-公钥配置-平台生产公钥
|
||||
sopPublicKey = "041F63D23554FC1F5A36D43ABD0DE368A202849BCC03BEB34E7EF4F8C478E12F5529DB649B7C2C2DB5F55B4DC89A6EC83A8226218B7D931638DBFC4B344C628C33"
|
||||
// sopPrivateKey 服开与合作方配对私钥 服开门户网站-用户中心-私钥配置-平台生产私钥
|
||||
sopPrivateKey = "174AB6AB44FF23A6B65934B54E9D94B00A81D0D50D57DB0C38257E2AFB2D6C35"
|
||||
// 文件上传接口
|
||||
// fileUrl = http://220.248.252.123:8443/sop-h5/biz/ufile/upload/接口名/接口版本/合作方编号/appID/流水号
|
||||
fileUrl = "http://220.248.252.123:8443/sop-h5/biz/ufile/upload/${method}/${version}/${merchantId}/${appID}/${partnerTxSriNo}"
|
||||
// 解码格式 如果文件下载时解析出来的报文乱码,请改变解码格式
|
||||
charsetName = "gbk"
|
||||
m_id = "lansexiongdi666"
|
||||
pri = "7FE05FEB81673ABF0AC4F9EEB99FB03BFE7BE432CA474CB9AAD8E302CAF00766"
|
||||
msoapub = "04A3C235C15070E127679628FE024E6C77FFCB1840B4EFBE09D19CBE0EE69BEA231912943984D34C03989771E503DFBB4ECB186F4BBC27764109E63C2875005C81"
|
||||
soapb = "0432F7E91033AC40472885B11EB673B1DE942FD555E95CD12E799CC50DD9E93B04A6AC254D725C22DCBD65436D97A46727B09FFFA1B1098EEA8D43F79AAB84F5B1"
|
||||
)
|
||||
|
||||
// TestSm4Decrypt test sm4 decrypt
|
||||
func TestSm4Decrypt(t *testing.T) {
|
||||
//respJson := "{\n\t\"accessToken\": \"C0A3853384EA0443C2942FF1A1B0757D\",\n\t\"encryptKey\": \"04F15C783C14DBD91DE13696BFCDAA4523F2D6CABB610A8BA809D7224234E53422EA4143ED6C3BD92CB9745A0330142E94C7F6EB36B5601A133F9E7E8C42E18C693BC975AD245EF14FDA9734523F407B7561CC390883D8D45C35DD8E3575970BFD75554EB9119F4A18B65EC5F3AB37D866\",\n\t\"request\": \"JBKuG9pmsYwLgFPJL5nFci9jEgX9M60ebFK8lNteJqJZgZZEF7Sg7+qabTq7AOhHN/u9a+O4Zv8e\\r\\nIncXNC9sFPKOrqRJaMmAZTrDCwD3VhDt/maMn9lQbdkXaqMwo4S1iYiqoni5riS/8ivbFqz4sQvv\\r\\nPoiyIaQCMkqAAEkFxL6aSlPGY7f7kdLHK6cpixT3h2H1coMPuCQrG1J7jnbe+IlPJiVOKn5Hchus\\r\\n4TnHAuH/y9/8W9sW2tILApqm0MUpNxkor3qkAEqkbhciuJJDp/lDqebcJeRt2UET6UtTFTECYEWR\\r\\nNPfQCHKQxMWWserd/wCcWMx8ePPK/UKqUW8Rpg6hncKcC8b0U5i0z3MFuKJscz/DAS/X6Vex9kUp\\r\\njvy5c+ahm/rx0y5pTkNKP9KzVTFE6SffyA3E5NR+6fIQ9VvOU1A/YPiBhCvLF/dCodUk\",\n\t\"signature\": \"60bd9f1bb4a234acf4c8789f048151cbd688538b8f02d2cf87cdd74309c346e6#6aaaad738d9c10b3aaf85c86b5134d95b4d815dc017b43bc9e5e012243eb26f6\"\n}" // 解析接到的请求
|
||||
respJson := EN()
|
||||
reqMsg, err := postbank.Decrypt(merchantId, privateKey, sopPublicKey, respJson, true)
|
||||
fmt.Println(reqMsg, err)
|
||||
}
|
||||
|
||||
func TestSm4Encrypt(t *testing.T) {
|
||||
input := "{\"body\":{\"voucher_id\":\"1708321361154\",\"voucher_code\":\"AAABt6wYMDBjNf25\",\"short_url\":\"http://test.22233.cn/AAABt6wYMDBjNf25\",\"voucher_sdate\":\"20240219\",\"voucher_edate\":\"20240331\",\"code_type\":\"00\"},\"header\":{\"partnerTxSriNo\":\"\",\"method\":\"\",\"version\":\"1\",\"merchantId\":\"YANWEI001\",\"appID\":\"YANWEI00001\",\"respTime\":\"20240301141359\"}}"
|
||||
res, err := postbank.Encrypt(merchantId, privateKey, sopPublicKey, input, "", true)
|
||||
fmt.Println(res, err)
|
||||
}
|
||||
|
||||
func EN() string {
|
||||
input := "{\"head\":\"{\"appID\":\"PSBC\",\"merchantId\":\"lansexiongdi666\",\"method\":\"points.elecCoupRece\",\"partnerTxSriNo\":\"202406131805225935940667\",\"reqTime\":\"20240613180522\",\"version\":\"1\"}\",\"body\":\"{\"acName\":\"2024年二季度新客开卡礼营销活动\",\"orderNo\":\"20240613001534531\",\"merNo\":\"1000000004\",\"acCode\":\"202403HK110052930016\",\"num\":\"1\",\"svcNo\":\"YCTZBZS0004\",\"transSeq\":\"99360000007202406131805227108867\"}\"}"
|
||||
res, _ := postbank.Encrypt(merchantId, sopPrivateKey, publicKey, input, "", true)
|
||||
return res
|
||||
}
|
||||
|
||||
func TestGenerateKey(t *testing.T) {
|
||||
hexPri, publicKeyHex := postbank.GenerateKey()
|
||||
fmt.Println(hexPri, publicKeyHex)
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,54 @@
|
|||
package postbank
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// go run main.go postbank decrypt --key="sm4 key"
|
||||
var decryptCmd = &cobra.Command{
|
||||
Use: "decrypt",
|
||||
Short: "邮储国密解密",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
if err = forceArgsToStringContext(cmd, merchantIdKey, merchantIdContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = forceArgsToStringContext(cmd, privateKeyKey, privateKeyContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = forceArgsToStringContext(cmd, sopPublicKeyKey, sopPublicKeyContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = forceArgsToStringContext(cmd, inputKey, inputContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = forceArgsToBoolContext(cmd, isRequestKey, isRequestContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Run: decryptRun,
|
||||
}
|
||||
|
||||
func decryptRun(cmd *cobra.Command, args []string) {
|
||||
merchantId := cmd.Context().Value(merchantIdContextKey).(string)
|
||||
privateKey := cmd.Context().Value(privateKeyContextKey).(string)
|
||||
sopPublicKey := cmd.Context().Value(sopPublicKeyContextKey).(string)
|
||||
inputJson := cmd.Context().Value(inputContextKey).(string)
|
||||
isRequest := cmd.Context().Value(isRequestContextKey).(bool)
|
||||
res, errStr := decrypt(merchantId, privateKey, sopPublicKey, inputJson, isRequest)
|
||||
cmd.Println(res)
|
||||
cmd.Print(errStr)
|
||||
}
|
||||
|
||||
func decrypt(merchantId, privateKey, sopPublicKey, inputJson string, isRequest bool) (string, string) {
|
||||
data, _ := base64.StdEncoding.DecodeString(inputJson)
|
||||
resp, err := Decrypt(merchantId, privateKey, sopPublicKey, string(data), isRequest)
|
||||
if err != nil {
|
||||
return "fail", fmt.Sprintf("解密失败: %v", err)
|
||||
}
|
||||
return resp, "success"
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package postbank
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// go run main.go postbank encrypt --key="sm4 key"
|
||||
var encryptCmd = &cobra.Command{
|
||||
Use: "encrypt",
|
||||
Short: "邮储国密加密",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
if err = forceArgsToStringContext(cmd, merchantIdKey, merchantIdContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = forceArgsToStringContext(cmd, privateKeyKey, privateKeyContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = forceArgsToStringContext(cmd, sopPublicKeyKey, sopPublicKeyContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = forceArgsToStringContext(cmd, inputKey, inputContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = forceArgsToBoolContext(cmd, isRequestKey, isRequestContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 不强制
|
||||
if err = argsToStringContext(cmd, accessTokenKey, accessTokenContextKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Run: encryptRun,
|
||||
}
|
||||
|
||||
func encryptRun(cmd *cobra.Command, args []string) {
|
||||
merchantId := cmd.Context().Value(merchantIdContextKey).(string)
|
||||
privateKey := cmd.Context().Value(privateKeyContextKey).(string)
|
||||
sopPublicKey := cmd.Context().Value(sopPublicKeyContextKey).(string)
|
||||
inputJson := cmd.Context().Value(inputContextKey).(string)
|
||||
isRequest := cmd.Context().Value(isRequestContextKey).(bool)
|
||||
accessToken := cmd.Context().Value(accessTokenContextKey).(string)
|
||||
|
||||
res, errStr := encrypt(merchantId, privateKey, sopPublicKey, inputJson, accessToken, isRequest)
|
||||
cmd.Println(res)
|
||||
cmd.Print(errStr)
|
||||
}
|
||||
|
||||
func encrypt(merchantId, privateKey, sopPublicKey, inputJson, accessToken string, isRequest bool) (string, string) {
|
||||
data, _ := base64.StdEncoding.DecodeString(inputJson)
|
||||
|
||||
resp, err := Encrypt(merchantId, privateKey, sopPublicKey, string(data), accessToken, isRequest)
|
||||
if err != nil {
|
||||
return "fail", fmt.Sprintf("加密失败: %v", err)
|
||||
}
|
||||
return resp, "success"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package postbank
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// GenerateKey go run main.go postbank generateKey
|
||||
var generateCmd = &cobra.Command{
|
||||
Use: "generateKey",
|
||||
Short: "邮储国密sm2公私钥生成",
|
||||
Args: nil,
|
||||
Run: generateRun,
|
||||
}
|
||||
|
||||
func generateRun(cmd *cobra.Command, args []string) {
|
||||
pri, pub := GenerateKey()
|
||||
cmd.Println(pri)
|
||||
cmd.Print(pub)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
|||
* 来源于 github.com/tjfoc/gmsm
|
||||
* 修改适配 邮储对接
|
|
@ -0,0 +1,219 @@
|
|||
package sm2
|
||||
|
||||
// reference to ecdsa
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"github.com/tjfoc/gmsm/sm3"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var (
|
||||
default_uid = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}
|
||||
)
|
||||
|
||||
type PublicKey struct {
|
||||
elliptic.Curve
|
||||
X, Y *big.Int
|
||||
}
|
||||
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
D *big.Int
|
||||
}
|
||||
|
||||
type sm2Signature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
|
||||
func (priv *PrivateKey) Public() crypto.PublicKey {
|
||||
return &priv.PublicKey
|
||||
}
|
||||
|
||||
var errZeroParam = errors.New("zero parameter")
|
||||
var one = new(big.Int).SetInt64(1)
|
||||
var two = new(big.Int).SetInt64(2)
|
||||
|
||||
func (priv *PrivateKey) Sign(random io.Reader, msg []byte, signer crypto.SignerOpts) ([]byte, error) {
|
||||
r, s, err := Sm2Sign(priv, msg, nil, random)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return asn1.Marshal(sm2Signature{r, s})
|
||||
}
|
||||
|
||||
func Sm2Sign(priv *PrivateKey, msg, uid []byte, random io.Reader) (r, s *big.Int, err error) {
|
||||
digest, err := priv.PublicKey.Sm3Digest(msg, uid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
e := new(big.Int).SetBytes(digest)
|
||||
c := priv.PublicKey.Curve
|
||||
N := c.Params().N
|
||||
if N.Sign() == 0 {
|
||||
return nil, nil, errZeroParam
|
||||
}
|
||||
var k *big.Int
|
||||
for { // 调整算法细节以实现SM2
|
||||
for {
|
||||
k, err = randFieldElement(c, random)
|
||||
if err != nil {
|
||||
r = nil
|
||||
return
|
||||
}
|
||||
r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
|
||||
r.Add(r, e)
|
||||
r.Mod(r, N)
|
||||
if r.Sign() != 0 {
|
||||
if t := new(big.Int).Add(r, k); t.Cmp(N) != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
rD := new(big.Int).Mul(priv.D, r)
|
||||
s = new(big.Int).Sub(k, rD)
|
||||
d1 := new(big.Int).Add(priv.D, one)
|
||||
d1Inv := new(big.Int).ModInverse(d1, N)
|
||||
s.Mul(s, d1Inv)
|
||||
s.Mod(s, N)
|
||||
if s.Sign() != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (pub *PublicKey) Sm3Digest(msg, uid []byte) ([]byte, error) {
|
||||
if len(uid) == 0 {
|
||||
uid = default_uid
|
||||
}
|
||||
|
||||
za, err := getZ(pub, uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e, err := msgHash(za, msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e.Bytes(), nil
|
||||
}
|
||||
|
||||
func Sm2Verify(pub *PublicKey, msg, uid []byte, r, s *big.Int) bool {
|
||||
c := pub.Curve
|
||||
N := c.Params().N
|
||||
one := new(big.Int).SetInt64(1)
|
||||
if r.Cmp(one) < 0 || s.Cmp(one) < 0 {
|
||||
return false
|
||||
}
|
||||
if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 {
|
||||
return false
|
||||
}
|
||||
if len(uid) == 0 {
|
||||
uid = default_uid
|
||||
}
|
||||
za, err := getZ(pub, uid)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
e, err := msgHash(za, msg)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
t := new(big.Int).Add(r, s)
|
||||
t.Mod(t, N)
|
||||
if t.Sign() == 0 {
|
||||
return false
|
||||
}
|
||||
var x *big.Int
|
||||
x1, y1 := c.ScalarBaseMult(s.Bytes())
|
||||
x2, y2 := c.ScalarMult(pub.X, pub.Y, t.Bytes())
|
||||
x, _ = c.Add(x1, y1, x2, y2)
|
||||
|
||||
x.Add(x, e)
|
||||
x.Mod(x, N)
|
||||
return x.Cmp(r) == 0
|
||||
}
|
||||
|
||||
func msgHash(za, msg []byte) (*big.Int, error) {
|
||||
e := sm3.New()
|
||||
e.Write(za)
|
||||
e.Write(msg)
|
||||
return new(big.Int).SetBytes(e.Sum(nil)[:32]), nil
|
||||
}
|
||||
|
||||
func bigIntToByte(n *big.Int) []byte {
|
||||
byteArray := n.Bytes()
|
||||
// If the most significant byte's most significant bit is set,
|
||||
// prepend a 0 byte to the slice to avoid being interpreted as a negative number.
|
||||
if (byteArray[0] & 0x80) != 0 {
|
||||
byteArray = append([]byte{0}, byteArray...)
|
||||
}
|
||||
return byteArray
|
||||
}
|
||||
|
||||
func getZ(pub *PublicKey, uid []byte) ([]byte, error) {
|
||||
z := sm3.New()
|
||||
uidLen := len(uid) * 8
|
||||
entla := []byte{byte(uidLen >> 8), byte(uidLen & 255)}
|
||||
z.Write(entla)
|
||||
z.Write(uid)
|
||||
|
||||
// a 先写死,原来的没有暴露
|
||||
z.Write(bigIntToByte(sm2P256ToBig(&sm2P256.a)))
|
||||
z.Write(bigIntToByte(sm2P256.B))
|
||||
z.Write(bigIntToByte(sm2P256.Gx))
|
||||
z.Write(bigIntToByte(sm2P256.Gy))
|
||||
|
||||
z.Write(bigIntToByte(pub.X))
|
||||
z.Write(bigIntToByte(pub.Y))
|
||||
return z.Sum(nil), nil
|
||||
}
|
||||
|
||||
func randFieldElement(c elliptic.Curve, random io.Reader) (k *big.Int, err error) {
|
||||
if random == nil {
|
||||
random = rand.Reader //If there is no external trusted random source,please use rand.Reader to instead of it.
|
||||
}
|
||||
params := c.Params()
|
||||
b := make([]byte, params.BitSize/8+8)
|
||||
_, err = io.ReadFull(random, b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
k = new(big.Int).SetBytes(b)
|
||||
n := new(big.Int).Sub(params.N, one)
|
||||
k.Mod(k, n)
|
||||
k.Add(k, one)
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateKey(random io.Reader) (*PrivateKey, error) {
|
||||
c := P256Sm2()
|
||||
if random == nil {
|
||||
random = rand.Reader //If there is no external trusted random source,please use rand.Reader to instead of it.
|
||||
}
|
||||
params := c.Params()
|
||||
b := make([]byte, params.BitSize/8+8)
|
||||
_, err := io.ReadFull(random, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k := new(big.Int).SetBytes(b)
|
||||
n := new(big.Int).Sub(params.N, two)
|
||||
k.Mod(k, n)
|
||||
k.Add(k, one)
|
||||
priv := new(PrivateKey)
|
||||
priv.PublicKey.Curve = c
|
||||
priv.D = k
|
||||
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
|
||||
|
||||
return priv, nil
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package sm2
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func ReadPrivateKeyFromHex(Dhex string) (*PrivateKey, error) {
|
||||
c := P256Sm2()
|
||||
d, err := hex.DecodeString(Dhex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k := new(big.Int).SetBytes(d)
|
||||
params := c.Params()
|
||||
one := new(big.Int).SetInt64(1)
|
||||
n := new(big.Int).Sub(params.N, one)
|
||||
if k.Cmp(n) >= 0 {
|
||||
return nil, errors.New("privateKey's D is overflow.")
|
||||
}
|
||||
priv := new(PrivateKey)
|
||||
priv.PublicKey.Curve = c
|
||||
priv.D = k
|
||||
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
func ReadPublicKeyFromHex(Qhex string) (*PublicKey, error) {
|
||||
q, err := hex.DecodeString(Qhex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(q) == 65 && q[0] == byte(0x04) {
|
||||
q = q[1:]
|
||||
}
|
||||
if len(q) != 64 {
|
||||
return nil, errors.New("publicKey is not uncompressed.")
|
||||
}
|
||||
pub := new(PublicKey)
|
||||
pub.Curve = P256Sm2()
|
||||
pub.X = new(big.Int).SetBytes(q[:32])
|
||||
pub.Y = new(big.Int).SetBytes(q[32:])
|
||||
return pub, nil
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
zzsm2 "github.com/ZZMarquis/gm/sm2"
|
||||
"github.com/tjfoc/gmsm/sm3"
|
||||
"github.com/tjfoc/gmsm/x509"
|
||||
"math/big"
|
||||
"qteam/app/utils/postbank/internal/sm2"
|
||||
)
|
||||
|
||||
func Sm2Decrypt(privateKey *sm2.PrivateKey, encryptData []byte) ([]byte, error) {
|
||||
C1Byte := make([]byte, 65)
|
||||
copy(C1Byte, encryptData[:65])
|
||||
x, y := elliptic.Unmarshal(privateKey.Curve, C1Byte)
|
||||
dBC1X, dBC1Y := privateKey.Curve.ScalarMult(x, y, bigIntToByte(privateKey.D))
|
||||
dBC1Bytes := elliptic.Marshal(privateKey.Curve, dBC1X, dBC1Y)
|
||||
|
||||
kLen := len(encryptData) - 65 - 32
|
||||
t, err := kdf(dBC1Bytes, kLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
M := make([]byte, kLen)
|
||||
for i := 0; i < kLen; i++ {
|
||||
M[i] = encryptData[65+i] ^ t[i]
|
||||
}
|
||||
|
||||
C3 := make([]byte, 32)
|
||||
copy(C3, encryptData[len(encryptData)-32:])
|
||||
u := calculateHash(dBC1X, M, dBC1Y)
|
||||
|
||||
if bytes.Compare(u, C3) == 0 {
|
||||
return M, nil
|
||||
} else {
|
||||
return nil, errors.New("解密失败")
|
||||
}
|
||||
}
|
||||
|
||||
func Sm2Encrypt(publicKey *sm2.PublicKey, m []byte) ([]byte, error) {
|
||||
kLen := len(m)
|
||||
var C1, t []byte
|
||||
var err error
|
||||
var kx, ky *big.Int
|
||||
for {
|
||||
k, _ := rand.Int(rand.Reader, publicKey.Params().N)
|
||||
C1x, C1y := zzsm2.GetSm2P256V1().ScalarBaseMult(bigIntToByte(k))
|
||||
// C1x, C1y := sm2.P256Sm2().ScalarBaseMult(bigIntToByte(k))
|
||||
C1 = elliptic.Marshal(publicKey.Curve, C1x, C1y)
|
||||
|
||||
kx, ky = publicKey.ScalarMult(publicKey.X, publicKey.Y, bigIntToByte(k))
|
||||
kpbBytes := elliptic.Marshal(publicKey, kx, ky)
|
||||
t, err = kdf(kpbBytes, kLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isAllZero(t) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
C2 := make([]byte, kLen)
|
||||
for i := 0; i < kLen; i++ {
|
||||
C2[i] = m[i] ^ t[i]
|
||||
}
|
||||
|
||||
C3 := calculateHash(kx, m, ky)
|
||||
|
||||
r := make([]byte, 0, len(C1)+len(C2)+len(C3))
|
||||
r = append(r, C1...)
|
||||
r = append(r, C2...)
|
||||
r = append(r, C3...)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func isAllZero(m []byte) bool {
|
||||
for i := 0; i < len(m); i++ {
|
||||
if m[i] != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func calculateHash(x *big.Int, M []byte, y *big.Int) []byte {
|
||||
digest := sm3.New()
|
||||
digest.Write(bigIntToByte(x))
|
||||
digest.Write(M)
|
||||
digest.Write(bigIntToByte(y))
|
||||
result := digest.Sum(nil)[:32]
|
||||
return result
|
||||
}
|
||||
|
||||
func bigIntToByte(n *big.Int) []byte {
|
||||
byteArray := n.Bytes()
|
||||
// If the most significant byte's most significant bit is set,
|
||||
// prepend a 0 byte to the slice to avoid being interpreted as a negative number.
|
||||
if (byteArray[0] & 0x80) != 0 {
|
||||
byteArray = append([]byte{0}, byteArray...)
|
||||
}
|
||||
return byteArray
|
||||
}
|
||||
|
||||
func kdf(Z []byte, klen int) ([]byte, error) {
|
||||
ct := 1
|
||||
end := (klen + 31) / 32
|
||||
result := make([]byte, 0)
|
||||
for i := 1; i <= end; i++ {
|
||||
b, err := sm3hash(Z, toByteArray(ct))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, b...)
|
||||
ct++
|
||||
}
|
||||
last, err := sm3hash(Z, toByteArray(ct))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if klen%32 == 0 {
|
||||
result = append(result, last...)
|
||||
} else {
|
||||
result = append(result, last[:klen%32]...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func sm3hash(sources ...[]byte) ([]byte, error) {
|
||||
b, err := joinBytes(sources...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
md := make([]byte, 32)
|
||||
h := x509.SM3.New()
|
||||
h.Write(b)
|
||||
h.Sum(md[:0])
|
||||
return md, nil
|
||||
}
|
||||
|
||||
func joinBytes(params ...[]byte) ([]byte, error) {
|
||||
var buffer bytes.Buffer
|
||||
for i := 0; i < len(params); i++ {
|
||||
_, err := buffer.Write(params[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func toByteArray(i int) []byte {
|
||||
byteArray := []byte{
|
||||
byte(i >> 24),
|
||||
byte((i & 16777215) >> 16),
|
||||
byte((i & 65535) >> 8),
|
||||
byte(i & 255),
|
||||
}
|
||||
return byteArray
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateSM4Key() []byte {
|
||||
str := "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"
|
||||
buffer := make([]byte, 16)
|
||||
for i := 0; i < 16; i++ {
|
||||
nextInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(str))))
|
||||
buffer[i] = str[nextInt.Int64()]
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func GenAccessToken(token string) string {
|
||||
if token != "" {
|
||||
return token
|
||||
}
|
||||
now := time.Now()
|
||||
return strings.ToUpper(Md5Hash(now.Format("2006A01B02CD15E04F05"), ""))
|
||||
}
|
||||
|
||||
func Md5Hash(password, salt string) string {
|
||||
m := md5.New()
|
||||
m.Write([]byte(salt + password))
|
||||
return hex.EncodeToString(m.Sum(nil))
|
||||
}
|
||||
|
||||
// GetSM4IV 获取SM4的IV
|
||||
func GetSM4IV() []byte {
|
||||
return []byte("UISwD9fW6cFh9SNS")
|
||||
}
|
||||
|
||||
func Padding(input []byte, mode int) []byte {
|
||||
if input == nil {
|
||||
return nil
|
||||
} else {
|
||||
var ret []byte
|
||||
if mode == 1 {
|
||||
p := 16 - len(input)%16
|
||||
ret = make([]byte, len(input)+p)
|
||||
copy(ret, input)
|
||||
|
||||
for i := 0; i < p; i++ {
|
||||
ret[len(input)+i] = byte(p)
|
||||
}
|
||||
} else {
|
||||
p := input[len(input)-1]
|
||||
ret = make([]byte, len(input)-int(p))
|
||||
copy(ret, input[:len(input)-int(p)])
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
package postbank
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ZZMarquis/gm/sm4"
|
||||
"math/big"
|
||||
"qteam/app/utils/postbank/internal/sm2"
|
||||
"qteam/app/utils/postbank/internal/util"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func checkInData(reqData map[string]string, key string) (string, error) {
|
||||
data, ok := reqData[key]
|
||||
if !ok {
|
||||
return "", errors.New("请求数据中不存在" + key)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func Decrypt(merchantId, privateKey, sopPublicKey, respJson string, isRequest bool) (string, error) {
|
||||
var reqData map[string]string
|
||||
err := json.Unmarshal([]byte(respJson), &reqData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
keys := [4]string{}
|
||||
if isRequest {
|
||||
keys = [4]string{"request", "signature", "encryptKey", "accessToken"}
|
||||
} else {
|
||||
keys = [4]string{"response", "signature", "encryptKey", "accessToken"}
|
||||
}
|
||||
var inEncryptKey, inAccessToken, inData, inSignature string
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
data, err := checkInData(reqData, keys[i])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
switch keys[i] {
|
||||
case "request", "response":
|
||||
inData = data
|
||||
case "signature":
|
||||
inSignature = data
|
||||
case "encryptKey":
|
||||
inEncryptKey = data
|
||||
case "accessToken":
|
||||
inAccessToken = data
|
||||
}
|
||||
}
|
||||
|
||||
checked := verify(fmt.Sprintf("%s%s%s", inData, inEncryptKey, inAccessToken), inSignature, sopPublicKey, merchantId)
|
||||
if !checked {
|
||||
return "", errors.New("签名验证失败")
|
||||
}
|
||||
|
||||
priKey, err := sm2.ReadPrivateKeyFromHex(privateKey)
|
||||
if err != nil {
|
||||
return "", errors.New("读取私钥失败")
|
||||
}
|
||||
hexEncryptKey, err := hex.DecodeString(inEncryptKey)
|
||||
if err != nil {
|
||||
return "", errors.New("解密sm4key失败")
|
||||
}
|
||||
sm4Key, err := util.Sm2Decrypt(priKey, hexEncryptKey)
|
||||
|
||||
request, _ := base64.StdEncoding.DecodeString(inData)
|
||||
|
||||
encryptedSm4Key, err := sm4.CBCDecrypt(sm4Key, util.GetSM4IV(), request)
|
||||
|
||||
return string(util.Padding(encryptedSm4Key, 0)), nil
|
||||
}
|
||||
|
||||
func Encrypt(merchantId, privateKey, sopPublicKey, inputJson, token string, isRequest bool) (string, error) {
|
||||
sm4Key := util.GenerateSM4Key()
|
||||
iv := util.GetSM4IV()
|
||||
tmp, err := sm4.CBCEncrypt(sm4Key, iv, util.Padding([]byte(inputJson), 1))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
responseMsg := base64.StdEncoding.EncodeToString(tmp)
|
||||
responseMsg = addNewline(responseMsg)
|
||||
|
||||
pubKey, err := sm2.ReadPublicKeyFromHex(sopPublicKey)
|
||||
if err != nil {
|
||||
return "", errors.New("读取私钥失败")
|
||||
}
|
||||
encryptKeyBytes, err := util.Sm2Encrypt(pubKey, sm4Key)
|
||||
encryptKey := strings.ToUpper(hex.EncodeToString(encryptKeyBytes))
|
||||
|
||||
//accessToken := util.GenAccessToken(token)
|
||||
accessToken := token
|
||||
signContent := fmt.Sprintf("%s%s%s", responseMsg, encryptKey, accessToken)
|
||||
signature, err := sign(merchantId, privateKey, signContent)
|
||||
|
||||
var reqData map[string]string
|
||||
|
||||
if isRequest {
|
||||
reqData = map[string]string{
|
||||
"request": responseMsg,
|
||||
"signature": signature,
|
||||
"encryptKey": encryptKey,
|
||||
"accessToken": accessToken,
|
||||
}
|
||||
} else {
|
||||
reqData = map[string]string{
|
||||
"response": responseMsg,
|
||||
"signature": signature,
|
||||
"encryptKey": encryptKey,
|
||||
"accessToken": accessToken,
|
||||
}
|
||||
}
|
||||
|
||||
jsonStr, err := json.Marshal(reqData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(jsonStr), err
|
||||
}
|
||||
|
||||
// GenerateKey 生成密钥对
|
||||
func GenerateKey() (string, string) {
|
||||
pri, _ := sm2.GenerateKey(rand.Reader)
|
||||
hexPri := pri.D.Text(16)
|
||||
// 获取公钥
|
||||
publicKeyHex := publicKeyToString(&pri.PublicKey)
|
||||
return strings.ToUpper(hexPri), publicKeyHex
|
||||
}
|
||||
|
||||
// publicKeyToString 公钥sm2.PublicKey转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
|
||||
func publicKeyToString(publicKey *sm2.PublicKey) string {
|
||||
xBytes := publicKey.X.Bytes()
|
||||
yBytes := publicKey.Y.Bytes()
|
||||
|
||||
// 确保坐标字节切片长度相同
|
||||
byteLen := len(xBytes)
|
||||
if len(yBytes) > byteLen {
|
||||
byteLen = len(yBytes)
|
||||
}
|
||||
|
||||
// 为坐标补齐前导零
|
||||
xBytes = append(make([]byte, byteLen-len(xBytes)), xBytes...)
|
||||
yBytes = append(make([]byte, byteLen-len(yBytes)), yBytes...)
|
||||
|
||||
// 添加 "04" 前缀
|
||||
publicKeyBytes := append([]byte{0x04}, append(xBytes, yBytes...)...)
|
||||
|
||||
return strings.ToUpper(hex.EncodeToString(publicKeyBytes))
|
||||
}
|
||||
|
||||
func addNewline(str string) string {
|
||||
lineLength := 76
|
||||
var result strings.Builder
|
||||
for i := 0; i < len(str); i++ {
|
||||
if i > 0 && i%lineLength == 0 {
|
||||
result.WriteString("\r\n")
|
||||
}
|
||||
result.WriteByte(str[i])
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
func sign(merchantId string, privateKeyHex string, signContent string) (string, error) {
|
||||
privateKey, err := sm2.ReadPrivateKeyFromHex(privateKeyHex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
r, s, err := sm2.Sm2Sign(privateKey, []byte(signContent), []byte(merchantId), rand.Reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return rSToSign(r, s), nil
|
||||
}
|
||||
|
||||
func verify(content string, signature string, publicKeyStr string, merchantId string) bool {
|
||||
pubKey, err := sm2.ReadPublicKeyFromHex(publicKeyStr)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("pubKeyBytes sm2 ReadPublicKeyFromHex err: %v", err))
|
||||
}
|
||||
r, s := signToRS(signature)
|
||||
return sm2.Sm2Verify(pubKey, []byte(content), []byte(merchantId), r, s)
|
||||
}
|
||||
|
||||
func signToRS(signStr string) (*big.Int, *big.Int) {
|
||||
signSub := strings.Split(signStr, "#")
|
||||
if len(signSub) != 2 {
|
||||
panic(fmt.Sprintf("err rs: %x", signSub))
|
||||
}
|
||||
r, _ := new(big.Int).SetString(signSub[0], 16)
|
||||
s, _ := new(big.Int).SetString(signSub[1], 16)
|
||||
return r, s
|
||||
}
|
||||
|
||||
func rSToSign(r *big.Int, s *big.Int) string {
|
||||
rStr := r.Text(16)
|
||||
sStr := s.Text(16)
|
||||
return fmt.Sprintf("%s#%s", rStr, sStr)
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package postbank
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
merchantId = "YANWEI001"
|
||||
// 模块名称 服开门户网站-文档中心-API文档-接口方法业务前缀
|
||||
// 此处默认为普通接口取数币服务模块名称 ecny 文件接口这里传ufile
|
||||
moduleName = "ecny"
|
||||
// appID 服开门户网站-用户中心-我的应用-APP_ID
|
||||
appID = "961925472724332544001"
|
||||
// url 协议 + 定版ip:端口号 + 接口方法业务前缀 + 合作方编号 + 接口地址后缀 + 合作方交易流水号(工具类生成)
|
||||
url = "http://220.248.252.123:8443/sop-h5/biz/${moduleName}/${merchantId}.htm?partnerTxSriNo=${partnerTxSriNo}"
|
||||
// h5token h5申请token的url 协议 + 定版ip:端口号 + 接口地址业务前缀 + 合作方编号 + 合作方交易流水号(工具类生成)
|
||||
h5token = "http://220.248.252.123:8443/sop-h5/h5/authToken/apply/${merchantId}?partnerTxSriNo=${partnerTxSriNo}"
|
||||
// 合作方私钥
|
||||
privateKey = "A56E64D94612E26095091C7321846979B3FA4AF2DCD3B13375FDE3888F2E126E"
|
||||
// 合作方公钥
|
||||
publicKey = "04A7DA7EA6F2974F3582DC2BB82190BEAD40C78DC54461020938306FD470C5B0D689DDA8B53D47C16593D67BDC3BE1B6594C4ECB6C536F440CCD02A0405B537873"
|
||||
// 服开与合作方配对公钥 服开门户网站-用户中心-公钥配置-平台生产公钥
|
||||
sopPublicKey = "041F63D23554FC1F5A36D43ABD0DE368A202849BCC03BEB34E7EF4F8C478E12F5529DB649B7C2C2DB5F55B4DC89A6EC83A8226218B7D931638DBFC4B344C628C33"
|
||||
// sopPrivateKey 服开与合作方配对私钥 服开门户网站-用户中心-私钥配置-平台生产私钥
|
||||
sopPrivateKey = "174AB6AB44FF23A6B65934B54E9D94B00A81D0D50D57DB0C38257E2AFB2D6C35"
|
||||
// 文件上传接口
|
||||
// fileUrl = http://220.248.252.123:8443/sop-h5/biz/ufile/upload/接口名/接口版本/合作方编号/appID/流水号
|
||||
fileUrl = "http://220.248.252.123:8443/sop-h5/biz/ufile/upload/${method}/${version}/${merchantId}/${appID}/${partnerTxSriNo}"
|
||||
// 解码格式 如果文件下载时解析出来的报文乱码,请改变解码格式
|
||||
charsetName = "gbk"
|
||||
m_id = "lansexiongdi666"
|
||||
pri = "7FE05FEB81673ABF0AC4F9EEB99FB03BFE7BE432CA474CB9AAD8E302CAF00766"
|
||||
msoapub = "04A3C235C15070E127679628FE024E6C77FFCB1840B4EFBE09D19CBE0EE69BEA231912943984D34C03989771E503DFBB4ECB186F4BBC27764109E63C2875005C81"
|
||||
soapb = "0432F7E91033AC40472885B11EB673B1DE942FD555E95CD12E799CC50DD9E93B04A6AC254D725C22DCBD65436D97A46727B09FFFA1B1098EEA8D43F79AAB84F5B1"
|
||||
)
|
||||
|
||||
func TestMarketSm4Decrypt(t *testing.T) {
|
||||
respJson := "{\"request\":\"DUgb\\/aU\\/0ahjHU3e93s4fGNj\\/b2XrhpCSqMhC\\/Q2aXt6BK8W2fKfOBBHTBCtR+5cMiESM27ogbR+\\r\\nzvPqJnyUnNvlVbvO6pLxo3AtieKSAlQkyGSa\\/26NpopHbPrvWKl1Ta8rfEDHLkYeZzLisS85GOQN\\r\\nTLdqamxjhP1tJwvlvdfkLd1zVJ\\/QlpmYSLkzCsiWcOmnr8YlqYb5PMKuOl5yM5KNHVMeHCbfIulV\\r\\nkjNFDSdlFlG+HhRS2KLD1TIYGafLt0DOB+PCg\\/WNb1+09KgLJ2kSBlim\\/MGjlw1hiJY0nLcp1yOL\\r\\nZYK4DqCY5ZPeE9sbzxfe\\/PvyMT0MYvIVJD1cLKrLKxFGWpq0+69L9s4IW10aY5uD4yc4iSoPEzq7\\r\\nLzfGJEybZErNSaCKaFa4sB6foMPgHwUXppw5745PDHrcBwuK746kBQx3yVTAI4pjDx4YuxhjrPa+\\r\\nb3I4NY2PG9uFYBoAewxC34Z5Y9RxRzON3dFY0HPPgNymH+CHq\\/B6OUl2ag2vg4bQA7bTJQAtVfhx\\r\\n+QWcSB2SofsD7JTQGBwNScgfRWhQ7ptQbC5BZV+boP+z1JRb0h3GpfOfpbRPDx51WfQbMOztOkpM\\r\\njYhc2DJ3VjM=\",\"signature\":\"c5bbcba4c567af196959ad2eefe39e84e6708837fff38b8fddcf5fcbc36192d5#fa805cc6c3f257f5bc584cf1d9bb9159530053e5944bfacdbf361e3f09c799b4\",\"encryptKey\":\"0449D815DE5CCDC30CFBCB590F7EEE7C8FC306565608B939B8CDFDDDD8A11A188EE787F78342A564B8F19C3FD7D3573CD48883BC7C9A0B195BCA75DD3FAE1CE487F3C955E560A4CEC0FFC039AE341214476E357BA5DBE06F994EF59AC29A97CDC878088D7D7ADB0508BD293EE7E9CC882B\",\"accessToken\":null}" // 解析接到的请求
|
||||
reqMsg, err := Decrypt(m_id, pri, msoapub, respJson, true)
|
||||
fmt.Println(reqMsg, err)
|
||||
}
|
||||
|
||||
// TestSm4Decrypt test sm4 decrypt
|
||||
func TestSm4Decrypt(t *testing.T) {
|
||||
respJson := "{\n\t\"accessToken\": \"C0A3853384EA0443C2942FF1A1B0757D\",\n\t\"encryptKey\": \"04F15C783C14DBD91DE13696BFCDAA4523F2D6CABB610A8BA809D7224234E53422EA4143ED6C3BD92CB9745A0330142E94C7F6EB36B5601A133F9E7E8C42E18C693BC975AD245EF14FDA9734523F407B7561CC390883D8D45C35DD8E3575970BFD75554EB9119F4A18B65EC5F3AB37D866\",\n\t\"request\": \"JBKuG9pmsYwLgFPJL5nFci9jEgX9M60ebFK8lNteJqJZgZZEF7Sg7+qabTq7AOhHN/u9a+O4Zv8e\\r\\nIncXNC9sFPKOrqRJaMmAZTrDCwD3VhDt/maMn9lQbdkXaqMwo4S1iYiqoni5riS/8ivbFqz4sQvv\\r\\nPoiyIaQCMkqAAEkFxL6aSlPGY7f7kdLHK6cpixT3h2H1coMPuCQrG1J7jnbe+IlPJiVOKn5Hchus\\r\\n4TnHAuH/y9/8W9sW2tILApqm0MUpNxkor3qkAEqkbhciuJJDp/lDqebcJeRt2UET6UtTFTECYEWR\\r\\nNPfQCHKQxMWWserd/wCcWMx8ePPK/UKqUW8Rpg6hncKcC8b0U5i0z3MFuKJscz/DAS/X6Vex9kUp\\r\\njvy5c+ahm/rx0y5pTkNKP9KzVTFE6SffyA3E5NR+6fIQ9VvOU1A/YPiBhCvLF/dCodUk\",\n\t\"signature\": \"60bd9f1bb4a234acf4c8789f048151cbd688538b8f02d2cf87cdd74309c346e6#6aaaad738d9c10b3aaf85c86b5134d95b4d815dc017b43bc9e5e012243eb26f6\"\n}" // 解析接到的请求
|
||||
reqMsg, err := Decrypt(merchantId, privateKey, sopPublicKey, respJson, true)
|
||||
fmt.Println(reqMsg, err)
|
||||
}
|
||||
|
||||
func TestSm4Encrypt(t *testing.T) {
|
||||
input := "12314"
|
||||
res, err := Encrypt(merchantId, privateKey, sopPublicKey, input, "", true)
|
||||
fmt.Println(res, err)
|
||||
}
|
||||
|
||||
func TestGenerateKey(t *testing.T) {
|
||||
hexPri, publicKeyHex := GenerateKey()
|
||||
|
||||
fmt.Println(hexPri, publicKeyHex)
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package smtypes
|
||||
|
||||
type RequestData struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
EncryptKey string `json:"encryptKey"`
|
||||
Request string `json:"request"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type ResponseData struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
EncryptKey string `json:"encryptKey"`
|
||||
Response string `json:"request"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/qit-team/snow-core/redis"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AcquireLock 尝试获取分布式锁
|
||||
func AcquireLock(key string, expired time.Duration) (bool, error) {
|
||||
ctx := context.Background()
|
||||
client := redis.GetRedis()
|
||||
// 设置锁的值,这里可以是一个随机的UUID,用于检测是否是同一个客户端持有的锁锁
|
||||
lockValue := fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
result := client.SetNX(ctx, key, lockValue, expired)
|
||||
return result.Val(), result.Err()
|
||||
}
|
||||
|
||||
func Del(key string) error {
|
||||
client := redis.GetRedis()
|
||||
result := client.Del(context.Background(), key)
|
||||
return result.Err()
|
||||
}
|
||||
|
||||
func Set(key string, value interface{}, expired time.Duration) error {
|
||||
client := redis.GetRedis()
|
||||
return client.Set(context.Background(), key, value, expired).Err()
|
||||
}
|
||||
|
||||
func Get(key string) (string, error) {
|
||||
client := redis.GetRedis()
|
||||
result := client.Get(context.Background(), key)
|
||||
return result.Result()
|
||||
}
|
||||
|
||||
func Decr(key string) (int64, error) {
|
||||
client := redis.GetRedis()
|
||||
result := client.Decr(context.Background(), key)
|
||||
return result.Result()
|
||||
}
|
||||
|
||||
func Incr(key string) (int64, error) {
|
||||
client := redis.GetRedis()
|
||||
result := client.Incr(context.Background(), key)
|
||||
return result.Result()
|
||||
}
|
||||
|
||||
func SetNX(key string, value interface{}, expired time.Duration) (bool, error) {
|
||||
client := redis.GetRedis()
|
||||
result := client.SetNX(context.Background(), key, value, expired)
|
||||
return result.Result()
|
||||
}
|
|
@ -85,7 +85,7 @@ func encryptLoc(publicKeyStr, data string) (string, error) {
|
|||
fmt.Println(err)
|
||||
}
|
||||
resultStr := hex.EncodeToString(decrypt)
|
||||
return base64.StdEncoding.EncodeToString([]byte(resultStr)), nil
|
||||
return resultStr, nil
|
||||
}
|
||||
|
||||
func decryptLoc(publicKeyStr, privateKeyStr, cipherText string) (string, error) {
|
||||
|
@ -158,3 +158,11 @@ func VerSm2Sig(pub *sm2.PublicKey, msg []byte, sign []byte) bool {
|
|||
isok := pub.Verify(msg, sign)
|
||||
return isok
|
||||
}
|
||||
|
||||
func SignSm2(privateKey *sm2.PrivateKey, msg []byte) ([]byte, error) {
|
||||
sign, err := privateKey.Sign(rand.Reader, msg, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sign, nil
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
@ -371,7 +372,7 @@ func IsNil(x interface{}) bool {
|
|||
}
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Id string
|
||||
Phone string
|
||||
}
|
||||
|
||||
|
@ -410,3 +411,49 @@ func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
|
|||
})
|
||||
return token, Claims, err
|
||||
}
|
||||
|
||||
// generateRequestNumber 生成一个全局唯一的请求流水号
|
||||
func GenerateRequestNumber() string {
|
||||
// 时间戳部分,精确到毫秒
|
||||
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
|
||||
// 机器标识部分,可以是IP地址、主机名或者自定义的机器唯一标识
|
||||
// 这里简单使用随机数模拟机器标识部分
|
||||
machineID := mrand.Int63n(1000)
|
||||
|
||||
// 序列号部分,使用原子操作保证线程安全的递增
|
||||
var sequence uint32
|
||||
atomic.AddUint32(&sequence, 1)
|
||||
|
||||
// 生成随机字符串部分,增加复杂度和唯一性
|
||||
randStr := RandomString(4)
|
||||
|
||||
// 拼接各部分生成最终的请求流水号
|
||||
return fmt.Sprintf("%d-%d-%d-%s", timestamp, machineID, sequence, randStr)
|
||||
}
|
||||
|
||||
// randomString 生成指定长度的随机字符串
|
||||
func RandomString(n int) string {
|
||||
const (
|
||||
letterIdxBits = 6 // 6 bits to represent a letter index
|
||||
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||
)
|
||||
var letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
var src = mrand.NewSource(time.Now().UnixNano())
|
||||
b := make([]byte, n)
|
||||
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
|
||||
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
|
||||
if remain == 0 {
|
||||
cache, remain = src.Int63(), letterIdxMax
|
||||
}
|
||||
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
|
||||
b[i] = letterBytes[idx]
|
||||
i--
|
||||
}
|
||||
cache >>= letterIdxBits
|
||||
remain--
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
|
|
@ -36,11 +36,18 @@ type Config struct {
|
|||
OpenApi OpenApi `toml:"OpenApi"`
|
||||
Jwt Jwt `toml:"Jwt"`
|
||||
AliOss AliOss `toml:"AliOss"`
|
||||
YouChu YouChu `toml:"YouChu"`
|
||||
YouChu YouChuConfig `toml:"YouChu"`
|
||||
}
|
||||
|
||||
type YouChu struct {
|
||||
MilkUrl string
|
||||
type YouChuConfig struct {
|
||||
Host string
|
||||
NotifyUrl string
|
||||
MilkUrl string
|
||||
MerchantId string //合作方编号
|
||||
MchtNo string //上单商户号
|
||||
AppID string //appid
|
||||
SopPublicKey string //服开公钥
|
||||
SopPrivateKey string //服开私钥
|
||||
}
|
||||
|
||||
type AliOss struct {
|
||||
|
|
8
go.mod
8
go.mod
|
@ -6,6 +6,8 @@ require (
|
|||
gitee.com/chengdu_blue_brothers/openapi-go-sdk v0.0.2
|
||||
github.com/BurntSushi/toml v0.4.1
|
||||
github.com/Shopify/sarama v1.19.0
|
||||
github.com/ZZMarquis/gm v1.3.2
|
||||
github.com/ahmetb/go-linq/v3 v3.2.0
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
github.com/forgoer/openssl v1.6.0
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
|
@ -21,6 +23,7 @@ require (
|
|||
github.com/qit-team/snow-core v0.1.28
|
||||
github.com/qit-team/work v0.3.11
|
||||
github.com/robfig/cron v1.2.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271
|
||||
github.com/swaggo/gin-swagger v1.3.3
|
||||
github.com/swaggo/swag v1.7.9
|
||||
|
@ -29,13 +32,13 @@ require (
|
|||
google.golang.org/grpc v1.56.3
|
||||
google.golang.org/protobuf v1.30.0
|
||||
gopkg.in/go-playground/validator.v9 v9.31.0
|
||||
xorm.io/builder v0.3.9
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/ahmetb/go-linq/v3 v3.2.0 // indirect
|
||||
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect
|
||||
github.com/alibabacloud-go/tea v1.1.17 // indirect
|
||||
github.com/alibabacloud-go/tea-utils v1.4.4 // indirect
|
||||
|
@ -67,6 +70,7 @@ require (
|
|||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/hetiansu5/accesslog v1.0.0 // indirect
|
||||
github.com/hetiansu5/cores v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
|
@ -89,6 +93,7 @@ require (
|
|||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/tidwall/gjson v1.12.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
|
@ -111,7 +116,6 @@ require (
|
|||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
stathat.com/c/consistent v1.0.0 // indirect
|
||||
xorm.io/builder v0.3.9 // indirect
|
||||
xorm.io/core v0.7.3 // indirect
|
||||
xorm.io/xorm v1.2.5 // indirect
|
||||
)
|
||||
|
|
9
go.sum
9
go.sum
|
@ -58,6 +58,8 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX
|
|||
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/ZZMarquis/gm v1.3.2 h1:lFtpzg5zeeVMZ/gKi0gtYcKLBEo9XTqsZDHDz6s3Gow=
|
||||
github.com/ZZMarquis/gm v1.3.2/go.mod h1:wWbjZYgruQVd7Bb8UkSN8ujU931kx2XUW6nZLCiDE0Q=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs=
|
||||
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
|
||||
|
@ -137,6 +139,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
|||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -368,6 +371,8 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK
|
|||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
|
@ -640,6 +645,7 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
|||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
|
@ -671,8 +677,11 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z
|
|||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
|
|
Loading…
Reference in New Issue