+alipayapi
This commit is contained in:
162
platformapi/alipayapi/alipayapi.go
Normal file
162
platformapi/alipayapi/alipayapi.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package alipayapi
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
"git.rosy.net.cn/baseapi/platformapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
prodURL = "https://openapi.alipay.com/gateway.do"
|
||||
|
||||
signKey = "sign"
|
||||
)
|
||||
|
||||
const (
|
||||
CommonErrSuccess = "10000"
|
||||
CommonErrServiceNotAvailable = "20000"
|
||||
)
|
||||
|
||||
type API struct {
|
||||
appID string
|
||||
privateKey *rsa.PrivateKey
|
||||
client *http.Client
|
||||
config *platformapi.APIConfig
|
||||
}
|
||||
|
||||
var (
|
||||
commonCanRetryErrMap = map[string]bool{
|
||||
CommonErrServiceNotAvailable: true,
|
||||
}
|
||||
)
|
||||
|
||||
func New(appID string, privateKey interface{}, config ...*platformapi.APIConfig) (a *API) {
|
||||
curConfig := platformapi.DefAPIConfig
|
||||
if len(config) > 0 {
|
||||
curConfig = *config[0]
|
||||
}
|
||||
var keyBytes []byte
|
||||
if keyBytes2, ok := privateKey.([]byte); ok {
|
||||
keyBytes = keyBytes2
|
||||
} else {
|
||||
keyBytes, _ = ioutil.ReadFile(privateKey.(string))
|
||||
}
|
||||
pubPem, _ := pem.Decode(keyBytes)
|
||||
if pubPem == nil {
|
||||
keyBytes, _ = base64.StdEncoding.DecodeString(string(keyBytes))
|
||||
} else {
|
||||
keyBytes = pubPem.Bytes
|
||||
}
|
||||
a = &API{
|
||||
appID: appID,
|
||||
client: &http.Client{Timeout: curConfig.ClientTimeout},
|
||||
config: &curConfig,
|
||||
}
|
||||
if privateKey, err := x509.ParsePKCS1PrivateKey(keyBytes); err == nil {
|
||||
a.privateKey = privateKey
|
||||
} else {
|
||||
baseapi.SugarLogger.Errorf("alpayapi.New failed with err:%v", err)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *API) GetAppID() string {
|
||||
return a.appID
|
||||
}
|
||||
|
||||
func (a *API) signParams(params map[string]interface{}) (sign string) {
|
||||
keys := make([]string, 0)
|
||||
for k := range params {
|
||||
if k != signKey {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
strList := make([]string, len(keys))
|
||||
for index, k := range keys {
|
||||
strList[index] = fmt.Sprintf("%s=%v", k, params[k])
|
||||
}
|
||||
|
||||
finalStr := strings.Join(strList, "&")
|
||||
// baseapi.SugarLogger.Debugf("finalStr:%s", finalStr)
|
||||
d := sha256.Sum256([]byte(finalStr))
|
||||
signature, _ := rsa.SignPKCS1v15(rand.Reader, a.privateKey, crypto.SHA256, d[:])
|
||||
sign = base64.StdEncoding.EncodeToString(signature)
|
||||
return sign
|
||||
}
|
||||
|
||||
func method2ResponseKey(method string) (responseKey string) {
|
||||
responseKey = strings.Join(append(strings.Split(method, "."), "response"), "_")
|
||||
return responseKey
|
||||
}
|
||||
|
||||
func (a *API) AccessAPI(method string, params, bizContent map[string]interface{}) (retVal map[string]interface{}, err error) {
|
||||
params = utils.MergeMaps(map[string]interface{}{
|
||||
"app_id": a.GetAppID(),
|
||||
"method": method,
|
||||
"format": "JSON",
|
||||
// "return_url"
|
||||
"charset": "utf-8",
|
||||
"sign_type": "RSA2",
|
||||
"version": "1.0",
|
||||
}, params)
|
||||
if len(bizContent) > 0 {
|
||||
params["biz_content"] = string(utils.MustMarshal(bizContent))
|
||||
}
|
||||
|
||||
err = platformapi.AccessPlatformAPIWithRetry(a.client,
|
||||
func() *http.Request {
|
||||
params["timestamp"] = utils.GetCurTimeStr()
|
||||
params[signKey] = a.signParams(params)
|
||||
fullURL := utils.GenerateGetURL(prodURL, "", params)
|
||||
request, _ := http.NewRequest(http.MethodGet, fullURL, nil)
|
||||
// request, _ := http.NewRequest(http.MethodPost, prodURL, bytes.NewReader(utils.MustMarshal(params)))
|
||||
return request
|
||||
},
|
||||
a.config,
|
||||
func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
|
||||
if jsonResult1 == nil {
|
||||
return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
|
||||
}
|
||||
if errorResponse, ok := jsonResult1["error_response"].(map[string]interface{}); ok {
|
||||
retVal = errorResponse
|
||||
} else {
|
||||
retVal, _ = jsonResult1[method2ResponseKey(method)].(map[string]interface{})
|
||||
if retVal == nil {
|
||||
return platformapi.ErrLevelGeneralFail, fmt.Errorf("结果格式异常")
|
||||
}
|
||||
}
|
||||
|
||||
errCode := utils.Interface2String(retVal["code"])
|
||||
if errCode != CommonErrSuccess && errCode != "" {
|
||||
err = utils.NewErrorCode(utils.Interface2String(retVal["msg"]), errCode)
|
||||
if commonCanRetryErrMap[errCode] {
|
||||
return platformapi.ErrLevelRecoverableErr, err
|
||||
}
|
||||
errLevel = platformapi.ErrLevelGeneralFail
|
||||
}
|
||||
subErrCode := utils.Interface2String(retVal["sub_code"])
|
||||
if subErrCode != "" {
|
||||
return platformapi.ErrLevelCodeIsNotOK, utils.NewErrorCode(utils.Interface2String(retVal["sub_msg"]), subErrCode)
|
||||
} else if err != nil {
|
||||
return errLevel, err
|
||||
}
|
||||
|
||||
return platformapi.ErrLevelSuccess, nil
|
||||
})
|
||||
return retVal, err
|
||||
}
|
||||
22
platformapi/alipayapi/alipayapi_test.go
Normal file
22
platformapi/alipayapi/alipayapi_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package alipayapi
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
api *API
|
||||
sugarLogger *zap.SugaredLogger
|
||||
)
|
||||
|
||||
func init() {
|
||||
logger, _ := zap.NewDevelopment()
|
||||
sugarLogger = logger.Sugar()
|
||||
baseapi.Init(sugarLogger)
|
||||
|
||||
api = New("2019110769024042", []byte(`
|
||||
MIIEpAIBAAKCAQEAnsbBe0lrK5c4/xhb7ZLDWjGRWmIaj7HyV4LQ9X4EcTV5I5IKLezH1YaNLXytD/VXc5NsJp9IDTFLyOYdXee8lJxAeSQbuBBy1+xLd6qK2JQdVUGP3RZ0pAwVZSc9m0JKj5pYEeA2lvgh4NhSfGEw4BdZacpiDjFWkrQYl+RZkl/eIH2w7sA4wXs/hLSnPiG0VRtLtyYzfGCQdEJNjP5PA6V6CJTd68qTytLnpuaTuVxIYHGGSNd08694b1wOuGpFv6YK+mZkfaGkFoEpp3gUhEQ05duKjNBY71f0ez/Fym7GQYdHNXlsIvCmGQzklkfvQkHj7+MvPpsME4PkqQjRgwIDAQABAoIBAHLzwzDXPtgYbBOEN0oRb43lRS8Cx+gxFRt2goK58c1kwYeXO/dz7loRSDUehs1++wmaOjrcJvYmMpAIykoqdMXDOop6MfdZMUxSr3C78DpNQc9v4BBZKal7diH9/wRhQkolnI2UnvE+RIjdFRsn4pLbVMja1ZMg9WTRLt0JXjAyQZus9pADWADK12OSEIHBvz7/+kiFpq0aM+KPMElQG/mSDg1ESmzfYEbXYmPoiMwU+9frtnprrNdG9h143pb7mdzbXTt/8DbmpFgCfKL7ItpsC3VcZFDsj1Sd3gewrU+FLifH6oPGHTiwEoHNIn5m0RdPVEFoQnQxZnqxzDVBVYECgYEA5zCnBZBLotumxVdIRwncAXKEV5nMHJV2NKVmAc20vHJTsmI4/kA9B2Bjx9jzwwctBzFp1pIadccSbFO48Z5Hn0NdwYOnqba9W50n6R7wO3SnqQuCaoyTAvfkcjrZjZqq88Opa9tKGjD69MFFa+mnK7O7s8B7X6hCa/h80s0zDmkCgYEAr9C5yACb0haAp1WkuYf0B2TyPIofhYOXsjHcJqref+mDHgXSqPntYsXl/RVpJJSAGXJ/CnPd+jHQ0Fis2LuNpZ6ntYzcGoTQtnXx8BdQsnyEjyAvzxWv2JJV12zoSTEW7HL078qqEgbzmort6A8edRiv0kIoNf054QAtv/C9OwsCgYBPATVSlWkDkoR/U8CDZj8kz3miZhB2hC0M+KjPXPiynW80upQ3bsRsTOhMVzuWHlGo7533kZ4xOYJ2OnYtO6XGK0NS6ibVvHkhYadN5yC5cLgK8L/0oW1rykLrNmk6FuzsuKShEyNTqAFauuF6azKRoK44U0LWAa4RL62YbD9SYQKBgQCD0Qp5WXt6WES9MQj/0V607IpxuV1IzRC/GYLlutZ3MKyNpe/7oObKV3XH+nWKZ4xjh+SYAac8Hn1guBtfo77fncQ/6gxcFZgmNOfgCpsGNzVr2cX+jVP6HD0f9xdxSMzXGplp75jzSyL5i5AznKJJSOkJy3A6ilEK0Qd8ERLPYQKBgQC/QSV0c71x3nYz3koOQv7s36i5R0jeHnadSMyGUlrYmn/TaXj0KaUaEYRcgJyWB/dxy3cde8EKlqg0q80Zc6ExkZKhpO6k1lj9Bta+l+KAyVFroYDIy/b0Wukr2qV1mkMK0FQ0X2hVbv6TJBq5RMNeYAy1shOeWIwaS5muSYm4Ig==
|
||||
`))
|
||||
}
|
||||
29
platformapi/alipayapi/member.go
Normal file
29
platformapi/alipayapi/member.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package alipayapi
|
||||
|
||||
import "git.rosy.net.cn/baseapi/utils"
|
||||
|
||||
type UserInfo struct {
|
||||
Code string `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Avatar string `json:"avatar"`
|
||||
City string `json:"city"`
|
||||
Gender string `json:"gender"`
|
||||
IsCertified string `json:"is_certified"`
|
||||
IsStudentCertified string `json:"is_student_certified"`
|
||||
NickName string `json:"nick_name"`
|
||||
Province string `json:"province"`
|
||||
UserID string `json:"user_id"`
|
||||
UserStatus string `json:"user_status"`
|
||||
UserType string `json:"user_type"`
|
||||
}
|
||||
|
||||
func (a *API) UserInfoShare(accessToken string) (userInfo *UserInfo, err error) {
|
||||
params := map[string]interface{}{
|
||||
"auth_token": accessToken,
|
||||
}
|
||||
retVal, err := a.AccessAPI("alipay.user.info.share", params, nil)
|
||||
if err == nil {
|
||||
err = utils.Map2StructByJson(retVal, &userInfo, false)
|
||||
}
|
||||
return userInfo, err
|
||||
}
|
||||
11
platformapi/alipayapi/member_test.go
Normal file
11
platformapi/alipayapi/member_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package alipayapi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUserInfoShare(t *testing.T) {
|
||||
result, err := api.UserInfoShare("authusrBbe61f93632264c44b4e2f5da25267C98")
|
||||
t.Log(err)
|
||||
t.Log(result)
|
||||
}
|
||||
49
platformapi/alipayapi/utils.go
Normal file
49
platformapi/alipayapi/utils.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package alipayapi
|
||||
|
||||
import "git.rosy.net.cn/baseapi/utils"
|
||||
|
||||
const (
|
||||
GrantTypeCode = "authorization_code"
|
||||
GrantTypeRefresh = "refresh_token"
|
||||
)
|
||||
|
||||
type TokenInfo struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
AlipayUserID string `json:"alipay_user_id"`
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
ReExpiresIn int64 `json:"re_expires_in"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
UserID string `json:"user_id"`
|
||||
}
|
||||
|
||||
func (a *API) UserInfoAuth(scopes []string, state string) (retVal map[string]interface{}, err error) {
|
||||
retVal, err = a.AccessAPI("alipay.user.info.auth", nil, map[string]interface{}{
|
||||
"scopes": scopes,
|
||||
"state": state,
|
||||
})
|
||||
return retVal, err
|
||||
}
|
||||
|
||||
func (a *API) AuthTokenAppQuery(appAuthToken string) (retVal map[string]interface{}, err error) {
|
||||
retVal, err = a.AccessAPI("alipay.open.auth.token.app.query", nil, map[string]interface{}{
|
||||
"app_auth_token": appAuthToken,
|
||||
})
|
||||
return retVal, err
|
||||
}
|
||||
|
||||
func (a *API) SystemAuthToken(grantType, code, refreshToken string) (tokenInfo *TokenInfo, err error) {
|
||||
params := map[string]interface{}{
|
||||
"grant_type": grantType,
|
||||
}
|
||||
if code != "" {
|
||||
params["code"] = code
|
||||
}
|
||||
if refreshToken != "" {
|
||||
params["refresh_token"] = refreshToken
|
||||
}
|
||||
retVal, err := a.AccessAPI("alipay.system.oauth.token", params, nil)
|
||||
if err == nil {
|
||||
err = utils.Map2StructByJson(retVal, &tokenInfo, false)
|
||||
}
|
||||
return tokenInfo, err
|
||||
}
|
||||
11
platformapi/alipayapi/utils_test.go
Normal file
11
platformapi/alipayapi/utils_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package alipayapi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSystemAuthToken(t *testing.T) {
|
||||
result, err := api.SystemAuthToken(GrantTypeCode, "c91ed60bebae473b9507a08e0c86NE98", "")
|
||||
t.Log(err)
|
||||
t.Log(result)
|
||||
}
|
||||
Reference in New Issue
Block a user