package mixrepoimpl import ( "context" "encoding/json" "errors" "fmt" "github.com/go-kratos/kratos/v2/log" http2 "github.com/go-kratos/kratos/v2/transport/http" "io" "net/http" "net/url" "time" err2 "voucher/api/err" v1 "voucher/api/v1" "voucher/internal/biz/bo" "voucher/internal/biz/mixrepos" "voucher/internal/biz/vo" "voucher/internal/conf" "voucher/internal/pkg/cmb" "voucher/internal/pkg/helper" "voucher/internal/pkg/request" ) type CmbMixRepoImpl struct { bc *conf.Bootstrap } func NewCmbMixRepoImpl(bc *conf.Bootstrap) mixrepos.CmbMixRepo { return &CmbMixRepoImpl{bc: bc} } func (c *CmbMixRepoImpl) recordBody(ctx context.Context) { httpRequest, ok := http2.RequestFromServerContext(ctx) if !ok { log.Errorf("read body not ok") return } bodyBytes, err := io.ReadAll(httpRequest.Body) if err != nil { log.Errorf("read body not ok, %v", err) return } log.Errorf("body %v", string(bodyBytes)) } func (s *CmbMixRepoImpl) OrderVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbOrderRequest, error) { bizStr, err := s.VerifyNew(ctx, req) if err != nil { return nil, err2.ErrorCmbVerifyFail(err.Error()) } if len(bizStr) == 0 { s.recordBody(ctx) return nil, err2.ErrorCmbBizContentFail("业务参数获取异常,请检查参数是否正确传递") } var bizContent *v1.CmbOrderRequest if err = json.Unmarshal([]byte(bizStr), &bizContent); err != nil { return nil, err2.ErrorCmbBizContentFail(err.Error()) } if err = bizContent.Validate(); err != nil { return nil, err2.ErrorCmbBizContentFail(err.Error()) } return bizContent, nil } func (s *CmbMixRepoImpl) QueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryRequest, error) { bizStr, err := s.VerifyNew(ctx, req) if err != nil { return nil, err2.ErrorCmbVerifyFail(err.Error()) } if len(bizStr) == 0 { s.recordBody(ctx) return nil, err2.ErrorCmbBizContentFail("业务参数获取异常,请检查参数是否正确传递") } var bizContent *v1.CmbQueryRequest if err = json.Unmarshal([]byte(bizStr), &bizContent); err != nil { return nil, err2.ErrorCmbBizContentFail(err.Error()) } if err = bizContent.Validate(); err != nil { return nil, err2.ErrorCmbBizContentFail(err.Error()) } return bizContent, nil } func (s *CmbMixRepoImpl) ProductQueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryProductRequest, error) { bizStr, err := s.VerifyNew(ctx, req) if err != nil { return nil, err } if len(bizStr) == 0 { s.recordBody(ctx) return nil, err2.ErrorCmbBizContentFail("业务参数获取异常,请检查参数是否正确传递") } var bizContent *v1.CmbQueryProductRequest if err = json.Unmarshal([]byte(bizStr), &bizContent); err != nil { return nil, err2.ErrorCmbBizContentFail(err.Error()) } if err = bizContent.Validate(); err != nil { return nil, err2.ErrorCmbBizContentFail(err.Error()) } return bizContent, nil } func (s *CmbMixRepoImpl) Verify(_ context.Context, req *v1.CmbRequest) (string, error) { str := cmb.SortStructStr(req) b, err := cmb.Verify(s.bc.Cmb.CmbSm2Puk, str, req.Sign) if err != nil { return "", err2.ErrorCmbVerifyFail(err.Error()) } if !b { return "", err2.ErrorCmbVerifyFail("签名验证失败") } bizStr, err := cmb.Decrypt(s.bc.Cmb.Sm2Prk, req.EncryptBody) if err != nil { return "", err2.ErrorCmbBizContentDecryptFail(err.Error()) } return bizStr, nil } func (s *CmbMixRepoImpl) VerifyNew(_ context.Context, req *v1.CmbRequest) (string, error) { str := cmb.SortStructStr(req) //b, err := cmb.Verify(s.bc.Cmb.CmbSm2Puk, str, req.Sign) b, err := cmb.VerifyBody(str, req.Sign, s.bc.Cmb.CmbSm2Puk) if err != nil { return "", err2.ErrorCmbVerifyFail(err.Error()) } if !b { return "", err2.ErrorCmbVerifyFail("签名验证失败") } bizStr, err := cmb.DecryptBody(&cmb.Decrypts{req.EncryptBody, s.bc.Cmb.CmbSm2Pik}) if err != nil { return "", err2.ErrorCmbBizContentDecryptFail(err.Error()) } return string(bizStr), nil } func (s *CmbMixRepoImpl) GetRequest(_ context.Context, reqBo *bo.CmbRequestBo) (*v1.CmbRequest, error) { //encryptBody, err := cmb.Encrypt(s.bc.Cmb.CmbSm2Puk, reqBo.BizContent) encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: s.bc.Cmb.CmbSm2Puk, JsonParam: reqBo.BizContent}) if err != nil { return nil, err } req := &v1.CmbRequest{ Mid: s.bc.Cmb.Mid, Aid: s.bc.Cmb.Aid, Date: time.Now().Format("20060102150405"), Random: string(cmb.RandomBytes(16)), KeyAlias: s.bc.Cmb.KeyAlias, CmbKeyAlias: s.bc.Cmb.CmbKeyAlias, EncryptBody: encryptBody, Sign: "", } str := fmt.Sprintf("%s?%s", reqBo.FuncName, cmb.SortStructStr(req)) sing, err := cmb.Sign(s.bc.Cmb.CmbSm2Pik, str) if err != nil { return nil, err } req.Sign = sing return req, nil } func (s *CmbMixRepoImpl) GetMockRequest(_ context.Context, bizContent string) (*v1.CmbRequest, error) { if len(s.bc.Cmb.Sm2Puk) == 0 { return nil, errors.New("mock sm2 puk is empty") } if len(s.bc.Cmb.CmbSm2Pik) == 0 { return nil, errors.New("mock cmb sm2 pik is empty") } //encryptBody, err := cmb.Encrypt(s.bc.Cmb.Sm2Puk, bizContent) encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: s.bc.Cmb.CmbSm2Puk, JsonParam: bizContent}) if err != nil { return nil, err } req := &v1.CmbRequest{ Mid: s.bc.Cmb.Mid, Aid: s.bc.Cmb.Aid, Date: time.Now().Format("20060102150405"), Random: string(cmb.RandomBytes(16)), KeyAlias: s.bc.Cmb.KeyAlias, CmbKeyAlias: s.bc.Cmb.CmbKeyAlias, EncryptBody: encryptBody, Sign: "", } sign, err := cmb.SignBody(cmb.SortStructStr(req), s.bc.Cmb.CmbSm2Pik) if err != nil { return nil, err } req.Sign = sign return req, nil } func (s *CmbMixRepoImpl) VerifyResponse(_ context.Context, req *v1.CmbReply) error { str := cmb.SortStructStr(req) //b, err := cmb.Verify(s.bc.Cmb.CmbSm2Puk, str, req.Sign) b, err := cmb.VerifyBody(str, req.Sign, s.bc.Cmb.CmbSm2Puk) if err != nil { return err } if !b { return errors.New("签名验证失败") } return nil } func (s *CmbMixRepoImpl) GetResponse(_ context.Context, reqBo *bo.CmbResponseBo) (*v1.CmbReply, error) { reply := &v1.CmbReply{ RespCode: reqBo.RespCode, RespMsg: reqBo.RespMsg, Date: time.Now().Format("20060102150405"), KeyAlias: s.bc.Cmb.KeyAlias, CmbKeyAlias: s.bc.Cmb.CmbKeyAlias, EncryptBody: "", Sign: "", } if len(reqBo.BizContent) > 0 { encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: s.bc.Cmb.CmbSm2Puk, JsonParam: reqBo.BizContent}) if err != nil { return nil, err } reply.EncryptBody = encryptBody } sign, err := cmb.SignBody(cmb.SortStructStr(reply), s.bc.Cmb.Sm2Prk) if err != nil { return nil, err } reply.Sign = sign return reply, nil } func (s *CmbMixRepoImpl) Request(ctx context.Context, req *v1.CmbRequest, uri string) (*v1.CmbReply, error) { kvRows := helper.SortStructFieldsByKey(req) uv := url.Values{} for _, kv := range kvRows { uv.Set(kv.Key, fmt.Sprintf("%v", kv.Value)) } h := http.Header{ "Content-Type": []string{"application/x-www-form-urlencoded"}, } r := uri + "?" + uv.Encode() _, bodyBytes, err := request.Post(ctx, r, nil, request.WithHeaders(h)) if err != nil { log.Errorf("请求掌上生活报错,url=%s,err=%v", r, err) return nil, err } log.Warnf("请求掌上生活响应结果,url:%s,bodyBytes:%s", r, string(bodyBytes)) var response *v1.CmbReply if err = json.Unmarshal(bodyBytes, &response); err != nil { log.Errorf("请求掌上生活返回数据解析报错,err:%s", string(bodyBytes), err.Error()) return nil, err } if response.RespCode != vo.CmbResponseStatusSuccess.GetValue() { log.Errorf("请求掌上生活返回报错,err:%s", r, response.RespMsg) return nil, fmt.Errorf(response.RespMsg) } return response, nil } func (s *CmbMixRepoImpl) Decrypt(_ context.Context, encryptBody string) (string, error) { if len(s.bc.Cmb.CmbSm2Pik) == 0 { return "", errors.New("mock CmbSm2Pik is empty") } rs, err := cmb.DecryptBody(&cmb.Decrypts{EncryptBody: encryptBody, PrivateKey: s.bc.Cmb.CmbSm2Pik}) return string(rs), err //return cmb.Decrypt(s.bc.Cmb.CmbSm2Pik, encryptBody) }