| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package alipayclient
- import (
- "context"
- "crypto"
- "crypto/rand"
- "crypto/rsa"
- "crypto/sha256"
- "encoding/base64"
- "fmt"
- "net/http"
- "strings"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/httputils"
- "yunion.io/x/onecloud/pkg/util/seclib2"
- )
- const (
- AlipayGatewayUrl = "https://openapi.alipay.com/gateway.do"
- AlipayFormat = "json"
- AlipayCharset = "UTF-8"
- // AlipayCharsetGBK = "GBK"
- AlipaySignType = "RSA2"
- AlipayVersion = "1.0"
- )
- type SAlipayClient struct {
- url string
- appId string
- appPrivateKey *rsa.PrivateKey
- format string // always json
- charset string // always utf-8
- alipayPubKey string
- signType string // always RSA2
- version string
- httpClient *http.Client
- isDebug bool
- }
- func NewDefaultAlipayClient(appId string, appPrivateKey string, alipayPubKey string, isDebug bool) (*SAlipayClient, error) {
- return NewAlipayClient(AlipayGatewayUrl, appId, appPrivateKey, AlipayFormat, AlipayCharset, alipayPubKey, AlipaySignType, isDebug)
- }
- func NewAlipayClient(url string, appId string, appPrivateKey string, format string, charset string, alipayPubKey string, signType string, isDebug bool) (*SAlipayClient, error) {
- privKey, err := seclib2.DecodePrivateKey([]byte(appPrivateKey))
- if err != nil {
- return nil, errors.Wrap(err, "Invalid appPrivateKey")
- }
- httpClient := httputils.GetClient(true, time.Second*15)
- httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
- return http.ErrUseLastResponse
- }
- cli := &SAlipayClient{
- url: url,
- appId: appId,
- appPrivateKey: privKey,
- format: format,
- charset: charset,
- alipayPubKey: alipayPubKey,
- signType: signType,
- version: AlipayVersion,
- httpClient: httpClient,
- isDebug: isDebug,
- }
- return cli, nil
- }
- type SCommonRequestParameters struct {
- AppId string `json:"app_id"`
- Method string `json:"method"`
- Format string `json:"format"`
- Charset string `json:"charset"`
- SignType string `json:"sign_type"`
- Timestamp string `json:"timestamp"`
- Version string `json:"version"`
- AppAuthToken string `json:"app_auth_token"`
- }
- func (c *SAlipayClient) getCommonParams(method string) SCommonRequestParameters {
- return SCommonRequestParameters{
- AppId: c.appId,
- Method: method,
- Format: c.format,
- Charset: c.charset,
- SignType: c.signType,
- Timestamp: time.Now().Format("2006-01-02 15:04:05"),
- Version: c.version,
- }
- }
- func (c *SAlipayClient) execute(ctx context.Context, method string, params map[string]string) (jsonutils.JSONObject, error) {
- req := jsonutils.Marshal(params).(*jsonutils.JSONDict)
- req.Update(jsonutils.Marshal(c.getCommonParams(method)))
- signedReq, err := c.sign(req)
- if err != nil {
- return nil, errors.Wrap(err, "sign")
- }
- urlstr := fmt.Sprintf("%s?%s", c.url, signedReq.QueryString())
- _, resp, err := httputils.JSONRequest(c.httpClient, ctx, httputils.GET, urlstr, nil, nil, c.isDebug)
- if err != nil {
- return nil, errors.Wrap(err, "JSONRequest")
- }
- return resp, nil
- }
- func signedString(cont *jsonutils.JSONDict) string {
- keys := cont.SortedKeys()
- segs := make([]string, len(keys))
- for i, key := range keys {
- val, _ := cont.GetString(key)
- segs[i] = fmt.Sprintf("%s=%s", key, val)
- }
- return strings.Join(segs, "&")
- }
- func (c *SAlipayClient) sign(request *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
- content := request.CopyExcludes("sign")
- s := sha256.New()
- _, err := s.Write([]byte(signedString(content)))
- if err != nil {
- return nil, errors.Wrap(err, "sha256.Write")
- }
- signByte, err := c.appPrivateKey.Sign(rand.Reader, s.Sum(nil), crypto.SHA256)
- if err != nil {
- return nil, errors.Wrap(err, "privateKey.Sign")
- }
- signStr := base64.StdEncoding.EncodeToString(signByte)
- content.Set("sign", jsonutils.NewString(signStr))
- return content, nil
- }
- type SAlipaySystemOAuthTokenResponse struct {
- AccessToken string `json:"access_token"`
- AlipayUserId string `json:"alipay_user_id"`
- ExpiresIn int `json:"expires_in"`
- ReExpiresIn int `json:"re_expires_in"`
- RefreshToken string `json:"refresh_token"`
- UserId string `json:"user_id"`
- }
- // {"alipay_system_oauth_token_response":{"access_token":"authusrB9ee9ecc8105e4fc4869b41e8a470dX90","alipay_user_id":"20881023391875409149385062614990","expires_in":1296000,
- //
- // "re_expires_in":2592000,"refresh_token":"authusrBe1a9c0bdf8d344e786ee57f2df9d6E90","user_id":"2088002723447908"},
- // "sign":"rXGE/YX12UrmkEae9jw9WD7B2dS13Hs0r+EnqWwKdERGsUiFmP..."}
- func (c *SAlipayClient) GetOAuthToken(ctx context.Context, code string) (*SAlipaySystemOAuthTokenResponse, error) {
- resp, err := c.execute(ctx, "alipay.system.oauth.token", map[string]string{
- "code": code,
- "grant_type": "authorization_code",
- })
- if err != nil {
- return nil, errors.Wrap(err, "Execute")
- }
- tokenResp := SAlipaySystemOAuthTokenResponse{}
- err = resp.Unmarshal(&tokenResp, "alipay_system_oauth_token_response")
- if err != nil {
- return nil, errors.Wrap(err, "unmarshal fail")
- }
- return &tokenResp, nil
- }
- // {"alipay_user_info_share_response":{"code":"10000","msg":"Success","city":"北京市","gender":"m","nick_name":"剑","province":"北京","user_id":"2088002723447908"},
- //
- // "sign":"WZ+uBloiHvQYOOxq02aS/Y4MEoZf5+ANBnt1OKQ9Z8hOPmQsw=="}
- func (c *SAlipayClient) GetUserInfo(ctx context.Context, authToken string) (map[string]string, error) {
- resp, err := c.execute(ctx, "alipay.user.info.share", map[string]string{
- "auth_token": authToken,
- })
- if err != nil {
- return nil, errors.Wrap(err, "Execute")
- }
- ret := make(map[string]string)
- err = resp.Unmarshal(&ret, "alipay_user_info_share_response")
- if err != nil {
- return nil, errors.Wrap(err, "Unmarshal map")
- }
- return ret, nil
- }
|