1 Star 1 Fork 0

颜言/gopay

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
common_api.go 7.67 KB
一键复制 编辑 原始数据 按行查看 历史
颜言 提交于 2024-09-13 09:11 +08:00 . 重构:统一gopay包的依赖路径
package alipay
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rsa"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/url"
"reflect"
"time"
"gitee.com/ujq/gopay"
xaes "gitee.com/ujq/gopay/pkg/crypto/aes"
"gitee.com/ujq/gopay/pkg/crypto/xpem"
"gitee.com/ujq/gopay/pkg/crypto/xrsa"
"gitee.com/ujq/gopay/pkg/xhttp"
"gitee.com/ujq/gopay/pkg/xtime"
)
// 格式化请求URL参数
func FormatURLParam(body gopay.BodyMap) (urlParam string) {
v := url.Values{}
for key, value := range body {
v.Add(key, value.(string))
}
return v.Encode()
}
// DecryptOpenDataToStruct 解密支付宝开放数据到 结构体
// encryptedData:包括敏感数据在内的完整用户信息的加密数据
// secretKey:AES密钥,支付宝管理平台配置
// beanPtr:需要解析到的结构体指针
// 文档:https://opendocs.alipay.com/common/02mse3
func DecryptOpenDataToStruct(encryptedData, secretKey string, beanPtr any) (err error) {
if encryptedData == gopay.NULL || secretKey == gopay.NULL {
return errors.New("encryptedData or secretKey is null")
}
beanValue := reflect.ValueOf(beanPtr)
if beanValue.Kind() != reflect.Ptr {
return errors.New("传入参数类型必须是以指针形式")
}
if beanValue.Elem().Kind() != reflect.Struct {
return errors.New("传入any必须是结构体")
}
var (
block cipher.Block
blockMode cipher.BlockMode
originData []byte
)
aesKey, _ := base64.StdEncoding.DecodeString(secretKey)
ivKey := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
if block, err = aes.NewCipher(aesKey); err != nil {
return fmt.Errorf("aes.NewCipher:%w", err)
}
if len(secretData)%len(aesKey) != 0 {
return errors.New("encryptedData is error")
}
blockMode = cipher.NewCBCDecrypter(block, ivKey)
originData = make([]byte, len(secretData))
blockMode.CryptBlocks(originData, secretData)
if len(originData) > 0 {
originData = xaes.PKCS5UnPadding(originData)
}
if err = json.Unmarshal(originData, beanPtr); err != nil {
return fmt.Errorf("json.Unmarshal(%s):%w", string(originData), err)
}
return nil
}
// DecryptOpenDataToBodyMap 解密支付宝开放数据到 BodyMap
// encryptedData:包括敏感数据在内的完整用户信息的加密数据
// secretKey:AES密钥,支付宝管理平台配置
// 文档:https://opendocs.alipay.com/common/02mse3
func DecryptOpenDataToBodyMap(encryptedData, secretKey string) (bm gopay.BodyMap, err error) {
if encryptedData == gopay.NULL || secretKey == gopay.NULL {
return nil, errors.New("encryptedData or secretKey is null")
}
var (
aesKey, originData []byte
ivKey = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
block cipher.Block
blockMode cipher.BlockMode
)
aesKey, _ = base64.StdEncoding.DecodeString(secretKey)
secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
if block, err = aes.NewCipher(aesKey); err != nil {
return nil, fmt.Errorf("aes.NewCipher:%w", err)
}
if len(secretData)%len(aesKey) != 0 {
return nil, errors.New("encryptedData is error")
}
blockMode = cipher.NewCBCDecrypter(block, ivKey)
originData = make([]byte, len(secretData))
blockMode.CryptBlocks(originData, secretData)
if len(originData) > 0 {
originData = xaes.PKCS5UnPadding(originData)
}
bm = make(gopay.BodyMap)
if err = json.Unmarshal(originData, &bm); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s):%w", string(originData), err)
}
return
}
// SystemOauthToken 换取授权访问令牌(默认使用utf-8,RSA2)
// appId:应用ID
// privateKey:应用私钥
// grantType:值为 authorization_code 时,代表用code换取;值为 refresh_token 时,代表用refresh_token换取,传空默认code换取
// codeOrToken:支付宝授权码或refresh_token
// signType:签名方式 RSA 或 RSA2,默认 RSA2
// appAuthToken:可选参数,三方授权令牌
// 文档:https://opendocs.alipay.com/apis/api_9/alipay.system.oauth.token
func SystemOauthToken(ctx context.Context, appId string, privateKey, grantType, codeOrToken, signType string, appAuthToken ...string) (rsp *SystemOauthTokenResponse, err error) {
key := xrsa.FormatAlipayPrivateKey(privateKey)
aat := ""
if len(appAuthToken) > 0 {
aat = appAuthToken[0]
}
priKey, err := xpem.DecodePrivateKey([]byte(key))
if err != nil {
return nil, err
}
var bs []byte
bm := make(gopay.BodyMap)
switch grantType {
case "authorization_code":
bm.Set("grant_type", "authorization_code")
bm.Set("code", codeOrToken)
case "refresh_token":
bm.Set("grant_type", "refresh_token")
bm.Set("refresh_token", codeOrToken)
default:
bm.Set("grant_type", "authorization_code")
bm.Set("code", codeOrToken)
}
if bs, err = systemOauthToken(ctx, appId, priKey, bm, "alipay.system.oauth.token", true, signType, aat); err != nil {
return
}
rsp = new(SystemOauthTokenResponse)
if err = json.Unmarshal(bs, rsp); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s):%w", string(bs), err)
}
if (rsp.Response == nil) || (rsp.Response != nil && rsp.Response.AccessToken == "") {
return nil, errors.New("response is nil or access_token is NULL")
}
return
}
// systemOauthToken 向支付宝发送请求
func systemOauthToken(ctx context.Context, appId string, privateKey *rsa.PrivateKey, bm gopay.BodyMap, method string, isProd bool, signType, appAuthToken string) (bs []byte, err error) {
bm.Set("app_id", appId)
bm.Set("method", method)
bm.Set("format", "JSON")
bm.Set("charset", "utf-8")
if signType == gopay.NULL {
bm.Set("sign_type", RSA2)
} else {
bm.Set("sign_type", signType)
}
bm.Set("timestamp", time.Now().Format(xtime.TimeLayout))
bm.Set("version", "1.0")
if appAuthToken != gopay.NULL {
bm.Set("app_auth_token", appAuthToken)
}
var (
sign string
baseUrl = baseUrlUtf8
)
if sign, err = GetRsaSign(bm, bm.GetString("sign_type"), privateKey); err != nil {
return nil, err
}
bm.Set("sign", sign)
if !isProd {
baseUrl = sandboxBaseUrlUtf8
}
_, bs, err = xhttp.NewClient().Req(xhttp.TypeFormData).Post(baseUrl).SendString(bm.EncodeURLParams()).EndBytes(ctx)
if err != nil {
return nil, err
}
return bs, nil
}
// monitor.heartbeat.syn(验签接口)
// appId:应用ID
// privateKey:应用私钥,支持PKCS1和PKCS8
// signType:签名方式 alipay.RSA 或 alipay.RSA2,默认 RSA2
// bizContent:验签时该参数不做任何处理,{任意值},此参数具体看文档
// 文档:https://opendocs.alipay.com/apis/api_9/monitor.heartbeat.syn
func MonitorHeartbeatSyn(ctx context.Context, appId string, privateKey, signType, bizContent string) (aliRsp *MonitorHeartbeatSynResponse, err error) {
key := xrsa.FormatAlipayPrivateKey(privateKey)
priKey, err := xpem.DecodePrivateKey([]byte(key))
if err != nil {
return nil, err
}
var bs []byte
bm := make(gopay.BodyMap)
bm.Set("biz_content", bizContent)
bm.Set("app_id", appId)
bm.Set("method", "monitor.heartbeat.syn")
bm.Set("format", "JSON")
bm.Set("charset", "utf-8")
if signType == gopay.NULL {
bm.Set("sign_type", RSA2)
} else {
bm.Set("sign_type", signType)
}
bm.Set("timestamp", time.Now().Format(xtime.TimeLayout))
bm.Set("version", "1.0")
sign, err := GetRsaSign(bm, bm.GetString("sign_type"), priKey)
if err != nil {
return nil, err
}
bm.Set("sign", sign)
_, bs, err = xhttp.NewClient().Req(xhttp.TypeFormData).Post(baseUrlUtf8).SendString(bm.EncodeURLParams()).EndBytes(ctx)
if err != nil {
return nil, err
}
aliRsp = new(MonitorHeartbeatSynResponse)
if err = json.Unmarshal(bs, aliRsp); err != nil || aliRsp.Response == nil {
return nil, fmt.Errorf("[%w], bytes: %s", gopay.UnmarshalErr, string(bs))
}
if err = bizErrCheck(aliRsp.Response.ErrorResponse); err != nil {
return aliRsp, err
}
return aliRsp, nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/ujq/gopay.git
git@gitee.com:ujq/gopay.git
ujq
gopay
gopay
95cb943fb81a

搜索帮助