plugins/plugins/union_pay_cpn/internal/transform.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
}