package payment
import ( "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/pem" "fmt" "net/url" "sort" "strings"
"github.com/go-resty/resty/v2" )
type Client struct { AppID string PrivateKey *rsa.PrivateKey AlipayPublicKey *rsa.PublicKey GatewayURL string NotifyURL string ReturnURL string Sandbox bool }
func NewClient(appID, privateKeyPEM, alipayPublicKeyPEM string, sandbox bool) (*Client, error) { block, _ := pem.Decode([]byte(privateKeyPEM)) if block == nil { return nil, fmt.Errorf("私钥格式错误") } privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return nil, fmt.Errorf("解析私钥失败: %w", err) }
block, _ = pem.Decode([]byte(alipayPublicKeyPEM)) if block == nil { return nil, fmt.Errorf("公钥格式错误") } pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, fmt.Errorf("解析公钥失败: %w", err) } publicKey, ok := pubInterface.(*rsa.PublicKey) if !ok { return nil, fmt.Errorf("不是有效的 RSA 公钥") }
gateway := "https://openapi.alipay.com/gateway.do" if sandbox { gateway = "https://openapi-sandbox.dl.alipaydev.com/gateway.do" }
return &Client{ AppID: appID, PrivateKey: privateKey, AlipayPublicKey: publicKey, GatewayURL: gateway, Sandbox: sandbox, }, nil }
type CreateOrderRequest struct { OutTradeNo string Subject string TotalAmount float64 }
type CreateOrderResponse struct { Code string `json:"code"` Msg string `json:"msg"` QRCode string `json:"qr_code"` }
func (c *Client) CreateOrder(req *CreateOrderRequest) (*CreateOrderResponse, error) { bizContent := fmt.Sprintf(`{ "out_trade_no": "%s", "total_amount": "%.2f", "subject": "%s", "product_code": "FACE_TO_FACE_PAYMENT" }`, req.OutTradeNo, req.TotalAmount, req.Subject)
params := map[string]string{ "app_id": c.AppID, "method": "alipay.trade.precreate", "format": "JSON", "charset": "utf-8", "sign_type": "RSA2", "timestamp": time.Now().Format("2006-01-02 15:04:05"), "version": "1.0", "notify_url": c.NotifyURL, "biz_content": bizContent, }
sign, err := c.sign(params) if err != nil { return nil, err } params["sign"] = sign
client := resty.New() resp, err := client.R(). SetFormData(params). Post(c.GatewayURL) if err != nil { return nil, fmt.Errorf("请求支付宝失败: %w", err) }
return parseResponse(resp.Body()) }
func (c *Client) sign(params map[string]string) (string, error) { filtered := make(map[string]string) for k, v := range params { if k != "sign" && v != "" { filtered[k] = v } }
var keys []string for k := range filtered { keys = append(keys, k) } sort.Strings(keys)
var parts []string for _, k := range keys { parts = append(parts, fmt.Sprintf("%s=%s", k, filtered[k])) } content := strings.Join(parts, "&")
hash := sha256.Sum256([]byte(content)) signature, err := rsa.SignPKCS1v15( rand.Reader, c.PrivateKey, crypto.SHA256, hash[:], ) if err != nil { return "", err }
return base64.StdEncoding.EncodeToString(signature), nil }
|
评论
0 条评论