service_api.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. package alipay
  2. import (
  3. "crypto"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "crypto/md5"
  7. "crypto/rsa"
  8. "crypto/x509"
  9. "encoding/base64"
  10. "encoding/hex"
  11. "encoding/json"
  12. "encoding/pem"
  13. "errors"
  14. "fmt"
  15. "hash"
  16. "io/ioutil"
  17. "net/http"
  18. "net/url"
  19. "reflect"
  20. "strings"
  21. "time"
  22. "github.com/iGoogle-ink/gopay"
  23. )
  24. // 允许进行 sn 提取的证书签名算法
  25. var allowSignatureAlgorithm = map[string]bool{
  26. "MD2-RSA": true,
  27. "MD5-RSA": true,
  28. "SHA1-RSA": true,
  29. "SHA256-RSA": true,
  30. "SHA384-RSA": true,
  31. "SHA512-RSA": true,
  32. "SHA256-RSAPSS": true,
  33. "SHA384-RSAPSS": true,
  34. "SHA512-RSAPSS": true,
  35. }
  36. // 解析支付宝支付异步通知的参数到BodyMap
  37. // req:*http.Request
  38. // 返回参数bm:Notify请求的参数
  39. // 返回参数err:错误信息
  40. // 文档:https://opendocs.alipay.com/open/203/105286
  41. func ParseNotifyToBodyMap(req *http.Request) (bm gopay.BodyMap, err error) {
  42. if err = req.ParseForm(); err != nil {
  43. return nil, err
  44. }
  45. var form map[string][]string = req.Form
  46. bm = make(gopay.BodyMap, len(form)+1)
  47. for k, v := range form {
  48. if len(v) == 1 {
  49. bm.Set(k, v[0])
  50. }
  51. }
  52. return
  53. }
  54. // 通过 url.Values 解析支付宝支付异步通知的参数到Struct
  55. // value:url.Values
  56. // 返回参数notifyReq:Notify请求的参数
  57. // 返回参数err:错误信息
  58. // 文档:https://opendocs.alipay.com/open/203/105286
  59. func ParseNotifyByURLValues(value url.Values) (bm gopay.BodyMap, err error) {
  60. bm = make(gopay.BodyMap, len(value)+1)
  61. for k, v := range value {
  62. if len(v) == 1 {
  63. bm.Set(k, v[0])
  64. }
  65. }
  66. return
  67. }
  68. // Deprecated
  69. // 解析支付宝支付异步通知的参数到Struct
  70. // req:*http.Request
  71. // 返回参数notifyReq:Notify请求的参数
  72. // 返回参数err:错误信息
  73. // 文档:https://opendocs.alipay.com/open/203/105286
  74. func ParseNotifyResult(req *http.Request) (notifyReq *NotifyRequest, err error) {
  75. notifyReq = new(NotifyRequest)
  76. if err = req.ParseForm(); err != nil {
  77. return
  78. }
  79. notifyReq.NotifyTime = req.Form.Get("notify_time")
  80. notifyReq.NotifyType = req.Form.Get("notify_type")
  81. notifyReq.NotifyId = req.Form.Get("notify_id")
  82. notifyReq.AppId = req.Form.Get("app_id")
  83. notifyReq.Charset = req.Form.Get("charset")
  84. notifyReq.Version = req.Form.Get("version")
  85. notifyReq.SignType = req.Form.Get("sign_type")
  86. notifyReq.Sign = req.Form.Get("sign")
  87. notifyReq.AuthAppId = req.Form.Get("auth_app_id")
  88. notifyReq.TradeNo = req.Form.Get("trade_no")
  89. notifyReq.OutTradeNo = req.Form.Get("out_trade_no")
  90. notifyReq.OutBizNo = req.Form.Get("out_biz_no")
  91. notifyReq.BuyerId = req.Form.Get("buyer_id")
  92. notifyReq.BuyerLogonId = req.Form.Get("buyer_logon_id")
  93. notifyReq.SellerId = req.Form.Get("seller_id")
  94. notifyReq.SellerEmail = req.Form.Get("seller_email")
  95. notifyReq.TradeStatus = req.Form.Get("trade_status")
  96. notifyReq.TotalAmount = req.Form.Get("total_amount")
  97. notifyReq.ReceiptAmount = req.Form.Get("receipt_amount")
  98. notifyReq.InvoiceAmount = req.Form.Get("invoice_amount")
  99. notifyReq.BuyerPayAmount = req.Form.Get("buyer_pay_amount")
  100. notifyReq.PointAmount = req.Form.Get("point_amount")
  101. notifyReq.RefundFee = req.Form.Get("refund_fee")
  102. notifyReq.Subject = req.Form.Get("subject")
  103. notifyReq.Body = req.Form.Get("body")
  104. notifyReq.GmtCreate = req.Form.Get("gmt_create")
  105. notifyReq.GmtPayment = req.Form.Get("gmt_payment")
  106. notifyReq.GmtRefund = req.Form.Get("gmt_refund")
  107. notifyReq.GmtClose = req.Form.Get("gmt_close")
  108. notifyReq.PassbackParams = req.Form.Get("passback_params")
  109. billList := req.Form.Get("fund_bill_list")
  110. if billList != gopay.NULL {
  111. bills := make([]*FundBillListInfo, 0)
  112. if err = json.Unmarshal([]byte(billList), &bills); err != nil {
  113. return nil, fmt.Errorf(`"fund_bill_list" xml.Unmarshal(%s):%w`, billList, err)
  114. }
  115. notifyReq.FundBillList = bills
  116. } else {
  117. notifyReq.FundBillList = nil
  118. }
  119. detailList := req.Form.Get("voucher_detail_list")
  120. if detailList != gopay.NULL {
  121. details := make([]*VoucherDetailListInfo, 0)
  122. if err = json.Unmarshal([]byte(detailList), &details); err != nil {
  123. return nil, fmt.Errorf(`"voucher_detail_list" xml.Unmarshal(%s):%w`, detailList, err)
  124. }
  125. notifyReq.VoucherDetailList = details
  126. } else {
  127. notifyReq.VoucherDetailList = nil
  128. }
  129. return
  130. }
  131. /*
  132. Q:使用公钥证书签名方式下,为什么开放平台网关的响应报文需要携带支付宝公钥证书SN(alipay_cert_sn)?
  133. **
  134. A:开发者上传自己的应用公钥证书后,开放平台会为开发者应用自动签发支付宝公钥证书供开发者下载,用来对开放平台网关响应报文做验签。
  135. 但是支付宝公钥证书可能因证书到期或者变更CA签发机构等原因,可能会重新签发证书。在重新签发前,开放平台会在门户上提前提醒开发者支付宝应用公钥证书变更时间。
  136. 但为避免开发者因未能及时感知支付宝公钥证书变更而导致验签失败,开放平台提供了一种支付宝公钥证书无感知升级机制,具体流程如下:
  137. 1)开放平台网关在响应报文中会多返回支付宝公钥证书SN
  138. 2)开放平台网关提供根据SN下载对应支付宝公钥证书的API接口
  139. 3)开发者在验签过程中,先比较本地使用的支付宝公钥证书SN与开放平台网关响应中SN是否一致。若不一致,可调用支付宝公钥证书下载接口下载对应SN的支付宝公钥证书。
  140. 4)对下载的支付宝公钥证书执行证书链校验,若校验通过,则用该证书验签。
  141. 基于该机制可实现支付宝公钥证书变更时开发者无感知,当前开放平台提供的SDK已基于该机制实现对应功能。若开发者未通过SDK接入,须自行实现该功能。
  142. */
  143. // VerifySyncSign 支付宝同步返回验签
  144. // 注意:APP支付,手机网站支付,电脑网站支付 暂不支持同步返回验签
  145. // aliPayPublicKey:支付宝公钥
  146. // signData:待验签参数,aliRsp.SignData
  147. // sign:待验签sign,aliRsp.Sign
  148. // 返回参数ok:是否验签通过
  149. // 返回参数err:错误信息
  150. // 验签文档:https://opendocs.alipay.com/open/200/106120
  151. func VerifySyncSign(aliPayPublicKey, signData, sign string) (ok bool, err error) {
  152. // 支付宝公钥验签
  153. pKey := FormatPublicKey(aliPayPublicKey)
  154. if err = verifySign(signData, sign, RSA2, pKey); err != nil {
  155. return false, err
  156. }
  157. return true, nil
  158. }
  159. // VerifySign 支付宝异步通知验签
  160. // 注意:APP支付,手机网站支付,电脑网站支付 暂不支持同步返回验签
  161. // aliPayPublicKey:支付宝公钥
  162. // bean:此参数为异步通知解析的结构体或BodyMap:notifyReq 或 bm,推荐通 BodyMap 验签
  163. // 返回参数ok:是否验签通过
  164. // 返回参数err:错误信息
  165. // 验签文档:https://opendocs.alipay.com/open/200/106120
  166. func VerifySign(aliPayPublicKey string, bean interface{}) (ok bool, err error) {
  167. if aliPayPublicKey == gopay.NULL {
  168. return false, errors.New("aliPayPublicKey is null")
  169. }
  170. if bean == nil {
  171. return false, errors.New("bean is nil")
  172. }
  173. var (
  174. bodySign string
  175. bodySignType string
  176. signData string
  177. bm = make(gopay.BodyMap)
  178. )
  179. if reflect.ValueOf(bean).Kind() == reflect.Map {
  180. if bm, ok = bean.(gopay.BodyMap); ok {
  181. bodySign = bm.Get("sign")
  182. bodySignType = bm.Get("sign_type")
  183. bm.Remove("sign")
  184. bm.Remove("sign_type")
  185. signData = bm.EncodeAliPaySignParams()
  186. }
  187. } else {
  188. bs, err := json.Marshal(bean)
  189. if err != nil {
  190. return false, fmt.Errorf("json.Marshal:%w", err)
  191. }
  192. if err = json.Unmarshal(bs, &bm); err != nil {
  193. return false, fmt.Errorf("json.Unmarshal(%s):%w", string(bs), err)
  194. }
  195. bodySign = bm.Get("sign")
  196. bodySignType = bm.Get("sign_type")
  197. bm.Remove("sign")
  198. bm.Remove("sign_type")
  199. signData = bm.EncodeAliPaySignParams()
  200. }
  201. pKey := FormatPublicKey(aliPayPublicKey)
  202. if err = verifySign(signData, bodySign, bodySignType, pKey); err != nil {
  203. return false, err
  204. }
  205. return true, nil
  206. }
  207. func verifySign(signData, sign, signType, aliPayPublicKey string) (err error) {
  208. var (
  209. h hash.Hash
  210. hashs crypto.Hash
  211. block *pem.Block
  212. pubKey interface{}
  213. publicKey *rsa.PublicKey
  214. ok bool
  215. )
  216. signBytes, _ := base64.StdEncoding.DecodeString(sign)
  217. if block, _ = pem.Decode([]byte(aliPayPublicKey)); block == nil {
  218. return errors.New("支付宝公钥Decode错误")
  219. }
  220. if pubKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
  221. return fmt.Errorf("x509.ParsePKIXPublicKey:%w", err)
  222. }
  223. if publicKey, ok = pubKey.(*rsa.PublicKey); !ok {
  224. return errors.New("支付宝公钥转换错误")
  225. }
  226. switch signType {
  227. case RSA:
  228. hashs = crypto.SHA1
  229. case RSA2:
  230. hashs = crypto.SHA256
  231. default:
  232. hashs = crypto.SHA256
  233. }
  234. h = hashs.New()
  235. h.Write([]byte(signData))
  236. return rsa.VerifyPKCS1v15(publicKey, hashs, h.Sum(nil), signBytes)
  237. }
  238. // VerifySignWithCert 支付宝异步通知验签
  239. // 注意:APP支付,手机网站支付,电脑网站支付 暂不支持同步返回验签
  240. // aliPayPublicKeyPath:支付宝公钥存放路径 alipayCertPublicKey_RSA2.crt
  241. // bean:此参数为异步通知解析的结构体或BodyMap:notifyReq 或 bm,推荐通 BodyMap 验签
  242. // 返回参数ok:是否验签通过
  243. // 返回参数err:错误信息
  244. // 验签文档:https://opendocs.alipay.com/open/200/106120
  245. func VerifySignWithCert(aliPayPublicKeyPath string, bean interface{}) (ok bool, err error) {
  246. if aliPayPublicKeyPath == gopay.NULL {
  247. return false, errors.New("aliPayPublicKeyPath is null")
  248. }
  249. if bean == nil {
  250. return false, errors.New("bean is nil")
  251. }
  252. var (
  253. bodySign string
  254. bodySignType string
  255. signData string
  256. bm = make(gopay.BodyMap)
  257. )
  258. if reflect.ValueOf(bean).Kind() == reflect.Map {
  259. if bm, ok = bean.(gopay.BodyMap); ok {
  260. bodySign = bm.Get("sign")
  261. bodySignType = bm.Get("sign_type")
  262. bm.Remove("sign")
  263. bm.Remove("sign_type")
  264. signData = bm.EncodeAliPaySignParams()
  265. }
  266. } else {
  267. bs, err := json.Marshal(bean)
  268. if err != nil {
  269. return false, fmt.Errorf("json.Marshal:%w", err)
  270. }
  271. if err = json.Unmarshal(bs, &bm); err != nil {
  272. return false, fmt.Errorf("json.Unmarshal(%s):%w", string(bs), err)
  273. }
  274. bodySign = bm.Get("sign")
  275. bodySignType = bm.Get("sign_type")
  276. bm.Remove("sign")
  277. bm.Remove("sign_type")
  278. signData = bm.EncodeAliPaySignParams()
  279. }
  280. if err = verifySignCert(signData, bodySign, bodySignType, aliPayPublicKeyPath); err != nil {
  281. return false, err
  282. }
  283. return true, nil
  284. }
  285. func verifySignCert(signData, sign, signType, aliPayPublicKeyPath string) (err error) {
  286. var (
  287. h hash.Hash
  288. hashs crypto.Hash
  289. block *pem.Block
  290. pubKey *x509.Certificate
  291. publicKey *rsa.PublicKey
  292. ok bool
  293. bytes []byte
  294. )
  295. if bytes, err = ioutil.ReadFile(aliPayPublicKeyPath); err != nil {
  296. return fmt.Errorf("支付宝公钥文件读取失败: %w", err)
  297. }
  298. signBytes, _ := base64.StdEncoding.DecodeString(sign)
  299. if block, _ = pem.Decode(bytes); block == nil {
  300. return errors.New("支付宝公钥Decode错误")
  301. }
  302. if pubKey, err = x509.ParseCertificate(block.Bytes); err != nil {
  303. return fmt.Errorf("x509.ParseCertificate:%w", err)
  304. }
  305. if publicKey, ok = pubKey.PublicKey.(*rsa.PublicKey); !ok {
  306. return errors.New("支付宝公钥转换错误")
  307. }
  308. switch signType {
  309. case RSA:
  310. hashs = crypto.SHA1
  311. case RSA2:
  312. hashs = crypto.SHA256
  313. default:
  314. hashs = crypto.SHA256
  315. }
  316. h = hashs.New()
  317. h.Write([]byte(signData))
  318. return rsa.VerifyPKCS1v15(publicKey, hashs, h.Sum(nil), signBytes)
  319. }
  320. // FormatPrivateKey 格式化 普通应用秘钥
  321. func FormatPrivateKey(privateKey string) (pKey string) {
  322. var buffer strings.Builder
  323. buffer.WriteString("-----BEGIN RSA PRIVATE KEY-----\n")
  324. rawLen := 64
  325. keyLen := len(privateKey)
  326. raws := keyLen / rawLen
  327. temp := keyLen % rawLen
  328. if temp > 0 {
  329. raws++
  330. }
  331. start := 0
  332. end := start + rawLen
  333. for i := 0; i < raws; i++ {
  334. if i == raws-1 {
  335. buffer.WriteString(privateKey[start:])
  336. } else {
  337. buffer.WriteString(privateKey[start:end])
  338. }
  339. buffer.WriteByte('\n')
  340. start += rawLen
  341. end = start + rawLen
  342. }
  343. buffer.WriteString("-----END RSA PRIVATE KEY-----\n")
  344. pKey = buffer.String()
  345. return
  346. }
  347. // FormatPublicKey 格式化 普通支付宝公钥
  348. func FormatPublicKey(publicKey string) (pKey string) {
  349. var buffer strings.Builder
  350. buffer.WriteString("-----BEGIN PUBLIC KEY-----\n")
  351. rawLen := 64
  352. keyLen := len(publicKey)
  353. raws := keyLen / rawLen
  354. temp := keyLen % rawLen
  355. if temp > 0 {
  356. raws++
  357. }
  358. start := 0
  359. end := start + rawLen
  360. for i := 0; i < raws; i++ {
  361. if i == raws-1 {
  362. buffer.WriteString(publicKey[start:])
  363. } else {
  364. buffer.WriteString(publicKey[start:end])
  365. }
  366. buffer.WriteByte('\n')
  367. start += rawLen
  368. end = start + rawLen
  369. }
  370. buffer.WriteString("-----END PUBLIC KEY-----\n")
  371. pKey = buffer.String()
  372. return
  373. }
  374. // GetCertSN 获取证书序列号SN
  375. // certPath:X.509证书文件路径(appCertPublicKey.crt、alipayCertPublicKey_RSA2.crt)
  376. // 返回 sn:证书序列号(app_cert_sn、alipay_cert_sn)
  377. // 返回 err:error 信息
  378. func GetCertSN(certPath string) (sn string, err error) {
  379. certData, err := ioutil.ReadFile(certPath)
  380. if err != nil {
  381. return gopay.NULL, err
  382. }
  383. if block, _ := pem.Decode(certData); block != nil {
  384. cert, err := x509.ParseCertificate(block.Bytes)
  385. if err != nil {
  386. return gopay.NULL, err
  387. }
  388. name := cert.Issuer.String()
  389. serialNumber := cert.SerialNumber.String()
  390. h := md5.New()
  391. h.Write([]byte(name))
  392. h.Write([]byte(serialNumber))
  393. sn = hex.EncodeToString(h.Sum(nil))
  394. }
  395. if sn == gopay.NULL {
  396. return gopay.NULL, errors.New("failed to get sn,please check your cert")
  397. }
  398. return sn, nil
  399. }
  400. // GetRootCertSN 获取root证书序列号SN
  401. // rootCertPath:X.509证书文件路径(alipayRootCert.crt)
  402. // 返回 sn:证书序列号(alipay_root_cert_sn)
  403. // 返回 err:error 信息
  404. func GetRootCertSN(rootCertPath string) (sn string, err error) {
  405. var certEnd = `-----END CERTIFICATE-----`
  406. certData, err := ioutil.ReadFile(rootCertPath)
  407. if err != nil {
  408. return gopay.NULL, err
  409. }
  410. pems := strings.Split(string(certData), certEnd)
  411. for _, c := range pems {
  412. if block, _ := pem.Decode([]byte(c + certEnd)); block != nil {
  413. cert, err := x509.ParseCertificate(block.Bytes)
  414. if err != nil {
  415. continue
  416. }
  417. if !allowSignatureAlgorithm[cert.SignatureAlgorithm.String()] {
  418. continue
  419. }
  420. name := cert.Issuer.String()
  421. serialNumber := cert.SerialNumber.String()
  422. h := md5.New()
  423. h.Write([]byte(name))
  424. h.Write([]byte(serialNumber))
  425. if sn == gopay.NULL {
  426. sn += hex.EncodeToString(h.Sum(nil))
  427. } else {
  428. sn += "_" + hex.EncodeToString(h.Sum(nil))
  429. }
  430. }
  431. }
  432. if sn == gopay.NULL {
  433. return gopay.NULL, errors.New("failed to get sn,please check your cert")
  434. }
  435. return sn, nil
  436. }
  437. // DecryptOpenDataToStruct 解密支付宝开放数据到 结构体
  438. // encryptedData:包括敏感数据在内的完整用户信息的加密数据
  439. // secretKey:AES密钥,支付宝管理平台配置
  440. // beanPtr:需要解析到的结构体指针
  441. // 文档:https://opendocs.alipay.com/mini/introduce/aes
  442. // 文档:https://opendocs.alipay.com/open/common/104567
  443. func DecryptOpenDataToStruct(encryptedData, secretKey string, beanPtr interface{}) (err error) {
  444. if encryptedData == gopay.NULL || secretKey == gopay.NULL {
  445. return errors.New("encryptedData or secretKey is null")
  446. }
  447. beanValue := reflect.ValueOf(beanPtr)
  448. if beanValue.Kind() != reflect.Ptr {
  449. return errors.New("传入参数类型必须是以指针形式")
  450. }
  451. if beanValue.Elem().Kind() != reflect.Struct {
  452. return errors.New("传入interface{}必须是结构体")
  453. }
  454. var (
  455. block cipher.Block
  456. blockMode cipher.BlockMode
  457. originData []byte
  458. )
  459. aesKey, _ := base64.StdEncoding.DecodeString(secretKey)
  460. ivKey := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  461. secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
  462. if block, err = aes.NewCipher(aesKey); err != nil {
  463. return fmt.Errorf("aes.NewCipher:%w", err)
  464. }
  465. if len(secretData)%len(aesKey) != 0 {
  466. return errors.New("encryptedData is error")
  467. }
  468. blockMode = cipher.NewCBCDecrypter(block, ivKey)
  469. originData = make([]byte, len(secretData))
  470. blockMode.CryptBlocks(originData, secretData)
  471. if len(originData) > 0 {
  472. originData = gopay.PKCS5UnPadding(originData)
  473. }
  474. if err = json.Unmarshal(originData, beanPtr); err != nil {
  475. return fmt.Errorf("json.Unmarshal(%s):%w", string(originData), err)
  476. }
  477. return nil
  478. }
  479. // DecryptOpenDataToBodyMap 解密支付宝开放数据到 BodyMap
  480. // encryptedData:包括敏感数据在内的完整用户信息的加密数据
  481. // secretKey:AES密钥,支付宝管理平台配置
  482. // 文档:https://opendocs.alipay.com/mini/introduce/aes
  483. // 文档:https://opendocs.alipay.com/open/common/104567
  484. func DecryptOpenDataToBodyMap(encryptedData, secretKey string) (bm gopay.BodyMap, err error) {
  485. if encryptedData == gopay.NULL || secretKey == gopay.NULL {
  486. return nil, errors.New("encryptedData or secretKey is null")
  487. }
  488. var (
  489. aesKey, originData []byte
  490. ivKey = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  491. block cipher.Block
  492. blockMode cipher.BlockMode
  493. )
  494. aesKey, _ = base64.StdEncoding.DecodeString(secretKey)
  495. secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
  496. if block, err = aes.NewCipher(aesKey); err != nil {
  497. return nil, fmt.Errorf("aes.NewCipher:%w", err)
  498. }
  499. if len(secretData)%len(aesKey) != 0 {
  500. return nil, errors.New("encryptedData is error")
  501. }
  502. blockMode = cipher.NewCBCDecrypter(block, ivKey)
  503. originData = make([]byte, len(secretData))
  504. blockMode.CryptBlocks(originData, secretData)
  505. if len(originData) > 0 {
  506. originData = gopay.PKCS5UnPadding(originData)
  507. }
  508. bm = make(gopay.BodyMap)
  509. if err = json.Unmarshal(originData, &bm); err != nil {
  510. return nil, fmt.Errorf("json.Unmarshal(%s):%w", string(originData), err)
  511. }
  512. return
  513. }
  514. // SystemOauthToken 换取授权访问令牌(默认使用utf-8,RSA2)
  515. // appId:应用ID
  516. // t:支付宝私钥类型,alipay.PKCS1 或 alipay.PKCS8,默认 PKCS1
  517. // privateKey:应用私钥
  518. // grantType:值为 authorization_code 时,代表用code换取;值为 refresh_token 时,代表用refresh_token换取,传空默认code换取
  519. // codeOrToken:支付宝授权码或refresh_token
  520. // signType:签名方式 RSA 或 RSA2,默认 RSA2
  521. // 文档:https://opendocs.alipay.com/apis/api_9/alipay.system.oauth.token
  522. func SystemOauthToken(appId string, t PKCSType, privateKey, grantType, codeOrToken, signType string) (rsp *SystemOauthTokenResponse, err error) {
  523. var bs []byte
  524. bm := make(gopay.BodyMap)
  525. if "authorization_code" == grantType {
  526. bm.Set("grant_type", "authorization_code")
  527. bm.Set("code", codeOrToken)
  528. } else if "refresh_token" == grantType {
  529. bm.Set("grant_type", "refresh_token")
  530. bm.Set("refresh_token", codeOrToken)
  531. } else {
  532. bm.Set("grant_type", "authorization_code")
  533. bm.Set("code", codeOrToken)
  534. }
  535. if bs, err = systemOauthToken(appId, t, privateKey, bm, "alipay.system.oauth.token", true, signType); err != nil {
  536. return
  537. }
  538. rsp = new(SystemOauthTokenResponse)
  539. if err = json.Unmarshal(bs, rsp); err != nil {
  540. return nil, fmt.Errorf("json.Unmarshal(%s):%w", string(bs), err)
  541. }
  542. if rsp.Response.AccessToken == "" {
  543. return nil, errors.New("access_token is NULL")
  544. }
  545. return
  546. }
  547. // systemOauthToken 向支付宝发送请求
  548. func systemOauthToken(appId string, t PKCSType, privateKey string, bm gopay.BodyMap, method string, isProd bool, signType string) (bs []byte, err error) {
  549. bm.Set("app_id", appId)
  550. bm.Set("method", method)
  551. bm.Set("format", "JSON")
  552. bm.Set("charset", "utf-8")
  553. if signType == gopay.NULL {
  554. bm.Set("sign_type", RSA2)
  555. } else {
  556. bm.Set("sign_type", signType)
  557. }
  558. bm.Set("timestamp", time.Now().Format(gopay.TimeLayout))
  559. bm.Set("version", "1.0")
  560. var (
  561. sign string
  562. url = baseUrlUtf8
  563. )
  564. if sign, err = GetRsaSign(bm, bm.Get("sign_type"), t, privateKey); err != nil {
  565. return nil, err
  566. }
  567. bm.Set("sign", sign)
  568. if !isProd {
  569. url = sandboxBaseUrlUtf8
  570. }
  571. _, bs, errs := gopay.NewHttpClient().Type(gopay.TypeForm).Post(url).SendString(FormatURLParam(bm)).EndBytes()
  572. if len(errs) > 0 {
  573. return nil, errs[0]
  574. }
  575. return bs, nil
  576. }
  577. // monitor.heartbeat.syn(验签接口)
  578. // appId:应用ID
  579. // privateKey:应用私钥,支持PKCS1和PKCS8
  580. // signType:签名方式 alipay.RSA 或 alipay.RSA2,默认 RSA2
  581. // bizContent:验签时该参数不做任何处理,{任意值},此参数具体看文档
  582. // 文档:https://opendocs.alipay.com/apis/api_9/monitor.heartbeat.syn
  583. func MonitorHeartbeatSyn(appId string, t PKCSType, privateKey, signType, bizContent string) (rsp *MonitorHeartbeatSynResponse, err error) {
  584. var bs []byte
  585. bm := make(gopay.BodyMap)
  586. bm.Set("biz_content", bizContent)
  587. bm.Set("app_id", appId)
  588. bm.Set("method", "monitor.heartbeat.syn")
  589. bm.Set("format", "JSON")
  590. bm.Set("charset", "utf-8")
  591. if signType == gopay.NULL {
  592. bm.Set("sign_type", RSA2)
  593. } else {
  594. bm.Set("sign_type", signType)
  595. }
  596. bm.Set("timestamp", time.Now().Format(gopay.TimeLayout))
  597. bm.Set("version", "1.0")
  598. sign, err := GetRsaSign(bm, bm.Get("sign_type"), t, privateKey)
  599. if err != nil {
  600. return nil, err
  601. }
  602. bm.Set("sign", sign)
  603. _, bs, errs := gopay.NewHttpClient().Type(gopay.TypeForm).Post(baseUrlUtf8).SendString(FormatURLParam(bm)).EndBytes()
  604. if len(errs) > 0 {
  605. return nil, errs[0]
  606. }
  607. fmt.Println("bsbsbs:", string(bs))
  608. rsp = new(MonitorHeartbeatSynResponse)
  609. if err = json.Unmarshal(bs, rsp); err != nil {
  610. return nil, err
  611. }
  612. return rsp, nil
  613. }