蛋蛋星球-制度模式
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 rivejä
7.7 KiB

  1. package alipay
  2. import (
  3. "context"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "crypto/rsa"
  7. "encoding/base64"
  8. "encoding/json"
  9. "errors"
  10. "fmt"
  11. "net/url"
  12. "reflect"
  13. "time"
  14. xaes "github.com/go-pay/crypto/aes"
  15. "github.com/go-pay/crypto/xpem"
  16. "github.com/go-pay/crypto/xrsa"
  17. "github.com/go-pay/gopay"
  18. "github.com/go-pay/gopay/pkg/xhttp"
  19. "github.com/go-pay/xtime"
  20. )
  21. // 格式化请求URL参数
  22. func FormatURLParam(body gopay.BodyMap) (urlParam string) {
  23. v := url.Values{}
  24. for key, value := range body {
  25. v.Add(key, value.(string))
  26. }
  27. return v.Encode()
  28. }
  29. // DecryptOpenDataToStruct 解密支付宝开放数据到 结构体
  30. // encryptedData:包括敏感数据在内的完整用户信息的加密数据
  31. // secretKey:AES密钥,支付宝管理平台配置
  32. // beanPtr:需要解析到的结构体指针
  33. // 文档:https://opendocs.alipay.com/common/02mse3
  34. func DecryptOpenDataToStruct(encryptedData, secretKey string, beanPtr any) (err error) {
  35. if encryptedData == gopay.NULL || secretKey == gopay.NULL {
  36. return errors.New("encryptedData or secretKey is null")
  37. }
  38. beanValue := reflect.ValueOf(beanPtr)
  39. if beanValue.Kind() != reflect.Ptr {
  40. return errors.New("传入参数类型必须是以指针形式")
  41. }
  42. if beanValue.Elem().Kind() != reflect.Struct {
  43. return errors.New("传入any必须是结构体")
  44. }
  45. var (
  46. block cipher.Block
  47. blockMode cipher.BlockMode
  48. originData []byte
  49. )
  50. aesKey, _ := base64.StdEncoding.DecodeString(secretKey)
  51. ivKey := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  52. secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
  53. if block, err = aes.NewCipher(aesKey); err != nil {
  54. return fmt.Errorf("aes.NewCipher:%w", err)
  55. }
  56. if len(secretData)%len(aesKey) != 0 {
  57. return errors.New("encryptedData is error")
  58. }
  59. blockMode = cipher.NewCBCDecrypter(block, ivKey)
  60. originData = make([]byte, len(secretData))
  61. blockMode.CryptBlocks(originData, secretData)
  62. if len(originData) > 0 {
  63. originData = xaes.PKCS5UnPadding(originData)
  64. }
  65. if err = json.Unmarshal(originData, beanPtr); err != nil {
  66. return fmt.Errorf("json.Unmarshal(%s):%w", string(originData), err)
  67. }
  68. return nil
  69. }
  70. // DecryptOpenDataToBodyMap 解密支付宝开放数据到 BodyMap
  71. // encryptedData:包括敏感数据在内的完整用户信息的加密数据
  72. // secretKey:AES密钥,支付宝管理平台配置
  73. // 文档:https://opendocs.alipay.com/common/02mse3
  74. func DecryptOpenDataToBodyMap(encryptedData, secretKey string) (bm gopay.BodyMap, err error) {
  75. if encryptedData == gopay.NULL || secretKey == gopay.NULL {
  76. return nil, errors.New("encryptedData or secretKey is null")
  77. }
  78. var (
  79. aesKey, originData []byte
  80. ivKey = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  81. block cipher.Block
  82. blockMode cipher.BlockMode
  83. )
  84. aesKey, _ = base64.StdEncoding.DecodeString(secretKey)
  85. secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
  86. if block, err = aes.NewCipher(aesKey); err != nil {
  87. return nil, fmt.Errorf("aes.NewCipher:%w", err)
  88. }
  89. if len(secretData)%len(aesKey) != 0 {
  90. return nil, errors.New("encryptedData is error")
  91. }
  92. blockMode = cipher.NewCBCDecrypter(block, ivKey)
  93. originData = make([]byte, len(secretData))
  94. blockMode.CryptBlocks(originData, secretData)
  95. if len(originData) > 0 {
  96. originData = xaes.PKCS5UnPadding(originData)
  97. }
  98. bm = make(gopay.BodyMap)
  99. if err = json.Unmarshal(originData, &bm); err != nil {
  100. return nil, fmt.Errorf("json.Unmarshal(%s):%w", string(originData), err)
  101. }
  102. return
  103. }
  104. // SystemOauthToken 换取授权访问令牌(默认使用utf-8,RSA2)
  105. // appId:应用ID
  106. // privateKey:应用私钥
  107. // grantType:值为 authorization_code 时,代表用code换取;值为 refresh_token 时,代表用refresh_token换取,传空默认code换取
  108. // codeOrToken:支付宝授权码或refresh_token
  109. // signType:签名方式 RSA 或 RSA2,默认 RSA2
  110. // appAuthToken:可选参数,三方授权令牌
  111. // 文档:https://opendocs.alipay.com/apis/api_9/alipay.system.oauth.token
  112. func SystemOauthToken(ctx context.Context, appId string, privateKey, grantType, codeOrToken, signType string, appAuthToken ...string) (rsp *SystemOauthTokenResponse, err error) {
  113. key := xrsa.FormatAlipayPrivateKey(privateKey)
  114. aat := ""
  115. if len(appAuthToken) > 0 {
  116. aat = appAuthToken[0]
  117. }
  118. priKey, err := xpem.DecodePrivateKey([]byte(key))
  119. if err != nil {
  120. return nil, err
  121. }
  122. var bs []byte
  123. bm := make(gopay.BodyMap)
  124. switch grantType {
  125. case "authorization_code":
  126. bm.Set("grant_type", "authorization_code")
  127. bm.Set("code", codeOrToken)
  128. case "refresh_token":
  129. bm.Set("grant_type", "refresh_token")
  130. bm.Set("refresh_token", codeOrToken)
  131. default:
  132. bm.Set("grant_type", "authorization_code")
  133. bm.Set("code", codeOrToken)
  134. }
  135. if bs, err = systemOauthToken(ctx, appId, priKey, bm, "alipay.system.oauth.token", true, signType, aat); err != nil {
  136. return
  137. }
  138. rsp = new(SystemOauthTokenResponse)
  139. if err = json.Unmarshal(bs, rsp); err != nil {
  140. return nil, fmt.Errorf("json.Unmarshal(%s):%w", string(bs), err)
  141. }
  142. if (rsp.Response == nil) || (rsp.Response != nil && rsp.Response.AccessToken == "") {
  143. return nil, errors.New("response is nil or access_token is NULL")
  144. }
  145. return
  146. }
  147. // systemOauthToken 向支付宝发送请求
  148. func systemOauthToken(ctx context.Context, appId string, privateKey *rsa.PrivateKey, bm gopay.BodyMap, method string, isProd bool, signType, appAuthToken string) (bs []byte, err error) {
  149. bm.Set("app_id", appId)
  150. bm.Set("method", method)
  151. bm.Set("format", "JSON")
  152. bm.Set("charset", "utf-8")
  153. if signType == gopay.NULL {
  154. bm.Set("sign_type", RSA2)
  155. } else {
  156. bm.Set("sign_type", signType)
  157. }
  158. bm.Set("timestamp", time.Now().Format(xtime.TimeLayout))
  159. bm.Set("version", "1.0")
  160. if appAuthToken != gopay.NULL {
  161. bm.Set("app_auth_token", appAuthToken)
  162. }
  163. var (
  164. sign string
  165. baseUrl = baseUrlUtf8
  166. )
  167. if sign, err = GetRsaSign(bm, bm.GetString("sign_type"), privateKey); err != nil {
  168. return nil, err
  169. }
  170. bm.Set("sign", sign)
  171. if !isProd {
  172. baseUrl = sandboxBaseUrlUtf8
  173. }
  174. _, bs, err = xhttp.NewClient().Req(xhttp.TypeFormData).Post(baseUrl).SendString(bm.EncodeURLParams()).EndBytes(ctx)
  175. if err != nil {
  176. return nil, err
  177. }
  178. return bs, nil
  179. }
  180. // monitor.heartbeat.syn(验签接口)
  181. // appId:应用ID
  182. // privateKey:应用私钥,支持PKCS1和PKCS8
  183. // signType:签名方式 alipay.RSA 或 alipay.RSA2,默认 RSA2
  184. // bizContent:验签时该参数不做任何处理,{任意值},此参数具体看文档
  185. // 文档:https://opendocs.alipay.com/apis/api_9/monitor.heartbeat.syn
  186. func MonitorHeartbeatSyn(ctx context.Context, appId string, privateKey, signType, bizContent string) (aliRsp *MonitorHeartbeatSynResponse, err error) {
  187. key := xrsa.FormatAlipayPrivateKey(privateKey)
  188. priKey, err := xpem.DecodePrivateKey([]byte(key))
  189. if err != nil {
  190. return nil, err
  191. }
  192. var bs []byte
  193. bm := make(gopay.BodyMap)
  194. bm.Set("biz_content", bizContent)
  195. bm.Set("app_id", appId)
  196. bm.Set("method", "monitor.heartbeat.syn")
  197. bm.Set("format", "JSON")
  198. bm.Set("charset", "utf-8")
  199. if signType == gopay.NULL {
  200. bm.Set("sign_type", RSA2)
  201. } else {
  202. bm.Set("sign_type", signType)
  203. }
  204. bm.Set("timestamp", time.Now().Format(xtime.TimeLayout))
  205. bm.Set("version", "1.0")
  206. sign, err := GetRsaSign(bm, bm.GetString("sign_type"), priKey)
  207. if err != nil {
  208. return nil, err
  209. }
  210. bm.Set("sign", sign)
  211. _, bs, err = xhttp.NewClient().Req(xhttp.TypeFormData).Post(baseUrlUtf8).SendString(bm.EncodeURLParams()).EndBytes(ctx)
  212. if err != nil {
  213. return nil, err
  214. }
  215. aliRsp = new(MonitorHeartbeatSynResponse)
  216. if err = json.Unmarshal(bs, aliRsp); err != nil || aliRsp.Response == nil {
  217. return nil, fmt.Errorf("[%w], bytes: %s", gopay.UnmarshalErr, string(bs))
  218. }
  219. if err = bizErrCheck(aliRsp.Response.ErrorResponse); err != nil {
  220. return aliRsp, err
  221. }
  222. return aliRsp, nil
  223. }