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 } type KeyMobile struct { Code string `json:"code"` Msg string `json:"msg"` Mobile string `json:"mobile"` } 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{}, isPost bool) (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 { var request *http.Request params["timestamp"] = utils.GetCurTimeStr() params[signKey] = a.signParams(params) fullURL := utils.GenerateGetURL(prodURL, "", params) if isPost { request, _ = http.NewRequest(http.MethodPost, prodURL, strings.NewReader(utils.Map2URLValues(params).Encode())) } else { request, _ = http.NewRequest(http.MethodGet, fullURL, nil) } 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 }