client_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. // Copyright 2021 Tencent Inc. All rights reserved.
  2. package core_test
  3. import (
  4. "bytes"
  5. "context"
  6. "crypto"
  7. "crypto/rsa"
  8. "crypto/sha256"
  9. "crypto/x509"
  10. "encoding/base64"
  11. "encoding/json"
  12. "fmt"
  13. "io"
  14. "io/ioutil"
  15. "math/rand"
  16. "mime/multipart"
  17. "net/http"
  18. "net/http/httptest"
  19. "net/url"
  20. "strconv"
  21. "strings"
  22. "testing"
  23. "time"
  24. "git.nanodreamtech.com/sg/wechatpay-go/core"
  25. "git.nanodreamtech.com/sg/wechatpay-go/core/auth"
  26. "git.nanodreamtech.com/sg/wechatpay-go/core/auth/signers"
  27. "git.nanodreamtech.com/sg/wechatpay-go/core/auth/verifiers"
  28. "git.nanodreamtech.com/sg/wechatpay-go/core/option"
  29. "git.nanodreamtech.com/sg/wechatpay-go/utils"
  30. "github.com/stretchr/testify/assert"
  31. "github.com/stretchr/testify/require"
  32. )
  33. const (
  34. testMchID = "example-mchid"
  35. testCertificateSerialNumber = "example-sn"
  36. // NOTE: 以下是随机生成的测试密钥,请勿用于生产环境
  37. testPrivateKey = "-----BEGIN TESTING KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCkxOav8p5RFFmN\n7hjLGrNtXPYgCd0Zuvxabv+IWl1HVkWi/1iVqac+XwKH/ZeCYDURqZ6P0iiq8NBd\npygoeJiQM+qzaTV3alNXdLcpcaQSNqJ16a7Z2Co5LgOkBJHIF1qUWf+BpAtyLqo5\niGUQ47w3IWQHtfpW7RrECiNsI7kGKuSc4U2JX/gxrFG7ugpRA9Gp1eF0/wBMWSKV\nmKveKERvAueaTKEujpN4lctoO1wsMW93nuFNH1gtHPYmkaZaJS88GEp0VYJIcOpX\n2HlVPPiWx+0KAndcqMLVQ+qTk21tivpjuqPDstxcT9cXn/3CzSEkYrKkMLpjpSl/\nIn+amHddAgMBAAECggEADtQRlsAU82MLdDR7UrwCbdMx60w387raPyFCKflH78WZ\n2sN0K3PrMzfFuItf+UHDROWo+XSGaGvntKX4fTvtLv0dICxVvXt6KKK+YSJzC5iT\nIl13eO91TVQQy9AFdqZzZmp7DiW/SfVdKHRX9B8qryN4JyF/eBc6k23+JhtI6X8J\nrPeXGcw/Muo2cvoZ6oarRvzKU7pDivZADavAsgGwi+QwZUtrpUhDbJxWxoasCrWz\n6X24D+JbKndf/AeyHC2mqoAwTYdQgkkPHLJWGsqAt/GHtHLmZHIPc0dXfksP5omX\nOIii34YNud1j2/X0ryxoRBKPGIolV5Tyyh9PX75UAQKBgQDa/oPFUNsBo+NkfE4r\nMry+mnAeBM06vZh65acbHu3prqzn7tzfQR5rF9nIEUjNkach+ZeTnUxKfX4TwfiL\nibbM1Epb+yhEpSlA9HhY1AGhdNKpFbq63oVzS+lwpmLcXFLHhOYw8GrnYH9lcE+E\nYCqFcuk4t/y3rU/8Y5GhZKZ53QKBgQDAnKk6CEgnSkXfZLmzVtoM/5hnI0GEDOUo\nV35alqvgdJtCiPs4C03snYLVHqHjAknGzLGONAQ6h9au2qwcHy1qH/noq151u92b\nbCrKmghnm2SoIgCaZ7i2scWm6NM9Da60H662WxjaKcZMnUClm+G+Irl9m5cm1i3V\n56ZU63nbgQKBgHyAlFO6mzg8f4via+J9TvciADngyvjpT2YXaECv/dyL9TtK/oFi\nmTOTdLocsYJFm3piVv2SQQxcejArZ+2U1rtuufO/P25/Y4vNMRp3NZIgQ5/jfay9\n06rv7oCf57aWOm26LdCG7pAquWLnTh3ZOnNyGAup9mBKhR3dUa8q9MZ1AoGATZGJ\n0VYugKw3sXymEKRkkiGJJdgb9WsgCnwZ5a+SLoWnVUdHLM3YpvbUDrIUbhCo14ft\n5Z/rKAs2mRp1f6nKp1eTVHFXTEDJQWNxZEBeLCN3iQKQjZ5B1EmJmOtgztCoz9+G\ng+fx/UIfmxElTMyXP/RKEVzMpZZRxThSUxa174ECgYEAsviAmgBskM2ibtrA8tNW\njHdgut0xAtIJIIHlYmtksWgAWD4cPtCg7HPurXqBKxMogH/ZsZc6/5PpOIRYBNl0\nEebH0MZ/yiEOrmgsFJ1gKWk3fx8/yLQBlhn32AIhj7wmcFwzi/4hcwihHRCjS7t5\nQhVpKswxQyxqeIdQw0CgKZY=\n-----END TESTING KEY-----"
  38. testWechatPrivateKeyStr = "-----BEGIN TESTING KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDzbMaiMbCzJ11sZ2r7/XisolGu1pVpWvnv1PVOAWZZAWr/WcNNuLni8ddJkIs8NdTz+iAHtb9XMNG4hj7d10cy8QE6QG8YUef1fGb47Wee3DjJRk8N9lWyPDAy9AW70yYWItl/05XgkGt2eJuoU5CcZ+Cy0u2nAXxEGs2Z4Fg/100Ylcyq4GrimjngIyUFLnowOcZltJUQSw/Iu63V0BEh9PMNnXhKkwS2xfkA2nZRzSgzpMvX74v8F7Zf+HkyrHHzwY/7YUWg3pSj3GD0xKsJOwzz0MLlS8uIdLj1lKGzzt1ROgwe0sM5LL5XMfmbjDhcVBmyxQI80WiNaC281tVhAgMBAAECggEBAJ134Wrs0Ayky2ej4u5OAvFSM5rxj0fPJV3DGkiy2R18sFWtII03kXBA1+7rxVZW0IJfbLbwGG3z08cVeLeTWqiWhR/ErNlDqtT/+7DOCrkWZtm1VNCIaNla3Ccp+keNiNbLBn4NRqg1ZH8H+FHEdQjonc+waTIe4N9Bo30GRrBMeMbAgN8mwQhZ+6R23j3GsrJOViFpPRgGhih4aEAORxU+DWl22vklxO8lnDzSwfnXvNDvapnPaA8VXekkThxABq3p/ggv5MI1QPYl8BU5PWd6AJlPs/u2nzFGmCgVufHMjdszWYd3hbj5EmhlL1VMs4uxhCC8OM+ypbnx0CmBB5ECgYEA+W3KJciw4qG6XqWgjK9hkgiZrO8z3tu6tQ6ge28f3BxYEbGUab8bKKzacbYODRiRCM5oKcrXfTqP6IbqUoESiuoz0CUPbShp7k00wXKd7BH8LoIECDbctz77NG0KBE31OGEJw4hm762M14V9nU0KDHGuud1CH1fqivTGG0g2HiUCgYEA+dZ+e5iSvin+omXkr3Vhwf3kutX+GNkKm5LrWZNmWybOKw/K1YFvESpfl1b6YgjA/qUXj0tbOumFQw6e/OLfxIvK/dtkd+7pbSC4T9w8rH7zgjYdJ030Nyv3UGfHDbES+z9MDMo5h+3RKsLN2bp1JcXp6vht9CiXDYm1df3O340CgYB118Qg08+WU1iM7O2MajPL3dpVFPJJwUBV2GJDzv2bbZzCR0baKxr2vau6+4tp7ohfQ718uUPT+34QGuXMMwUCsqHmHgxKw0RA/SMGnlM0PE8L3gtvohPnU481dqq72+UWTOpjAie35yPak0wErGgp9u/ZCkr6Kfw6yGhsbVJ8LQKBgEBLxS1FrK4n3JIqqtnE2a21C4JRxBzc7m/vNYZN+s+GgxRt8gNUViMSxpsKFVHZcuGV1yRXflkA8/y37I6kTHYmi80dAxQidgxRmV1kDnFOEpj2GDafRzRTqkgVDRMm+P2T4pyABqJGv8fDbnqUE8Xu0y5XVOS69XTUddCxyuWZAoGAbpF6JOh6B7OV4XRTDm98Z8OPYmYd9JQ4xt8bqsG9LdzvhU/PI4zwIaKDqZ8vzCI+r8TOrC6SBEfjAe6o2FEExmFWTjBAVCp+Qvnz+Pj7d+WP3kCX/B62IZckVhdV3a2frMTBPvAh8XdENdvsu4DsWJBCA54GLU3wdUa/FO0RUsA=\n-----END TESTING KEY-----\n"
  39. testWechatCertificateStr = "-----BEGIN CERTIFICATE-----\nMIID1TCCAr2gAwIBAgIRAJ8qZJYAQUwUheimQ8sQNZMwDQYJKoZIhvcNAQELBQAw\nXjELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBU15U1NMMSswKQYDVQQLEyJNeVNTTCBU\nZXN0IFJTQSAtIEZvciB0ZXN0IHVzZSBvbmx5MRIwEAYDVQQDEwlNeVNTTC5jb20w\nHhcNMjEwNjI3MTMwNTM2WhcNMjIwNjI3MTMwNTM2WjAkMQswCQYDVQQGEwJDTjEV\nMBMGA1UEAxMMd2VjaGF0cGF5LWdvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEA82zGojGwsyddbGdq+/14rKJRrtaVaVr579T1TgFmWQFq/1nDTbi54vHX\nSZCLPDXU8/ogB7W/VzDRuIY+3ddHMvEBOkBvGFHn9Xxm+O1nntw4yUZPDfZVsjww\nMvQFu9MmFiLZf9OV4JBrdnibqFOQnGfgstLtpwF8RBrNmeBYP9dNGJXMquBq4po5\n4CMlBS56MDnGZbSVEEsPyLut1dARIfTzDZ14SpMEtsX5ANp2Uc0oM6TL1++L/Be2\nX/h5Mqxx88GP+2FFoN6Uo9xg9MSrCTsM89DC5UvLiHS49ZShs87dUToMHtLDOSy+\nVzH5m4w4XFQZssUCPNFojWgtvNbVYQIDAQABo4HHMIHEMA4GA1UdDwEB/wQEAwIH\ngDATBgNVHSUEDDAKBggrBgEFBQcDAzAfBgNVHSMEGDAWgBQogSYF0TQaP8FzD7uT\nzxUcPwO/fzBjBggrBgEFBQcBAQRXMFUwIQYIKwYBBQUHMAGGFWh0dHA6Ly9vY3Nw\nLm15c3NsLmNvbTAwBggrBgEFBQcwAoYkaHR0cDovL2NhLm15c3NsLmNvbS9teXNz\nbHRlc3Ryc2EuY3J0MBcGA1UdEQQQMA6CDHdlY2hhdHBheS1nbzANBgkqhkiG9w0B\nAQsFAAOCAQEAjh4oxMcJqsVaN5/aA+4+NSfV9wR4uzTVtAyL/dApymZn6Wjknd85\nDltekcTflNP84bDiFEE3Ls3RYatjRx9pWeW7QbpdYvfDtWuxL5dhzRYtUO83z8wT\n+/sceeyNOQAWGD6Gt7Aw7yb7bIZ5slcZYepqdKSHyMnn06CCNRZtDVTuQYRqnmoh\nCaK5RNe4lYM/hncMgddE/DugTxzh5NUMpAY4xAsqOofkVmptX3trVZVILPglJ6nQ\n5dALCpp2UCuxikwFdEpvvGIC2qZQv5jmemFLCDIQZ227GUZ/EcbuTtAdQYHUnGJT\nvrFBSDe4FbKwljUmrccD/LkR9FmPn6gWKA==\n-----END CERTIFICATE-----\n"
  40. fileName = "picture.jpeg"
  41. responseBody = `{"hello":"client"}`
  42. testRequestUri = "/v3/resource?first=this+is+a+field&second=was+it+clear+%28already%29%3F"
  43. )
  44. var (
  45. privateKey *rsa.PrivateKey
  46. wechatPayPrivateKey *rsa.PrivateKey
  47. wechatPayCertificate *x509.Certificate
  48. signer auth.Signer
  49. verifier auth.Verifier
  50. ctx context.Context
  51. )
  52. type signParameter map[string]string
  53. func init() {
  54. ctx = context.Background()
  55. var err error
  56. privateKey, err = utils.LoadPrivateKey(testingKey(testPrivateKey))
  57. if err != nil {
  58. panic(fmt.Errorf("load merchant testing key err:%s", err.Error()))
  59. }
  60. wechatPayCertificate, err = utils.LoadCertificate(testWechatCertificateStr)
  61. if err != nil {
  62. panic(fmt.Errorf("generate wechatpay testing certificate err:%s", err.Error()))
  63. }
  64. wechatPayPrivateKey, err = utils.LoadPrivateKey(testingKey(testWechatPrivateKeyStr))
  65. if err != nil {
  66. panic(fmt.Errorf("generate wechatpay testing key err:%s", err.Error()))
  67. }
  68. }
  69. func writeResponse(w http.ResponseWriter) {
  70. writeSignature(w, responseBody)
  71. w.WriteHeader(http.StatusOK)
  72. fmt.Fprint(w, responseBody)
  73. }
  74. func writeSignature(w http.ResponseWriter, body string) {
  75. w.Header().Set("Request-Id", "0")
  76. w.Header().Set("Wechatpay-Serial", utils.GetCertificateSerialNumber(*wechatPayCertificate))
  77. nonce := "this-is-a-nonce"
  78. w.Header().Set("Wechatpay-Nonce", nonce)
  79. timestamp := strconv.FormatInt(time.Now().Unix(), 10)
  80. w.Header().Set("Wechatpay-Timestamp", timestamp)
  81. signature, _ := utils.SignSHA256WithRSA(
  82. fmt.Sprintf("%s\n%s\n%s\n", timestamp, nonce, body), wechatPayPrivateKey)
  83. w.Header().Set("Wechatpay-Signature", signature)
  84. }
  85. func parseAuthorization(t *testing.T, authorization string) (schema string, param signParameter) {
  86. m := make(signParameter)
  87. s1 := strings.Split(authorization, " ")
  88. assert.Equal(t, len(s1), 2)
  89. s2 := strings.Split(s1[1], ",")
  90. assert.Equal(t, len(s2), 5)
  91. for _, v := range s2 {
  92. s3 := strings.SplitN(v, "=", 2)
  93. assert.Equal(t, len(s3), 2)
  94. pk := s3[0]
  95. pv := strings.Trim(s3[1], "\"")
  96. m[pk] = pv
  97. }
  98. return s1[0], m
  99. }
  100. func assertAuthorization(t *testing.T, schema, method, uri string, params signParameter, body []byte) {
  101. assert.Equal(t, schema, "WECHATPAY2-SHA256-RSA2048")
  102. assert.Equal(t, params["mchid"], testMchID)
  103. assert.Equal(t, params["serial_no"], testCertificateSerialNumber)
  104. message := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n",
  105. method,
  106. uri,
  107. params["timestamp"],
  108. params["nonce_str"],
  109. body)
  110. hashed := sha256.Sum256([]byte(message))
  111. signBytes, err := base64.StdEncoding.DecodeString(params["signature"])
  112. assert.NoError(t, err)
  113. assert.NoError(t, rsa.VerifyPKCS1v15(&privateKey.PublicKey, crypto.SHA256, hashed[:], signBytes))
  114. }
  115. func TestGet(t *testing.T) {
  116. opts := []core.ClientOption{
  117. option.WithMerchantCredential(testMchID, testCertificateSerialNumber, privateKey),
  118. option.WithWechatPayCertificate([]*x509.Certificate{wechatPayCertificate}),
  119. }
  120. client, err := core.NewClient(ctx, opts...)
  121. require.NoError(t, err)
  122. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  123. assert.Equal(t, r.Method, "GET")
  124. assert.Equal(t, r.RequestURI, testRequestUri)
  125. schema, params := parseAuthorization(t, r.Header.Get("Authorization"))
  126. assertAuthorization(t, schema, r.Method, r.RequestURI, params, make([]byte, 0))
  127. writeResponse(w)
  128. }))
  129. defer ts.Close()
  130. result, err := client.Get(ctx, ts.URL+testRequestUri)
  131. assert.NoError(t, err)
  132. body, err := ioutil.ReadAll(result.Response.Body)
  133. assert.NoError(t, err)
  134. assert.Equal(t, string(body), responseBody)
  135. }
  136. type testData struct {
  137. StockID string `json:"stock_id"`
  138. StockCreatorMchID string `json:"stock_creator_mchid"`
  139. OutRequestNo string `json:"out_request_no"`
  140. AppID string `json:"appid"`
  141. }
  142. func TestPost(t *testing.T) {
  143. opts := []core.ClientOption{
  144. option.WithMerchantCredential(testMchID, testCertificateSerialNumber, privateKey),
  145. option.WithWechatPayCertificate([]*x509.Certificate{wechatPayCertificate}),
  146. }
  147. client, err := core.NewClient(ctx, opts...)
  148. require.NoError(t, err)
  149. data := &testData{
  150. StockID: "xxx",
  151. StockCreatorMchID: "xxx",
  152. OutRequestNo: "xxx",
  153. AppID: "xxx",
  154. }
  155. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  156. assert.Equal(t, r.Method, "POST")
  157. assert.Equal(t, r.RequestURI, testRequestUri)
  158. schema, params := parseAuthorization(t, r.Header.Get("Authorization"))
  159. body, _ := ioutil.ReadAll(r.Body)
  160. assertAuthorization(t, schema, r.Method, r.RequestURI, params, body)
  161. writeResponse(w)
  162. }))
  163. defer ts.Close()
  164. result, err := client.Post(ctx, ts.URL+testRequestUri, data)
  165. assert.NoError(t, err)
  166. body, err := ioutil.ReadAll(result.Response.Body)
  167. assert.NoError(t, err)
  168. assert.Equal(t, string(body), responseBody)
  169. }
  170. func TestRequest(t *testing.T) {
  171. opts := []core.ClientOption{
  172. option.WithMerchantCredential(testMchID, testCertificateSerialNumber, privateKey),
  173. option.WithWechatPayCertificate([]*x509.Certificate{wechatPayCertificate}),
  174. }
  175. client, err := core.NewClient(ctx, opts...)
  176. require.NoError(t, err)
  177. data := &testData{
  178. StockID: "xxx",
  179. StockCreatorMchID: "xxx",
  180. OutRequestNo: "xxx",
  181. AppID: "xxx",
  182. }
  183. tt := []struct {
  184. method string
  185. uri string
  186. contentType string
  187. body interface{}
  188. header http.Header
  189. }{
  190. {
  191. http.MethodGet,
  192. "/v3/get",
  193. "",
  194. nil,
  195. http.Header{"My-Id": {"1234"}},
  196. },
  197. {
  198. http.MethodPost,
  199. testRequestUri,
  200. "application/json",
  201. data,
  202. http.Header{"My-Id": {"1234"}},
  203. },
  204. {
  205. http.MethodDelete,
  206. "/v3/delete",
  207. "",
  208. nil,
  209. nil,
  210. },
  211. {
  212. http.MethodPut,
  213. "/v3/put",
  214. "",
  215. data,
  216. http.Header{"My-Id": {"1234"}},
  217. },
  218. {
  219. http.MethodPatch,
  220. "/v3/patch",
  221. "",
  222. data,
  223. nil,
  224. },
  225. }
  226. for _, test := range tt {
  227. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  228. assert.Equal(t, test.method, r.Method)
  229. assert.Equal(t, test.uri, r.RequestURI)
  230. if test.header != nil {
  231. assert.Equal(t, "1234", r.Header.Get("My-Id"))
  232. }
  233. schema, params := parseAuthorization(t, r.Header.Get("Authorization"))
  234. body, _ := ioutil.ReadAll(r.Body)
  235. assertAuthorization(t, schema, r.Method, r.RequestURI, params, body)
  236. assert.Equal(t, "9F2A649600414C1485E8A643CB103593", r.Header.Get("Wechatpay-Serial"))
  237. if test.body != nil {
  238. assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
  239. var req testData
  240. err = json.Unmarshal(body, &req)
  241. assert.NoError(t, err)
  242. assert.Equal(t, data, &req)
  243. }
  244. writeResponse(w)
  245. }))
  246. testUrl, err := url.Parse(ts.URL + test.uri)
  247. assert.NoError(t, err)
  248. result, err := client.Request(
  249. ctx,
  250. test.method,
  251. ts.URL+testUrl.Path,
  252. test.header,
  253. testUrl.Query(),
  254. test.body,
  255. test.contentType,
  256. )
  257. assert.NoError(t, err)
  258. body, err := ioutil.ReadAll(result.Response.Body)
  259. assert.NoError(t, err)
  260. assert.Equal(t, http.StatusOK, result.Response.StatusCode)
  261. assert.Equal(t, responseBody, string(body))
  262. ts.Close()
  263. }
  264. }
  265. func TestClientVerifyFail(t *testing.T) {
  266. opts := []core.ClientOption{
  267. option.WithMerchantCredential(testMchID, testCertificateSerialNumber, privateKey),
  268. option.WithWechatPayCertificate([]*x509.Certificate{wechatPayCertificate}),
  269. }
  270. client, err := core.NewClient(ctx, opts...)
  271. require.NoError(t, err)
  272. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
  273. w.Header().Set("Request-Id", "0")
  274. w.Header().Set("Wechatpay-Serial", utils.GetCertificateSerialNumber(*wechatPayCertificate))
  275. nonce := "this-is-a-nonce"
  276. w.Header().Set("Wechatpay-Nonce", nonce)
  277. timestamp := strconv.FormatInt(time.Now().Unix(), 10)
  278. w.Header().Set("Wechatpay-Timestamp", timestamp)
  279. w.Header().Set("Wechatpay-Signature", "AABB")
  280. w.WriteHeader(http.StatusOK)
  281. }))
  282. defer ts.Close()
  283. _, err = client.Get(ctx, ts.URL+testRequestUri)
  284. assert.Contains(t, err.Error(), "verify fail")
  285. }
  286. func TestClientNoAuth(t *testing.T) {
  287. opts := []core.ClientOption{
  288. option.WithMerchantCredential(testMchID, testCertificateSerialNumber, privateKey),
  289. option.WithWechatPayCertificate([]*x509.Certificate{wechatPayCertificate}),
  290. }
  291. client, err := core.NewClient(ctx, opts...)
  292. require.NoError(t, err)
  293. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
  294. w.WriteHeader(401)
  295. w.Header().Set("Request-Id", "0")
  296. fmt.Fprint(w, `{"code":"SIGN_ERROR","message":"sign error"}`)
  297. }))
  298. defer ts.Close()
  299. _, err = client.Get(ctx, ts.URL+testRequestUri)
  300. apiError, ok := err.(*core.APIError)
  301. assert.True(t, ok)
  302. assert.Equal(t, 401, apiError.StatusCode)
  303. assert.Equal(t, "SIGN_ERROR", apiError.Code)
  304. assert.Equal(t, "sign error", apiError.Message)
  305. }
  306. type meta struct {
  307. FileName string `json:"filename" binding:"required"` // 商户上传的媒体图片的名称,商户自定义,必须以JPG、BMP、PNG为后缀。
  308. Sha256 string `json:"sha256" binding:"required"` // 图片文件的文件摘要,即对图片文件的二进制内容进行sha256计算得到的值。
  309. }
  310. func TestClient_Upload(t *testing.T) {
  311. // 如果你有自定义的Signer或者Verifier
  312. signer = &signers.SHA256WithRSASigner{
  313. MchID: testMchID,
  314. PrivateKey: privateKey,
  315. CertificateSerialNo: testCertificateSerialNumber,
  316. }
  317. verifier = verifiers.NewSHA256WithRSAVerifier(
  318. core.NewCertificateMap(
  319. map[string]*x509.Certificate{utils.GetCertificateSerialNumber(*wechatPayCertificate): wechatPayCertificate},
  320. ),
  321. )
  322. client, err := core.NewClient(ctx, option.WithSigner(signer), option.WithVerifier(verifier))
  323. require.NoError(t, err)
  324. pictureBytes := make([]byte, 1024)
  325. // 随机的数据充当图片数据
  326. rand.Read(pictureBytes)
  327. // 计算文件序列化后的sha256
  328. h := sha256.New()
  329. _, err = h.Write(pictureBytes)
  330. assert.NoError(t, err)
  331. metaObject := &meta{}
  332. pictureSha256 := h.Sum(nil)
  333. metaObject.FileName = fileName
  334. metaObject.Sha256 = fmt.Sprintf("%x", string(pictureSha256))
  335. metaByte, _ := json.Marshal(metaObject)
  336. reqBody := &bytes.Buffer{}
  337. writer := multipart.NewWriter(reqBody)
  338. err = core.CreateFormField(writer, "meta", "application/json", metaByte)
  339. assert.NoError(t, err)
  340. err = core.CreateFormFile(writer, fileName, "image/jpg", pictureBytes)
  341. assert.NoError(t, err)
  342. err = writer.Close()
  343. assert.NoError(t, err)
  344. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  345. assert.Equal(t, r.Method, "POST")
  346. assert.Equal(t, r.RequestURI, testRequestUri)
  347. mr, err := r.MultipartReader()
  348. assert.NoError(t, err)
  349. var body []byte
  350. for {
  351. p, err := mr.NextPart()
  352. if err == io.EOF {
  353. break
  354. }
  355. assert.NoError(t, err)
  356. if p.FormName() == "meta" {
  357. body, _ = ioutil.ReadAll(p)
  358. assert.Equal(t, metaByte, body)
  359. } else if p.FormName() == "file" {
  360. slurp, _ := ioutil.ReadAll(p)
  361. assert.Equal(t, pictureBytes, slurp)
  362. }
  363. }
  364. schema, params := parseAuthorization(t, r.Header.Get("Authorization"))
  365. assertAuthorization(t, schema, r.Method, r.RequestURI, params, body)
  366. writeResponse(w)
  367. }))
  368. defer ts.Close()
  369. result, err := client.Upload(
  370. ctx,
  371. ts.URL+testRequestUri,
  372. string(metaByte),
  373. reqBody.String(),
  374. writer.FormDataContentType())
  375. assert.NoError(t, err)
  376. if result.Response.Body != nil {
  377. defer result.Response.Body.Close()
  378. }
  379. body, err := ioutil.ReadAll(result.Response.Body)
  380. assert.NoError(t, err)
  381. t.Log(string(body))
  382. assert.Equal(t, string(body), responseBody)
  383. }
  384. func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }