229 lines
6.1 KiB
Go
229 lines
6.1 KiB
Go
package internal
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"gitea.cdlsxd.cn/sdk/plugin/proto"
|
|
sdkutils "gitea.cdlsxd.cn/sdk/plugin/utils"
|
|
"github.com/go-playground/validator/v10"
|
|
"net/http"
|
|
"plugins/union_pay_cpn/internal/po"
|
|
"plugins/union_pay_cpn/internal/vo"
|
|
"plugins/utils/request"
|
|
"plugins/utils/union_pay"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
AppId string `validate:"required" json:"app_id"`
|
|
ChNlId string `validate:"required" json:"chnlId"` // 渠道方代码
|
|
IV string `validate:"required" json:"iv"` // 加密密钥
|
|
KEY string `validate:"required" json:"key"` // 加密密钥
|
|
Prk string `validate:"required" json:"prk"` // 私钥
|
|
Npk string `validate:"required" json:"npk"` // 回调公钥
|
|
}
|
|
|
|
func (c *Config) validate() error {
|
|
err := validator.New().Struct(c)
|
|
if err != nil {
|
|
for _, err = range err.(validator.ValidationErrors) {
|
|
return proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func transConfig(config []byte) (*Config, error) {
|
|
var c Config
|
|
if err := json.Unmarshal(config, &c); err != nil {
|
|
return nil, proto.ErrorConfigFail(fmt.Sprintf("配置参数有误:%s", err.Error()))
|
|
}
|
|
if err := c.validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &c, nil
|
|
}
|
|
|
|
func (c *Config) verify(req *po.Notify, notifyBizMethod string) error {
|
|
if req.Headers.SignMethod != vo.SignMethod {
|
|
return fmt.Errorf("签名方式不匹配")
|
|
}
|
|
if req.Headers.AppId != c.AppId {
|
|
return fmt.Errorf("appId不匹配")
|
|
}
|
|
if req.Headers.BizMethod != notifyBizMethod {
|
|
return fmt.Errorf("业务方法不匹配")
|
|
}
|
|
rehash := union_pay.Sha(req.Headers.Version, c.AppId, req.Headers.BizMethod, req.GetReId(), string(req.ToJson()))
|
|
lowerStr := strings.ToLower(rehash)
|
|
if union_pay.Verify(lowerStr, req.Headers.Sign, []byte(sdkutils.NewPublic().Build(c.Npk))) {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("验签失败")
|
|
}
|
|
|
|
func (c *Config) headers(bizMethod string, req po.Req) http.Header {
|
|
h := make(http.Header)
|
|
h.Add("Content-type", vo.ContentType)
|
|
h.Add("version", vo.Version)
|
|
h.Add("appType", vo.AppType)
|
|
h.Add("signMethod", vo.SignMethod)
|
|
|
|
h.Add("appId", c.AppId)
|
|
h.Add("bizMethod", bizMethod)
|
|
|
|
h.Add("reqId", req.GetReId())
|
|
|
|
now := time.Now()
|
|
milliseconds := now.Unix()*1000 + int64(now.Nanosecond())/1e6
|
|
h.Add("reqTs", fmt.Sprintf("%d", milliseconds))
|
|
|
|
rehash := union_pay.Sha(vo.Version, c.AppId, bizMethod, req.GetReId(), string(req.ToJson()))
|
|
signValue, err := union_pay.Sign(rehash, []byte(sdkutils.NewPrivate().Build(c.Prk)))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
h.Add("sign", signValue)
|
|
return h
|
|
}
|
|
|
|
func (c *Config) Request(ctx context.Context, req po.Req, method, bizMethod string) (http.Header, []byte, error) {
|
|
respHeader, respBody, err := request.Post(
|
|
ctx,
|
|
fmt.Sprintf("%s%s", baseUri, method),
|
|
req.ToJson(),
|
|
request.WithHeaders(c.headers(bizMethod, req)),
|
|
request.WithTimeout(15*time.Second),
|
|
request.WithStatusCodeFunc(func(code int) bool {
|
|
return code == http.StatusOK
|
|
}),
|
|
)
|
|
if err != nil {
|
|
return nil, nil, proto.ErrorRequestFail(err.Error())
|
|
}
|
|
return respHeader, respBody, nil
|
|
}
|
|
|
|
func (c *Config) orderReq(in *proto.OrderRequest) (*po.OrderReq, error) {
|
|
type OrderExtra struct {
|
|
OrderDt string `json:"orderDt"`
|
|
}
|
|
var e OrderExtra
|
|
if err := json.Unmarshal(in.Order.Extra, &e); err != nil {
|
|
return nil, fmt.Errorf("订单拓展参数fail: %w", err)
|
|
}
|
|
|
|
mobile, err := union_pay.Encrypt([]byte(in.Order.Account), []byte(c.KEY), []byte(c.IV))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &po.OrderReq{
|
|
Cmd: vo.OrderCmd,
|
|
AccessId: vo.AccessId,
|
|
EntityTp: vo.EntityTp,
|
|
|
|
ChNlId: c.ChNlId,
|
|
Qid: in.Order.OrderNo,
|
|
OrderDt: e.OrderDt,
|
|
TraceId: in.Order.OrderNo,
|
|
DiscountId: in.Product.ProductNo,
|
|
Mobile: mobile,
|
|
DiscountNum: in.Order.Quantity,
|
|
}, nil
|
|
}
|
|
|
|
func orderResp(request *proto.OrderRequest, resp po.OrderResp) (*proto.OrderResponse, error) {
|
|
data, _ := json.Marshal(resp)
|
|
result := &proto.Result{
|
|
OrderNo: request.Order.OrderNo,
|
|
TradeNo: resp.CouponCd,
|
|
Message: resp.Msg,
|
|
Data: data,
|
|
}
|
|
if !resp.Code.IsSuccess() {
|
|
result.Status = proto.Status_FAIL
|
|
result.Message = resp.GetMsg()
|
|
} else {
|
|
result.Status = proto.Status_ING
|
|
}
|
|
return &proto.OrderResponse{Result: result}, nil
|
|
}
|
|
|
|
func queryReq(in *proto.QueryRequest, chNlId string) (*po.QueryReq, error) {
|
|
type OrderExtra struct {
|
|
OrigDate string `json:"origDate"`
|
|
}
|
|
var e OrderExtra
|
|
if err := json.Unmarshal(in.Order.Extra, &e); err != nil {
|
|
return nil, err
|
|
}
|
|
return &po.QueryReq{
|
|
ChNlId: chNlId,
|
|
Cmd: vo.QueryCmd,
|
|
OrigQid: in.Order.OrderNo,
|
|
OrigDate: e.OrigDate,
|
|
TraceId: in.Order.OrderNo,
|
|
}, nil
|
|
}
|
|
|
|
func queryResp(request *proto.QueryRequest, resp po.QueryResp) *proto.QueryResponse {
|
|
data, _ := json.Marshal(resp)
|
|
result := &proto.Result{
|
|
OrderNo: request.Order.OrderNo,
|
|
TradeNo: resp.CouponCd,
|
|
Status: resp.OperaSt.GetOrderStatus(),
|
|
Message: resp.GetMsg(),
|
|
Data: data,
|
|
}
|
|
return &proto.QueryResponse{Result: result}
|
|
}
|
|
|
|
func notifyReq(in *proto.NotifyRequest) (*po.Notify, error) {
|
|
var h po.Headers
|
|
if err := json.Unmarshal(in.Headers, &h); err != nil {
|
|
return nil, err
|
|
}
|
|
var body po.Body
|
|
if err := json.NewDecoder(strings.NewReader(string(in.Body))).Decode(&body); err != nil {
|
|
return nil, err
|
|
}
|
|
body.CouponNum = union_pay.ConvertToInt(body.CouponNum)
|
|
body.OrderAt = union_pay.ConvertToInt(body.OrderAt)
|
|
body.DiscountAt = union_pay.ConvertToInt(body.DiscountAt)
|
|
for i, cdInfo := range body.CouponCdInfos {
|
|
cdInfo.SubAcctOperAt = union_pay.ConvertToInt(cdInfo.SubAcctOperAt)
|
|
body.CouponCdInfos[i] = cdInfo
|
|
}
|
|
return &po.Notify{
|
|
Headers: &h,
|
|
Body: &body,
|
|
}, nil
|
|
}
|
|
|
|
func notifyResp(request *proto.NotifyRequest, n *po.Notify) (*proto.NotifyResponse, error) {
|
|
pb := &proto.NotifyResponse{
|
|
Result: &proto.Result{
|
|
OrderNo: n.Body.TransSeq,
|
|
TradeNo: n.Body.CouponCd,
|
|
Status: n.Body.OperTp.GetOrderStatus(),
|
|
Message: n.Body.OperTp.GetText(),
|
|
Data: request.Body,
|
|
},
|
|
Return: "success",
|
|
}
|
|
|
|
headers := make(http.Header)
|
|
headers.Set("Content-Type", "text/plain")
|
|
|
|
headersBytes, err := json.Marshal(headers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pb.Headers = string(headersBytes)
|
|
|
|
return pb, err
|
|
}
|