alipay_service_api.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. package gopay
  2. import (
  3. "crypto"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "crypto/rsa"
  7. "crypto/x509"
  8. "encoding/base64"
  9. "encoding/json"
  10. "encoding/pem"
  11. "errors"
  12. "fmt"
  13. "hash"
  14. "log"
  15. "net/http"
  16. "reflect"
  17. "strings"
  18. "time"
  19. )
  20. //解析支付宝支付完成后的Notify信息
  21. func ParseAliPayNotifyResult(req *http.Request) (notifyReq *AliPayNotifyRequest, err error) {
  22. notifyReq = new(AliPayNotifyRequest)
  23. notifyReq.NotifyTime = req.FormValue("notify_time")
  24. notifyReq.NotifyType = req.FormValue("notify_type")
  25. notifyReq.NotifyId = req.FormValue("notify_id")
  26. notifyReq.AppId = req.FormValue("app_id")
  27. notifyReq.Charset = req.FormValue("charset")
  28. notifyReq.Version = req.FormValue("version")
  29. notifyReq.SignType = req.FormValue("sign_type")
  30. notifyReq.Sign = req.FormValue("sign")
  31. notifyReq.AuthAppId = req.FormValue("auth_app_id")
  32. notifyReq.TradeNo = req.FormValue("trade_no")
  33. notifyReq.OutTradeNo = req.FormValue("out_trade_no")
  34. notifyReq.OutBizNo = req.FormValue("out_biz_no")
  35. notifyReq.BuyerId = req.FormValue("buyer_id")
  36. notifyReq.BuyerLogonId = req.FormValue("buyer_logon_id")
  37. notifyReq.SellerId = req.FormValue("seller_id")
  38. notifyReq.SellerEmail = req.FormValue("seller_email")
  39. notifyReq.TradeStatus = req.FormValue("trade_status")
  40. notifyReq.TotalAmount = req.FormValue("total_amount")
  41. notifyReq.ReceiptAmount = req.FormValue("receipt_amount")
  42. notifyReq.InvoiceAmount = req.FormValue("invoice_amount")
  43. notifyReq.BuyerPayAmount = req.FormValue("buyer_pay_amount")
  44. notifyReq.PointAmount = req.FormValue("point_amount")
  45. notifyReq.RefundFee = req.FormValue("refund_fee")
  46. notifyReq.Subject = req.FormValue("subject")
  47. notifyReq.Body = req.FormValue("body")
  48. notifyReq.GmtCreate = req.FormValue("gmt_create")
  49. notifyReq.GmtPayment = req.FormValue("gmt_payment")
  50. notifyReq.GmtRefund = req.FormValue("gmt_refund")
  51. notifyReq.GmtClose = req.FormValue("gmt_close")
  52. billList := req.FormValue("fund_bill_list")
  53. //log.Println("billList:", billList)
  54. if billList != null {
  55. bills := make([]fundBillListInfo, 0)
  56. err = json.Unmarshal([]byte(billList), &bills)
  57. if err != nil {
  58. return nil, err
  59. }
  60. notifyReq.FundBillList = bills
  61. } else {
  62. notifyReq.FundBillList = nil
  63. }
  64. notifyReq.PassbackParams = req.FormValue("passback_params")
  65. detailList := req.FormValue("voucher_detail_list")
  66. //log.Println("detailList:", detailList)
  67. if detailList != null {
  68. details := make([]voucherDetailListInfo, 0)
  69. err = json.Unmarshal([]byte(detailList), &details)
  70. if err != nil {
  71. return nil, err
  72. }
  73. notifyReq.VoucherDetailList = details
  74. } else {
  75. notifyReq.VoucherDetailList = nil
  76. }
  77. return notifyReq, err
  78. }
  79. //支付通知的签名验证和参数签名后的Sign(Deprecated)
  80. // aliPayPublicKey:支付宝公钥
  81. // notifyReq:利用 gopay.ParseAliPayNotifyResult() 得到的结构体
  82. // 返回参数ok:是否验证通过
  83. // 返回参数err:错误信息
  84. func VerifyAliPayResultSign(aliPayPublicKey string, notifyReq *AliPayNotifyRequest) (ok bool, err error) {
  85. body := make(BodyMap)
  86. body.Set("notify_time", notifyReq.NotifyTime)
  87. body.Set("notify_type", notifyReq.NotifyType)
  88. body.Set("notify_id", notifyReq.NotifyId)
  89. body.Set("app_id", notifyReq.AppId)
  90. body.Set("charset", notifyReq.Charset)
  91. body.Set("version", notifyReq.Version)
  92. //body.Set("sign", notifyReq.Sign) //验签时去掉
  93. //body.Set("sign_type", notifyReq.SignType) //验签时去掉
  94. body.Set("auth_app_id", notifyReq.AuthAppId)
  95. body.Set("trade_no", notifyReq.TradeNo)
  96. body.Set("out_trade_no", notifyReq.OutTradeNo)
  97. body.Set("out_biz_no", notifyReq.OutBizNo)
  98. body.Set("buyer_id", notifyReq.BuyerId)
  99. body.Set("buyer_logon_id", notifyReq.BuyerLogonId)
  100. body.Set("seller_id", notifyReq.SellerId)
  101. body.Set("seller_email", notifyReq.SellerEmail)
  102. body.Set("trade_status", notifyReq.TradeStatus)
  103. body.Set("total_amount", notifyReq.TotalAmount)
  104. body.Set("receipt_amount", notifyReq.ReceiptAmount)
  105. body.Set("invoice_amount", notifyReq.InvoiceAmount)
  106. body.Set("buyer_pay_amount", notifyReq.BuyerPayAmount)
  107. body.Set("point_amount", notifyReq.PointAmount)
  108. body.Set("refund_fee", notifyReq.RefundFee)
  109. body.Set("subject", notifyReq.Subject)
  110. body.Set("body", notifyReq.Body)
  111. body.Set("gmt_create", notifyReq.GmtCreate)
  112. body.Set("gmt_payment", notifyReq.GmtPayment)
  113. body.Set("gmt_refund", notifyReq.GmtRefund)
  114. body.Set("gmt_close", notifyReq.GmtClose)
  115. body.Set("fund_bill_list", jsonToString(notifyReq.FundBillList))
  116. body.Set("passback_params", notifyReq.PassbackParams)
  117. body.Set("voucher_detail_list", jsonToString(notifyReq.VoucherDetailList))
  118. newBody := make(BodyMap)
  119. for k, v := range body {
  120. if v != null {
  121. newBody.Set(k, v)
  122. }
  123. }
  124. pKey := FormatAliPayPublicKey(aliPayPublicKey)
  125. signData := newBody.EncodeAliPaySignParams()
  126. //log.Println("签名字符串:", signData)
  127. err = verifyAliPaySign(signData, notifyReq.Sign, notifyReq.SignType, pKey)
  128. if err != nil {
  129. return false, err
  130. }
  131. return true, nil
  132. }
  133. //支付宝同步返回验签或异步通知验签
  134. // 注意:APP支付,手机网站支付,电脑网站支付 暂不支持同步返回验签
  135. // aliPayPublicKey:支付宝公钥
  136. // bean: 同步返回验签时,此参数为 aliRsp.SignData ;异步通知验签时,此参数为异步通知解析的结构体 notifyReq
  137. // syncSign:同步返回验签时,此参数必传,即:aliRsp.Sign ;异步通知验签时,不传此参数,否则会出错。
  138. // 返回参数ok:是否验签通过
  139. // 返回参数err:错误信息
  140. // 验签文档:https://docs.open.alipay.com/200/106120
  141. func VerifyAliPaySign(aliPayPublicKey string, bean interface{}, syncSign ...string) (ok bool, err error) {
  142. if bean == nil {
  143. return false, errors.New("bean is nil")
  144. }
  145. var (
  146. bodySign string
  147. bodySignType string
  148. pKey string
  149. signData string
  150. bm BodyMap
  151. bs []byte
  152. )
  153. if len(syncSign) > 0 {
  154. bodySign = syncSign[0]
  155. bodySignType = "RSA2"
  156. signData = bean.(string)
  157. goto Verify
  158. }
  159. bs, err = json.Marshal(bean)
  160. if err != nil {
  161. return false, err
  162. }
  163. bm = make(BodyMap)
  164. err = json.Unmarshal(bs, &bm)
  165. if err != nil {
  166. return false, err
  167. }
  168. bodySign = bm.Get("sign")
  169. bodySignType = bm.Get("sign_type")
  170. bm.Remove("sign")
  171. bm.Remove("sign_type")
  172. signData = bm.EncodeAliPaySignParams()
  173. Verify:
  174. //fmt.Println("signData:", signData)
  175. //fmt.Println("bodySign:", bodySign)
  176. //fmt.Println("bodySignType:", bodySignType)
  177. pKey = FormatAliPayPublicKey(aliPayPublicKey)
  178. err = verifyAliPaySign(signData, bodySign, bodySignType, pKey)
  179. if err != nil {
  180. return false, err
  181. }
  182. return true, nil
  183. }
  184. func verifyAliPaySign(signData, sign, signType, aliPayPublicKey string) (err error) {
  185. var (
  186. h hash.Hash
  187. hashs crypto.Hash
  188. )
  189. signBytes, err := base64.StdEncoding.DecodeString(sign)
  190. if err != nil {
  191. return err
  192. }
  193. //解析秘钥
  194. block, _ := pem.Decode([]byte(aliPayPublicKey))
  195. if block == nil {
  196. return errors.New("支付宝公钥Decode错误")
  197. }
  198. key, err := x509.ParsePKIXPublicKey(block.Bytes)
  199. if err != nil {
  200. log.Println("x509.ParsePKIXPublicKey:", err)
  201. return err
  202. }
  203. publicKey, ok := key.(*rsa.PublicKey)
  204. if !ok {
  205. return errors.New("支付宝公钥转换错误")
  206. }
  207. //判断签名方式
  208. switch signType {
  209. case "RSA":
  210. hashs = crypto.SHA1
  211. case "RSA2":
  212. hashs = crypto.SHA256
  213. default:
  214. hashs = crypto.SHA256
  215. }
  216. h = hashs.New()
  217. h.Write([]byte(signData))
  218. return rsa.VerifyPKCS1v15(publicKey, hashs, h.Sum(nil), signBytes)
  219. }
  220. func jsonToString(v interface{}) (str string) {
  221. if v == nil {
  222. return ""
  223. }
  224. bs, err := json.Marshal(v)
  225. if err != nil {
  226. fmt.Println("err:", err)
  227. return ""
  228. }
  229. s := string(bs)
  230. if s == "null" {
  231. return ""
  232. }
  233. return s
  234. }
  235. //格式化应用秘钥
  236. func FormatPrivateKey(privateKey string) (pKey string) {
  237. var buffer strings.Builder
  238. buffer.WriteString("-----BEGIN RSA PRIVATE KEY-----\n")
  239. rawLen := 64
  240. keyLen := len(privateKey)
  241. raws := keyLen / rawLen
  242. temp := keyLen % rawLen
  243. if temp > 0 {
  244. raws++
  245. }
  246. start := 0
  247. end := start + rawLen
  248. for i := 0; i < raws; i++ {
  249. if i == raws-1 {
  250. buffer.WriteString(privateKey[start:])
  251. } else {
  252. buffer.WriteString(privateKey[start:end])
  253. }
  254. buffer.WriteByte('\n')
  255. start += rawLen
  256. end = start + rawLen
  257. }
  258. buffer.WriteString("-----END RSA PRIVATE KEY-----\n")
  259. pKey = buffer.String()
  260. return
  261. }
  262. //格式化支付宝公钥
  263. func FormatAliPayPublicKey(publicKey string) (pKey string) {
  264. var buffer strings.Builder
  265. buffer.WriteString("-----BEGIN PUBLIC KEY-----\n")
  266. rawLen := 64
  267. keyLen := len(publicKey)
  268. raws := keyLen / rawLen
  269. temp := keyLen % rawLen
  270. if temp > 0 {
  271. raws++
  272. }
  273. start := 0
  274. end := start + rawLen
  275. for i := 0; i < raws; i++ {
  276. if i == raws-1 {
  277. buffer.WriteString(publicKey[start:])
  278. } else {
  279. buffer.WriteString(publicKey[start:end])
  280. }
  281. buffer.WriteByte('\n')
  282. start += rawLen
  283. end = start + rawLen
  284. }
  285. buffer.WriteString("-----END PUBLIC KEY-----\n")
  286. pKey = buffer.String()
  287. return
  288. }
  289. //解密支付宝开放数据
  290. // encryptedData:包括敏感数据在内的完整用户信息的加密数据
  291. // secretKey:AES密钥,支付宝管理平台配置
  292. // beanPtr:需要解析到的结构体指针
  293. // 文档:https://docs.alipay.com/mini/introduce/aes
  294. // 文档:https://docs.open.alipay.com/common/104567
  295. func DecryptAliPayOpenDataToStruct(encryptedData, secretKey string, beanPtr interface{}) (err error) {
  296. //验证参数类型
  297. beanValue := reflect.ValueOf(beanPtr)
  298. if beanValue.Kind() != reflect.Ptr {
  299. return errors.New("传入参数类型必须是以指针形式")
  300. }
  301. //验证interface{}类型
  302. if beanValue.Elem().Kind() != reflect.Struct {
  303. return errors.New("传入interface{}必须是结构体")
  304. }
  305. aesKey, _ := base64.StdEncoding.DecodeString(secretKey)
  306. ivKey := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  307. secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
  308. block, err := aes.NewCipher(aesKey)
  309. if err != nil {
  310. return err
  311. }
  312. if len(secretData)%len(aesKey) != 0 {
  313. return errors.New("encryptedData is error")
  314. }
  315. blockMode := cipher.NewCBCDecrypter(block, ivKey)
  316. originData := make([]byte, len(secretData))
  317. blockMode.CryptBlocks(originData, secretData)
  318. if len(originData) > 0 {
  319. originData = PKCS5UnPadding(originData)
  320. }
  321. //fmt.Println("originDataStr:", string(originData))
  322. //解析
  323. err = json.Unmarshal(originData, beanPtr)
  324. if err != nil {
  325. return err
  326. }
  327. return nil
  328. }
  329. //换取授权访问令牌(默认使用utf-8,RSA2)
  330. // appId:应用ID
  331. // privateKey:应用私钥
  332. // grantType:值为 authorization_code 时,代表用code换取;值为 refresh_token 时,代表用refresh_token换取,传空默认code换取
  333. // codeOrToken:支付宝授权码或refresh_token
  334. // 文档:https://docs.open.alipay.com/api_9/alipay.system.oauth.token
  335. func AliPaySystemOauthToken(appId, privateKey, grantType, codeOrToken string) (rsp *AliPaySystemOauthTokenResponse, err error) {
  336. var bs []byte
  337. body := make(BodyMap)
  338. if "authorization_code" == grantType {
  339. body.Set("grant_type", "authorization_code")
  340. body.Set("code", codeOrToken)
  341. } else if "refresh_token" == grantType {
  342. body.Set("grant_type", "refresh_token")
  343. body.Set("refresh_token", codeOrToken)
  344. } else {
  345. body.Set("grant_type", "authorization_code")
  346. body.Set("code", codeOrToken)
  347. }
  348. bs, err = aliPaySystemOauthToken(appId, privateKey, body, "alipay.system.oauth.token", true)
  349. if err != nil {
  350. return nil, err
  351. }
  352. //fmt.Println("bs:", string(bs))
  353. rsp = new(AliPaySystemOauthTokenResponse)
  354. err = json.Unmarshal(bs, rsp)
  355. if err != nil {
  356. return nil, err
  357. }
  358. if rsp.AliPaySystemOauthTokenResponse.AccessToken != "" {
  359. return rsp, nil
  360. } else {
  361. return
  362. }
  363. }
  364. //向支付宝发送请求
  365. func aliPaySystemOauthToken(appId, privateKey string, body BodyMap, method string, isProd bool) (bytes []byte, err error) {
  366. //===============生成参数===================
  367. body.Set("app_id", appId)
  368. body.Set("method", method)
  369. body.Set("format", "JSON")
  370. body.Set("charset", "utf-8")
  371. body.Set("sign_type", "RSA2")
  372. body.Set("timestamp", time.Now().Format(TimeLayout))
  373. body.Set("version", "1.0")
  374. //===============获取签名===================
  375. pKey := FormatPrivateKey(privateKey)
  376. sign, err := getRsaSign(body, "RSA2", pKey)
  377. if err != nil {
  378. return nil, err
  379. }
  380. body.Set("sign", sign)
  381. //fmt.Println("rsaSign:", sign)
  382. //===============发起请求===================
  383. urlParam := FormatAliPayURLParam(body)
  384. var url string
  385. agent := HttpAgent()
  386. if !isProd {
  387. //沙箱环境
  388. url = zfb_sanbox_base_url_utf8
  389. //fmt.Println(url)
  390. agent.Post(url)
  391. } else {
  392. //正式环境
  393. url = zfb_base_url_utf8
  394. //fmt.Println(url)
  395. agent.Post(url)
  396. }
  397. _, bs, errs := agent.
  398. Type("form-data").
  399. SendString(urlParam).
  400. EndBytes()
  401. if len(errs) > 0 {
  402. return nil, errs[0]
  403. }
  404. return bs, nil
  405. }