service_api.go 23 KB

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