feat(third_party/heepay): 添加Heepay支付实现及相关测试
- 新增HeepayImpl结构体,包含支付、签名生成、加密等功能 - 实现SendCard方法用于发送卡密 - 添加PayNotify方法处理支付回调 - 新增单元测试以验证Heepay相关功能的正确性 - 更新go.mod以引入testify库进行测试
This commit is contained in:
1
go.mod
1
go.mod
@@ -18,6 +18,7 @@ require (
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
github.com/rs/xid v1.6.0
|
||||
github.com/shopspring/decimal v1.4.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/widuu/gojson v0.0.0-20170212122013-7da9d2cd949b
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.10.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
|
||||
|
||||
47
go.sum
47
go.sum
@@ -8,13 +8,9 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bytedance/gopkg v0.1.2-0.20240828084325-780ca9ee70fb h1:glte+Ka6C5efXn/QlEAE/wwNrvE+3mYo/ce69fpvtrE=
|
||||
github.com/bytedance/gopkg v0.1.2-0.20240828084325-780ca9ee70fb/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.12.9 h1:Od1BvK55NnewtGaJsTDeAOSnLVO2BTSLOe0+ooKokmQ=
|
||||
github.com/bytedance/sonic v1.12.9/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
|
||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
|
||||
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/carlmjohnson/requests v0.24.3 h1:LYcM/jVIVPkioigMjEAnBACXl2vb42TVqiC8EYNoaXQ=
|
||||
@@ -30,8 +26,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/duke-git/lancet/v2 v2.3.4 h1:8XGI7P9w+/GqmEBEXYaH/XuNiM0f4/90Ioti0IvYJls=
|
||||
github.com/duke-git/lancet/v2 v2.3.4/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
||||
github.com/duke-git/lancet/v2 v2.3.5 h1:vb49UWkkdyu2eewilZbl0L3X3T133znSQG0FaeJIBMg=
|
||||
github.com/duke-git/lancet/v2 v2.3.5/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||
@@ -43,8 +37,6 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo=
|
||||
github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw=
|
||||
github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI=
|
||||
github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-stomp/stomp/v3 v3.1.3 h1:5/wi+bI38O1Qkf2cc7Gjlw7N5beHMWB/BxpX+4p/MGI=
|
||||
@@ -53,13 +45,10 @@ github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||
@@ -126,56 +115,30 @@ github.com/widuu/gojson v0.0.0-20170212122013-7da9d2cd949b/go.mod h1:9W1pyetRkwX
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.9.0 h1:f+xpAfhQTjR8beiSMe1bnT/25PkeyWmOcI+SjXWguNw=
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.9.0/go.mod h1:T1Z1jyS5FttgQoF6UcGhnM+gF9wU32B4lHO69nXw4FE=
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 h1:ojdSRDvjrnm30beHOmwsSvLpoRF40MlwNCA+Oo93kXU=
|
||||
go.opentelemetry.io/contrib/bridges/otelzap v0.10.0/go.mod h1:oTTm4g7NEtHSV2i/0FeVdPaPgUIZPfQkFbq0vbzqnv0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.10.0 h1:q/heq5Zh8xV1+7GoMGJpTxM2Lhq5+bFxB29tshuRuw0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.10.0/go.mod h1:leO2CSTg0Y+LyvmR7Wm4pUxE8KAmaM2GCVx7O+RATLA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0 h1:C/Wi2F8wEmbxJ9Kuzw/nhP+Z9XaHYMkyDmXy6yR2cjw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0/go.mod h1:0Lr9vmGKzadCTgsiBydxr6GEZ8SsZ7Ks53LzjWG5Ar4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.34.0 h1:opwv08VbCZ8iecIWs+McMdHRcAXzjAeda3uG2kI/hcA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.34.0/go.mod h1:oOP3ABpW7vFHulLpE8aYtNBodrHhMTrvfxUXGvqm7Ac=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 h1:0NIXxOCFx+SKbhCVxwl3ETG8ClLPAa0KuKV6p3yhxP8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0/go.mod h1:ChZSJbbfbl/DcRZNc9Gqh6DYGlfjw4PvO1pEOZH1ZsE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/log v0.10.0 h1:1CXmspaRITvFcjA4kyVszuG4HjA61fPDxMb7q3BuyF0=
|
||||
go.opentelemetry.io/otel/log v0.10.0/go.mod h1:PbVdm9bXKku/gL0oFfUF4wwsQsOPlpo4VEqjvxih+FM=
|
||||
go.opentelemetry.io/otel/log v0.11.0 h1:c24Hrlk5WJ8JWcwbQxdBqxZdOK7PcP/LFtOtwpDTe3Y=
|
||||
go.opentelemetry.io/otel/log v0.11.0/go.mod h1:U/sxQ83FPmT29trrifhQg+Zj2lo1/IPN1PF6RTFqdwc=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/log v0.10.0 h1:lR4teQGWfeDVGoute6l0Ou+RpFqQ9vaPdrNJlST0bvw=
|
||||
go.opentelemetry.io/otel/sdk/log v0.10.0/go.mod h1:A+V1UTWREhWAittaQEG4bYm4gAZa6xnvVu+xKrIRkzo=
|
||||
go.opentelemetry.io/otel/sdk/log v0.11.0 h1:7bAOpjpGglWhdEzP8z0VXc4jObOiDEwr3IYbhBnjk2c=
|
||||
go.opentelemetry.io/otel/sdk/log v0.11.0/go.mod h1:dndLTxZbwBstZoqsJB3kGsRPkpAgaJrWfQg3lhlHFFY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
@@ -220,20 +183,12 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
|
||||
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
558
internal/service/supplier/third_party/heepay.go
vendored
Normal file
558
internal/service/supplier/third_party/heepay.go
vendored
Normal file
@@ -0,0 +1,558 @@
|
||||
package third_party
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gateway/internal/config"
|
||||
"gateway/internal/otelTrace"
|
||||
|
||||
"gateway/internal/service/supplier"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"gateway/internal/models/merchant"
|
||||
"gateway/internal/models/order"
|
||||
"gateway/internal/models/payfor"
|
||||
"gateway/internal/models/road"
|
||||
"gateway/internal/models/supply_model"
|
||||
"gateway/internal/service"
|
||||
"gateway/internal/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/cryptor"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/widuu/gojson"
|
||||
)
|
||||
|
||||
type HeepayImpl struct {
|
||||
web.Controller
|
||||
}
|
||||
|
||||
// HasDependencyHTML 是否有单独的支付页面
|
||||
func (c *HeepayImpl) HasDependencyHTML() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 生成tripleDes算法加密
|
||||
func TripleDesEncrypt(data, key string) string {
|
||||
block, err := des.NewTripleDESCipher([]byte(key))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 创建CBC加密模式
|
||||
blockSize := block.BlockSize()
|
||||
data = PKCS7Padding([]byte(data), blockSize)
|
||||
iv := make([]byte, blockSize)
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
|
||||
// 加密
|
||||
ciphertext := make([]byte, len(data))
|
||||
mode.CryptBlocks(ciphertext, []byte(data))
|
||||
|
||||
return base64.StdEncoding.EncodeToString(ciphertext)
|
||||
}
|
||||
|
||||
// PKCS7填充
|
||||
func PKCS7Padding(data []byte, blockSize int) string {
|
||||
padding := blockSize - len(data)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return string(append(data, padtext...))
|
||||
}
|
||||
|
||||
func generateSignHeepay(agentID, billID, billTime, cardType, cardData, payAmt, notifyURL, timeStamp, key string) string {
|
||||
// 按照指定顺序拼接参数
|
||||
signString := fmt.Sprintf("agent_id=%s&bill_id=%s&bill_time=%s&card_type=%s&card_data=%s&pay_amt=%s¬ify_url=%s&time_stamp=%s|||%s",
|
||||
agentID, billID, billTime, cardType, cardData, payAmt, notifyURL, timeStamp, key)
|
||||
|
||||
return cryptor.Md5String(signString)
|
||||
}
|
||||
|
||||
func (c *HeepayImpl) SendCard(ctx context.Context, jsonStr string, cardInfo supplier.RedeemCardInfo, attach string, merchantId string, orderInfo order.OrderInfo) (bool, string) {
|
||||
agentID, err := convertor.ToInt(gojson.Json(jsonStr).Get("agent_id").Tostring())
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("转换agent_id失败", zap.Error(err))
|
||||
return false, "内部错误请稍后再试试"
|
||||
}
|
||||
payAmt, err := convertor.ToFloat(cardInfo.FaceType)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("转换pay_amt失败", zap.Error(err))
|
||||
return false, "内部错误请稍后再试试"
|
||||
}
|
||||
timeStamp := time.Now().Format("20060102150405")
|
||||
timeStampInt, err := convertor.ToInt(timeStamp)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("转换time_stamp失败", zap.Error(err))
|
||||
return false, "内部错误请稍后再试试"
|
||||
}
|
||||
|
||||
params := map[string]any{
|
||||
"agent_id": agentID,
|
||||
"card_type": int64(10),
|
||||
"bill_id": attach,
|
||||
"bill_time": time.Now().Format("20060102150405"),
|
||||
"card_data": TripleDesEncrypt(fmt.Sprintf("%s,%s,%s", cardInfo.CardNo, cardInfo.Data, cardInfo.FaceType), gojson.Json(jsonStr).Get("3ds_key").Tostring()),
|
||||
"pay_amt": int64(payAmt),
|
||||
"notify_url": fmt.Sprintf("%s%s", config.GetConfig().GatewayAddr(), "/notify/Heepay"),
|
||||
"client_ip": strings.ReplaceAll("127.0.0.1", ".", "_"),
|
||||
"goods_detail": gojson.Json(jsonStr).Get("goods_detail").Tostring(),
|
||||
"time_stamp": timeStampInt,
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
params["sign"] = generateSignHeepay(
|
||||
convertor.ToString(params["agent_id"].(int64)),
|
||||
params["bill_id"].(string),
|
||||
params["bill_time"].(string),
|
||||
convertor.ToString(params["card_type"].(int64)),
|
||||
params["card_data"].(string),
|
||||
convertor.ToString(params["pay_amt"].(int64)),
|
||||
params["notify_url"].(string),
|
||||
convertor.ToString(params["time_stamp"].(int64)),
|
||||
gojson.Json(jsonStr).Get("secret").Tostring(),
|
||||
)
|
||||
|
||||
marshal, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("Map转化为byte数组失败,异常。", zap.Error(err))
|
||||
return false, "内部错误请稍后再试试"
|
||||
}
|
||||
|
||||
req := httplib.NewBeegoRequestWithCtx(ctx, "https://pay.Heepay.com/Api/CardPaySubmitService.aspx", "POST").
|
||||
SetTransport(otelhttp.NewTransport(http.DefaultTransport)).
|
||||
SetTimeout(time.Second*30, time.Second*30).
|
||||
Header("Content-Type", "application/json").
|
||||
Body(params).
|
||||
Header("Accept-Charset", "utf-8")
|
||||
|
||||
response, err := req.String()
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("JD请求失败:", zap.Error(err))
|
||||
return false, ""
|
||||
}
|
||||
|
||||
query, err := url.ParseQuery(response)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("解析失败", zap.Error(err))
|
||||
return false, "内部数据处理失败"
|
||||
}
|
||||
|
||||
otelTrace.Logger.WithContext(ctx).Info("请求参数:" + string(marshal))
|
||||
otelTrace.Logger.WithContext(ctx).Info("远端请求返回数据:" + response)
|
||||
|
||||
if query.Get("ret_code") != "0" {
|
||||
return false, response
|
||||
}
|
||||
return true, "等待兑换"
|
||||
}
|
||||
|
||||
func (c *HeepayImpl) Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData {
|
||||
ctx, cancel := otelTrace.Span(ctx, "JDCardImpl", "Scan", trace.WithAttributes(
|
||||
attribute.String("BankOrderId", orderInfo.BankOrderId),
|
||||
attribute.String("MerchantUid", orderInfo.MerchantUid),
|
||||
attribute.String("ExValue", orderInfo.ExValue),
|
||||
))
|
||||
defer cancel()
|
||||
cdata := supplier.RedeemCardInfo{}
|
||||
err := json.Unmarshal([]byte(orderInfo.ExValue), &cdata)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("格式化数据失败", zap.String("ExValue", orderInfo.ExValue))
|
||||
return supplier.ScanData{Status: "-1", Msg: "订单有有误,请稍后再试"}
|
||||
}
|
||||
ok, str := c.SendCard(ctx, roadInfo.Params, cdata, orderInfo.BankOrderId, orderInfo.MerchantOrderId, orderInfo)
|
||||
var scanData supplier.ScanData
|
||||
if !ok {
|
||||
scanData = supplier.ScanData{
|
||||
Status: "-1",
|
||||
Msg: "订单有有误,请稍后再试:" + str,
|
||||
BankNo: orderInfo.MerchantOrderId,
|
||||
OrderNo: orderInfo.BankOrderId,
|
||||
ReturnData: str,
|
||||
}
|
||||
return scanData
|
||||
}
|
||||
scanData.Status = "00"
|
||||
scanData.OrderNo = orderInfo.BankOrderId
|
||||
scanData.BankNo = orderInfo.MerchantOrderId
|
||||
scanData.OrderPrice = strconv.FormatFloat(orderInfo.OrderAmount, 'f', 2, 64)
|
||||
scanData.ReturnData = str
|
||||
return scanData
|
||||
}
|
||||
|
||||
// KMEncrypt 加密卡密
|
||||
func (c *HeepayImpl) kMEncrypt(kf, appSecret string) (string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
secret := utils.GetMD5LOWER(appSecret)[:16] // 加密秘钥
|
||||
block, err := aes.NewCipher([]byte(secret))
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("Joker: AesDecrypt failed to NewCipher")
|
||||
return "", err
|
||||
}
|
||||
// 数据填充
|
||||
plaintext := utils.PadType(kf)
|
||||
iv := "0102030405060708"
|
||||
mode := cipher.NewCBCEncrypter(block, []byte(iv))
|
||||
mode.CryptBlocks(plaintext, plaintext)
|
||||
return base64.StdEncoding.EncodeToString(plaintext), nil
|
||||
}
|
||||
|
||||
type HeepayNotifyResponse struct {
|
||||
RetCode int `json:"ret_code"` // 返回结果代码 0=接收成功
|
||||
RetMsg string `json:"ret_msg"` // 返回码信息提示
|
||||
AgentID int `json:"agent_id"` // 商户编号
|
||||
BillID string `json:"bill_id"` // 商户系统内部的订单号
|
||||
JnetBillNo string `json:"jnet_bill_no"` // 汇付宝交易号
|
||||
BillStatus string `json:"bill_status"` // 单据状态
|
||||
CardRealAmt float64 `json:"card_real_amt"` // 收到的卡的实际面值金额
|
||||
CardSettleAmt float64 `json:"card_settle_amt"` // 卡的结算金额
|
||||
CardDetailData string `json:"card_detail_data"` // 卡明细信息
|
||||
ExtParam string `json:"ext_param"` // 商户自定义参数或扩展参数
|
||||
UcardKind int `json:"ucard_kind"` // 卡种类型
|
||||
Sign string `json:"sign"` // 签名结果
|
||||
}
|
||||
|
||||
func (c *HeepayImpl) PayNotify() {
|
||||
ctx := c.Ctx.Request.Context()
|
||||
|
||||
// 从GET参数中获取数据
|
||||
retCode, err := c.GetInt("ret_code")
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("获取ret_code失败", zap.Error(err))
|
||||
c.Ctx.WriteString("FAIL")
|
||||
return
|
||||
}
|
||||
|
||||
agentID, err := c.GetInt("agent_id")
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("获取agent_id失败", zap.Error(err))
|
||||
c.Ctx.WriteString("FAIL")
|
||||
return
|
||||
}
|
||||
|
||||
cardRealAmt, err := c.GetFloat("card_real_amt")
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("获取card_real_amt失败", zap.Error(err))
|
||||
c.Ctx.WriteString("FAIL")
|
||||
return
|
||||
}
|
||||
|
||||
cardSettleAmt, err := c.GetFloat("card_settle_amt")
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("获取card_settle_amt失败", zap.Error(err))
|
||||
c.Ctx.WriteString("FAIL")
|
||||
return
|
||||
}
|
||||
|
||||
ucardKind, err := c.GetInt("ucard_kind")
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("获取ucard_kind失败", zap.Error(err))
|
||||
c.Ctx.WriteString("FAIL")
|
||||
return
|
||||
}
|
||||
|
||||
notifyData := HeepayNotifyResponse{
|
||||
RetCode: retCode,
|
||||
RetMsg: c.GetString("ret_msg"),
|
||||
AgentID: agentID,
|
||||
BillID: c.GetString("bill_id"),
|
||||
JnetBillNo: c.GetString("jnet_bill_no"),
|
||||
BillStatus: c.GetString("bill_status"),
|
||||
CardRealAmt: cardRealAmt,
|
||||
CardSettleAmt: cardSettleAmt,
|
||||
CardDetailData: c.GetString("card_detail_data"),
|
||||
ExtParam: c.GetString("ext_param"),
|
||||
UcardKind: ucardKind,
|
||||
Sign: c.GetString("sign"),
|
||||
}
|
||||
|
||||
// 获取订单信息
|
||||
orderInfo := order.GetOrderByBankOrderId(ctx, notifyData.BillID)
|
||||
if orderInfo.BankOrderId == "" {
|
||||
otelTrace.Logger.WithContext(ctx).Error("【Heepay】回调的订单号不存在,订单号=", zap.String("bill_id", notifyData.BillID))
|
||||
c.Ctx.WriteString("FAIL")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取通道信息
|
||||
roadInfo := road.GetRoadInfoByRoadUid(ctx, orderInfo.RoadUid)
|
||||
if roadInfo.RoadUid == "" {
|
||||
otelTrace.Logger.WithContext(ctx).Error("【Heepay】支付通道已经关闭或者删除,不进行回调")
|
||||
c.Ctx.WriteString("FAIL")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
signString := fmt.Sprintf("ret_code=%d&ret_msg=%s&agent_id=%d&bill_id=%s&jnet_bill_no=%s&bill_status=%s&card_real_amt=%.2f&card_settle_amt=%.2f&card_detail_data=%s&ext_param=%s&ucard_kind=%d|||%s",
|
||||
notifyData.RetCode, notifyData.RetMsg, notifyData.AgentID, notifyData.BillID, notifyData.JnetBillNo,
|
||||
notifyData.BillStatus, notifyData.CardRealAmt, notifyData.CardSettleAmt, notifyData.CardDetailData,
|
||||
notifyData.ExtParam, notifyData.UcardKind, gojson.Json(roadInfo.Params).Get("secret").Tostring())
|
||||
|
||||
calculatedSign := cryptor.Md5String(signString)
|
||||
|
||||
if calculatedSign != notifyData.Sign {
|
||||
otelTrace.Logger.WithContext(ctx).Error("【Heepay】签名验证失败")
|
||||
c.Ctx.WriteString("FAIL")
|
||||
return
|
||||
}
|
||||
|
||||
// 处理订单状态
|
||||
if notifyData.RetCode == 0 {
|
||||
isOk := false
|
||||
if notifyData.CardRealAmt == orderInfo.OrderAmount {
|
||||
// 订单支付成功
|
||||
isOk = service.SolvePaySuccess(ctx, orderInfo.BankOrderId, orderInfo.FactAmount, notifyData.JnetBillNo, "支付成功")
|
||||
} else {
|
||||
isOk = service.SolvePayFail(ctx, orderInfo.BankOrderId, notifyData.RetMsg, fmt.Sprintf("金额异议 卡面金额:%.2f 实际金额:%.2f:%s", orderInfo.OrderAmount, float64(notifyData.CardRealAmt), convertor.ToString(notifyData)))
|
||||
}
|
||||
if isOk {
|
||||
c.Ctx.WriteString("SUCCESS")
|
||||
} else {
|
||||
c.Ctx.WriteString("FAIL")
|
||||
}
|
||||
}
|
||||
|
||||
// 订单支付失败
|
||||
isOk := service.SolvePayFail(ctx, orderInfo.BankOrderId, notifyData.RetMsg, "")
|
||||
if isOk {
|
||||
c.Ctx.WriteString("SUCCESS")
|
||||
} else {
|
||||
c.Ctx.WriteString("FAIL")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HeepayImpl) PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool {
|
||||
params := map[string]any{}
|
||||
ctx := context.Background()
|
||||
cardData, err := sonic.GetFromString(orderInfo.CardReturnData)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
orderId, err := cardData.Get("order_id").String()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
params["order_id"] = orderId
|
||||
params["app_key"] = gojson.Json(roadInfo.Params).Get("appKey").Tostring()
|
||||
params["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
|
||||
params["sign"] = utils.GetMD5SignMF(params, gojson.Json(roadInfo.Params).Get("appSecret").Tostring())
|
||||
|
||||
cfg := config.Config{}
|
||||
url, err := cfg.GetMFCardQueryUrl()
|
||||
|
||||
req := httplib.Post(url)
|
||||
marshal, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("Map转化为byte数组失败,异常。", zap.Error(err))
|
||||
// fmt.Printf("Map转化为byte数组失败,异常:%s\n",zap.Error(err))
|
||||
return false
|
||||
}
|
||||
|
||||
otelTrace.Logger.WithContext(ctx).Info("请求参数:" + string(marshal))
|
||||
req.Header("Content-Type", "application/json")
|
||||
req.Body(marshal)
|
||||
req.Header("Accept-Charset", "utf-8")
|
||||
|
||||
response, err := req.String()
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("MF GetToken 请求失败:", zap.Error(err))
|
||||
return false
|
||||
}
|
||||
|
||||
otelTrace.Logger.WithContext(ctx).Info("远端请求返回数据:" + response)
|
||||
|
||||
resData, err := sonic.GetFromString(response)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
resStatus, err := resData.Get("data").Get("status").Int64()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
resCode, err := resData.Get("code").Int64()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if resCode == 0 && resStatus == 9 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *HeepayImpl) PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel {
|
||||
params := map[string]any{}
|
||||
ctx := context.Background()
|
||||
cardData, err := sonic.GetFromString(orderInfo.CardReturnData)
|
||||
if err != nil {
|
||||
return supply_model.CardMsgErr
|
||||
}
|
||||
|
||||
orderId, err := cardData.Get("order_id").String()
|
||||
if err != nil {
|
||||
return supply_model.CardMsgErr
|
||||
}
|
||||
|
||||
params["order_id"] = orderId
|
||||
params["app_key"] = gojson.Json(roadInfo.Params).Get("appKey").Tostring()
|
||||
params["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
|
||||
params["sign"] = utils.GetMD5SignMF(params, gojson.Json(roadInfo.Params).Get("appSecret").Tostring())
|
||||
|
||||
cfg := config.Config{}
|
||||
url, err := cfg.GetMFCardQueryUrl()
|
||||
|
||||
req := httplib.Post(url)
|
||||
marshal, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("Map转化为byte数组失败,异常。", zap.Error(err))
|
||||
// fmt.Printf("Map转化为byte数组失败,异常:%s\n",zap.Error(err))
|
||||
return supply_model.DataErr
|
||||
}
|
||||
|
||||
otelTrace.Logger.WithContext(ctx).Info("请求参数:" + string(marshal))
|
||||
req.Header("Content-Type", "application/json")
|
||||
req.Body(marshal)
|
||||
req.Header("Accept-Charset", "utf-8")
|
||||
|
||||
response, err := req.String()
|
||||
if err != nil {
|
||||
return supply_model.RemoteDataErr
|
||||
}
|
||||
|
||||
otelTrace.Logger.WithContext(ctx).Info("远端请求返回数据:" + response)
|
||||
|
||||
resData, err := sonic.GetFromString(response)
|
||||
if err != nil {
|
||||
return supply_model.RemoteDataErr
|
||||
}
|
||||
|
||||
resStatus, err := resData.Get("data").Get("status").Int64()
|
||||
if err != nil {
|
||||
return supply_model.RemoteDataErr
|
||||
}
|
||||
|
||||
resCode, err := resData.Get("code").Int64()
|
||||
if err != nil {
|
||||
return supply_model.RemoteDataErr
|
||||
}
|
||||
|
||||
if resCode == 0 {
|
||||
switch resStatus {
|
||||
case 9:
|
||||
return supply_model.RemoteSuccess
|
||||
case 2, 3, 4:
|
||||
return supply_model.RemoteDataDealing
|
||||
case 7:
|
||||
return supply_model.RemoteDataHandErr
|
||||
case 8:
|
||||
return supply_model.RemoteDataHealingErr
|
||||
}
|
||||
}
|
||||
return supply_model.RemoteDataErr
|
||||
}
|
||||
|
||||
func (c *HeepayImpl) PayFor(info payfor.PayforInfo) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *HeepayImpl) PayForQuery(payFor payfor.PayforInfo) (string, string) {
|
||||
cfg := config.Config{}
|
||||
url, err := cfg.GetMFCardQueryUrl()
|
||||
ctx := context.Background()
|
||||
if err != nil {
|
||||
return config.PAYFOR_FAIL, ""
|
||||
}
|
||||
|
||||
params := map[string]string{}
|
||||
params["order_id"] = payFor.BankOrderId
|
||||
params["app_key"] = gojson.Json("").Get("appKey").Tostring()
|
||||
|
||||
req := httplib.Post(url)
|
||||
marshal, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("Map转化为byte数组失败,异常。", zap.Error(err))
|
||||
// fmt.Printf("Map转化为byte数组失败,异常:%s\n",zap.Error(err))
|
||||
return config.PAYFOR_FAIL, "内部错误请稍后再试试"
|
||||
}
|
||||
otelTrace.Logger.WithContext(ctx).Info("请求参数:" + string(marshal))
|
||||
req.Header("Content-Type", "application/json")
|
||||
req.Body(marshal)
|
||||
req.Header("Accept-Charset", "utf-8")
|
||||
|
||||
response, err := req.String()
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("MF GetToken 请求失败:", zap.Error(err))
|
||||
return config.PAYFOR_FAIL, ""
|
||||
}
|
||||
|
||||
otelTrace.Logger.WithContext(ctx).Info("远端请求返回数据:" + response)
|
||||
|
||||
if gojson.Json(response).Get("code").Tostring() == "" {
|
||||
otelTrace.Logger.WithContext(ctx).Error("远程调用失败")
|
||||
return config.PAYFOR_BANKING, ""
|
||||
}
|
||||
|
||||
if gojson.Json(response).Get("code").Tostring() == "0" {
|
||||
|
||||
type data struct {
|
||||
OrderID int64 `json:"order_id"`
|
||||
CardNo string `json:"card_no"`
|
||||
CardPwd string `json:"card_pwd"`
|
||||
Status int `json:"status"`
|
||||
RspInfo string `json:"rsp_info"`
|
||||
FaceVal int `json:"face_val"`
|
||||
Amount int `json:"amount"`
|
||||
Discount string `json:"discount"`
|
||||
}
|
||||
|
||||
var d data
|
||||
|
||||
err2 := json.Unmarshal([]byte(gojson.Json(response).Get("data").Tostring()), &d)
|
||||
if err2 != nil {
|
||||
return config.PAYFOR_FAIL, ""
|
||||
}
|
||||
|
||||
if d.Status == 9 {
|
||||
return config.PAYFOR_SUCCESS, ""
|
||||
}
|
||||
|
||||
if d.Status == 4 {
|
||||
return config.PAYFOR_BANKING, ""
|
||||
}
|
||||
|
||||
if d.Status == 7 || d.Status == 8 {
|
||||
return config.PAYFOR_FAIL, ""
|
||||
}
|
||||
}
|
||||
|
||||
otelTrace.Logger.WithContext(ctx).Error("远程调用失败")
|
||||
return config.PAYFOR_BANKING, ""
|
||||
}
|
||||
|
||||
func (c *HeepayImpl) BalanceQuery(roadInfo road.RoadInfo) float64 {
|
||||
return 0.00
|
||||
}
|
||||
|
||||
func (c *HeepayImpl) PayForNotify() string {
|
||||
return ""
|
||||
}
|
||||
130
internal/service/supplier/third_party/heepay_test.go
vendored
Normal file
130
internal/service/supplier/third_party/heepay_test.go
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
package third_party
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gateway/internal/models/order"
|
||||
"gateway/internal/service/supplier"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHeepayImpl_SendCard(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
jsonStr string
|
||||
cardInfo supplier.RedeemCardInfo
|
||||
attach string
|
||||
merchantId string
|
||||
orderInfo order.OrderInfo
|
||||
want bool
|
||||
wantMsg string
|
||||
}{
|
||||
{
|
||||
name: "正常充值",
|
||||
jsonStr: `{
|
||||
"agent_id": "2340960",
|
||||
"secret": "2C3566FF9E354DBE99BF0C63",
|
||||
"3ds_key": "E6340D5A314B42E0B5DC307A",
|
||||
"goods_detail": "{\"name\":\"测试商品\"}"
|
||||
}`,
|
||||
cardInfo: supplier.RedeemCardInfo{
|
||||
CardNo: "1234567890123456",
|
||||
Data: "123456",
|
||||
FaceType: "100.00",
|
||||
},
|
||||
attach: uuid.New().String(),
|
||||
merchantId: uuid.New().String(),
|
||||
orderInfo: order.OrderInfo{
|
||||
BankOrderId: uuid.New().String(),
|
||||
MerchantOrderId: uuid.New().String(),
|
||||
OrderAmount: 100.00,
|
||||
FactAmount: 100.00,
|
||||
},
|
||||
want: true,
|
||||
wantMsg: "等待兑换",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &HeepayImpl{}
|
||||
ctx := context.Background()
|
||||
got, gotMsg := c.SendCard(ctx, tt.jsonStr, tt.cardInfo, tt.attach, tt.merchantId, tt.orderInfo)
|
||||
assert.Equal(t, tt.want, got)
|
||||
assert.Equal(t, tt.wantMsg, gotMsg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateSignHeepay(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
agentID string
|
||||
billID string
|
||||
billTime string
|
||||
cardType string
|
||||
cardData string
|
||||
payAmt string
|
||||
notifyURL string
|
||||
timeStamp string
|
||||
key string
|
||||
wantLength int
|
||||
}{
|
||||
{
|
||||
name: "正常签名",
|
||||
agentID: "1234567",
|
||||
billID: "test_order_123",
|
||||
billTime: time.Now().Format("20060102150405"),
|
||||
cardType: "10",
|
||||
cardData: "test_card_data",
|
||||
payAmt: "100.00",
|
||||
notifyURL: "http://test.com/notify",
|
||||
timeStamp: time.Now().Format("20060102150405"),
|
||||
key: "test_key",
|
||||
wantLength: 32, // MD5 签名长度
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := generateSignHeepay(tt.agentID, tt.billID, tt.billTime, tt.cardType, tt.cardData, tt.payAmt, tt.notifyURL, tt.timeStamp, tt.key)
|
||||
assert.Equal(t, tt.wantLength, len(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTripleDesEncrypt(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data string
|
||||
key string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "正常加密",
|
||||
data: "test_data",
|
||||
key: "test_key_1234567890123456",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "密钥长度不足",
|
||||
data: "test_data",
|
||||
key: "short_key",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := TripleDesEncrypt(tt.data, tt.key)
|
||||
if tt.wantErr {
|
||||
assert.Empty(t, got)
|
||||
} else {
|
||||
assert.NotEmpty(t, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
12
internal/service/supplier/third_party/ninja.go
vendored
12
internal/service/supplier/third_party/ninja.go
vendored
@@ -48,15 +48,15 @@ func (c *NinjaCardImpl) HasDependencyHTML() bool {
|
||||
func generateNinjaSign(ctx context.Context, params map[string]interface{}, key string) string {
|
||||
keys := maputil.Keys(params)
|
||||
sort.Strings(keys)
|
||||
sign_ := ""
|
||||
sign := ""
|
||||
for _, key2 := range keys {
|
||||
sign_ += key2 + "=" + convertor.ToString(params[key2]) + "&"
|
||||
sign += key2 + "=" + convertor.ToString(params[key2]) + "&"
|
||||
}
|
||||
sign_ += "secret=" + key
|
||||
otelTrace.Logger.WithContext(ctx).Info("构造sign参数", zap.Any("sign", sign_))
|
||||
return utils.EncodeMd5Str(sign_)
|
||||
sign += "secret=" + key
|
||||
return utils.EncodeMd5Str(sign)
|
||||
}
|
||||
|
||||
// SendCard 发送卡密
|
||||
func (c *NinjaCardImpl) SendCard(ctx context.Context, jsonStr string, orderInfo order.OrderInfo, cardInfo supplier.RedeemCardInfo, attach string) (bool, string) {
|
||||
cfg := config.Config{}
|
||||
|
||||
@@ -103,12 +103,12 @@ func (c *NinjaCardImpl) SendCard(ctx context.Context, jsonStr string, orderInfo
|
||||
}
|
||||
|
||||
var resData AutoGenerated
|
||||
otelTrace.Logger.WithContext(ctx).Info("远端请求返回数据:", zap.Any("response", response), zap.Any("解析结果", resData))
|
||||
err = sonic.Unmarshal([]byte(response), &resData)
|
||||
if err != nil {
|
||||
otelTrace.Logger.WithContext(ctx).Error("json解析失败", zap.Error(err), zap.Any("response", response))
|
||||
return false, "内部数据处理失败"
|
||||
}
|
||||
otelTrace.Logger.WithContext(ctx).Info("远端请求返回数据:", zap.Any("response", response), zap.Any("解析结果", resData))
|
||||
|
||||
if resData.Code == 200 {
|
||||
return true, response
|
||||
|
||||
Reference in New Issue
Block a user