蛋蛋星球-制度模式
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

201 rinda
5.5 KiB

  1. package lakala
  2. import (
  3. "context"
  4. "crypto/sha256"
  5. "encoding/hex"
  6. "fmt"
  7. "hash"
  8. "net/http"
  9. "strings"
  10. "sync"
  11. "time"
  12. "github.com/go-pay/gopay"
  13. "github.com/go-pay/gopay/pkg/xhttp"
  14. "github.com/go-pay/util"
  15. "github.com/go-pay/xlog"
  16. )
  17. // Client lakala
  18. type Client struct {
  19. ctx context.Context // 上下文
  20. PartnerCode string // partner_code:商户编码,由4~6位大写字母或数字构成
  21. credentialCode string // credential_code:系统为商户分配的开发校验码,请妥善保管,不要在公开场合泄露
  22. IsProd bool // 是否生产环境
  23. DebugSwitch gopay.DebugSwitch // 调试开关,是否打印日志
  24. logger xlog.XLogger
  25. hc *xhttp.Client
  26. sha256Hash hash.Hash
  27. mu sync.Mutex
  28. }
  29. // NewClient 初始化lakala户端
  30. // partnerCode: 商户编码,由4~6位大写字母或数字构成
  31. // credentialCode: 系统为商户分配的开发校验码,请妥善保管,不要在公开场合泄露
  32. // isProd: 是否生产环境
  33. func NewClient(partnerCode, credentialCode string, isProd bool) (client *Client, err error) {
  34. if partnerCode == gopay.NULL || credentialCode == gopay.NULL {
  35. return nil, gopay.MissLakalaInitParamErr
  36. }
  37. logger := xlog.NewLogger()
  38. logger.SetLevel(xlog.DebugLevel)
  39. client = &Client{
  40. ctx: context.Background(),
  41. PartnerCode: partnerCode,
  42. credentialCode: credentialCode,
  43. IsProd: isProd,
  44. DebugSwitch: gopay.DebugOff,
  45. logger: logger,
  46. hc: xhttp.NewClient(),
  47. sha256Hash: sha256.New(),
  48. }
  49. return client, nil
  50. }
  51. // SetBodySize 设置http response body size(MB)
  52. func (c *Client) SetBodySize(sizeMB int) {
  53. if sizeMB > 0 {
  54. c.hc.SetBodySize(sizeMB)
  55. }
  56. }
  57. // SetHttpClient 设置自定义的xhttp.Client
  58. func (c *Client) SetHttpClient(client *xhttp.Client) {
  59. if client != nil {
  60. c.hc = client
  61. }
  62. }
  63. func (c *Client) SetLogger(logger xlog.XLogger) {
  64. if logger != nil {
  65. c.logger = logger
  66. }
  67. }
  68. // 公共参数处理 Query Params
  69. func (c *Client) pubParamsHandle() (param string, err error) {
  70. bm := make(gopay.BodyMap)
  71. bm.Set("time", time.Now().UnixMilli())
  72. bm.Set("nonce_str", util.RandomString(20))
  73. sign, err := c.getRsaSign(bm)
  74. if err != nil {
  75. return "", fmt.Errorf("GetRsaSign Error: %w", err)
  76. }
  77. bm.Set("sign", sign)
  78. param = bm.EncodeURLParams()
  79. return
  80. }
  81. // 验证签名
  82. func VerifySign(notifyReq *NotifyRequest, partnerCode string, credentialCode string) (err error) {
  83. validStr := fmt.Sprintf("%v&%v&%v&%v", partnerCode, notifyReq.Time, notifyReq.NonceStr, credentialCode)
  84. h := sha256.New()
  85. h.Write([]byte(validStr))
  86. validSign := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
  87. if notifyReq.Sign != validSign {
  88. return fmt.Errorf("签名验证失败")
  89. }
  90. return
  91. }
  92. // getRsaSign 获取签名字符串
  93. func (c *Client) getRsaSign(bm gopay.BodyMap) (sign string, err error) {
  94. var (
  95. partnerCode = c.PartnerCode
  96. ts = bm.Get("time")
  97. nonceStr = bm.Get("nonce_str")
  98. credentialCode = c.credentialCode
  99. )
  100. if ts == "" || nonceStr == "" {
  101. return "", fmt.Errorf("签名缺少必要的参数")
  102. }
  103. validStr := fmt.Sprintf("%v&%v&%v&%v", partnerCode, ts, nonceStr, credentialCode)
  104. c.mu.Lock()
  105. defer func() {
  106. c.sha256Hash.Reset()
  107. c.mu.Unlock()
  108. }()
  109. c.sha256Hash.Write([]byte(validStr))
  110. sign = strings.ToLower(hex.EncodeToString(c.sha256Hash.Sum(nil)))
  111. return
  112. }
  113. // PUT 发起请求
  114. func (c *Client) doPut(ctx context.Context, path string, bm gopay.BodyMap) (bs []byte, err error) {
  115. var url = baseUrlProd + path
  116. param, err := c.pubParamsHandle()
  117. if err != nil {
  118. return nil, err
  119. }
  120. req := c.hc.Req()
  121. req.Header.Add("Accept", "application/json")
  122. uri := url + "?" + param
  123. if c.DebugSwitch == gopay.DebugOn {
  124. c.logger.Debugf("Lakala_Url: %s", uri)
  125. c.logger.Debugf("Lakala_Req_Body: %s", bm.JsonBody())
  126. c.logger.Debugf("Lakala_Req_Headers: %#v", req.Header)
  127. }
  128. res, bs, err := req.Put(uri).SendBodyMap(bm).EndBytes(ctx)
  129. if err != nil {
  130. return nil, err
  131. }
  132. if res.StatusCode != http.StatusOK {
  133. return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
  134. }
  135. return bs, nil
  136. }
  137. // PUT 发起请求
  138. func (c *Client) doPost(ctx context.Context, path string, bm gopay.BodyMap) (bs []byte, err error) {
  139. var url = baseUrlProd + path
  140. param, err := c.pubParamsHandle()
  141. if err != nil {
  142. return nil, err
  143. }
  144. req := c.hc.Req()
  145. req.Header.Add("Accept", "application/json")
  146. uri := url + "?" + param
  147. if c.DebugSwitch == gopay.DebugOn {
  148. c.logger.Debugf("Lakala_Url: %s", uri)
  149. c.logger.Debugf("Lakala_Req_Body: %s", bm.JsonBody())
  150. c.logger.Debugf("Lakala_Req_Headers: %#v", req.Header)
  151. }
  152. res, bs, err := req.Post(uri).SendBodyMap(bm).EndBytes(ctx)
  153. if err != nil {
  154. return nil, err
  155. }
  156. if res.StatusCode != http.StatusOK {
  157. return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
  158. }
  159. return bs, nil
  160. }
  161. // GET 发起请求
  162. func (c *Client) doGet(ctx context.Context, path, queryParams string) (bs []byte, err error) {
  163. var url = baseUrlProd + path
  164. param, err := c.pubParamsHandle()
  165. if err != nil {
  166. return nil, err
  167. }
  168. if queryParams != "" {
  169. param = param + "&" + queryParams
  170. }
  171. req := c.hc.Req()
  172. req.Header.Add("Accept", "application/json")
  173. uri := url + "?" + param
  174. if c.DebugSwitch == gopay.DebugOn {
  175. c.logger.Debugf("Lakala_Url: %s", uri)
  176. c.logger.Debugf("Lakala_Req_Headers: %#v", req.Header)
  177. }
  178. res, bs, err := req.Get(uri).EndBytes(ctx)
  179. if err != nil {
  180. return nil, err
  181. }
  182. if res.StatusCode != http.StatusOK {
  183. return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
  184. }
  185. return bs, nil
  186. }