Golang 支付宝沙盒环境创建支付链接

46 阅读4分钟

参考

github.com/go-pay/gopa…

沙盒地址

open.alipay.com/develop/san…

代码示例

package main

import (
    "context"
    "fmt"
    "github.com/go-pay/gopay"
    "github.com/go-pay/gopay/alipay"
    "github.com/shopspring/decimal"
    "log"
    "net/http"
    "time"
)

const (
    kAppId        = "9021000145654333"
    kPrivateKey   = `MIIEowIBAAKCAQEAiOqxZvy9DJwQSSL6hYRiJL8MBL8qi5ubFjz/uPHvEblkeH2UICHtHA84rXMyfZopG032KIeaEcYHN6bmJQrOtd8ss+2B3SPVqoX8PzKF80Qp8bS0T/RQkVnljfCOZqDJEaTMZxg9DnRnshB3vuAoQYhQnHW9nrzlYX8oJbn/LImCjnF9PVNcsrMjXLPEuu5nAQq17eKd4bmdDoVwPU9scdyHn3By35uWzoztAHkLnd+wL2VUl/bYgRbWgqzo/J8EAc8iJZ8tnOxa875Uo08Y9aKJgilTvn29h+N5FonsDowECNz5aBkqaa5slzfEqoH68yLD8lN3f9IVTgFr3qscIwIDAQABAoIBACB3rwviAR6dX4tIXSoOQ4u9NidmAbvLcZJrcqSz0mJcmUOtW9Pm16hgv6r5904roXXVHftsdI+kaw+6Zzb/ghdEzf/44WPEXKWsUi3WWEdgsaWcmWmgYP6m17W6Brq1hBjIAKSt7cSgu/pQx2SfGUy4+4yuqwGaf7aIBcg0fBteP/hP+W3VAK5niRgizMke73jrmvhrHBjWDNadqvIHmuefwliGyPMdiJlWhDc3sLiEwBcD8nHbeDUO2ogLKUyT3ZyyfgrKmZdH66q7boM4BStZCOfzl5eEWgpkovUzyKpolxcHc3sKPgoN24TiAqqA9o4pF/SnSShMO3TbIjIkwLkCgYEA+Y1FAcPsA3gTmY859CWFZ+7Ahd/TYbCSuegdQSbLL/JWdapNWm+bOT2c6aWp49G6JaUDxauN3xans2GidjymvlOnGMv9BsMFQabbgMAyq8PZHIQ3mew/BG+wmOOkIEEwvOS6xQa+XuaKYJiDX3A6z0utZa+tcdsiK63iMor+L2cCgYEAjHReBQG9bPkPENVmoCOfUCbYpOktF2Sovu6wPfvv71p3S3Vj6DlWaAdCDcUPCXwIaF0llzIoAWVxIx1B1v1cPbLEWVHZCDKdkPI8ZsMhCE97xRqZKRM67NbKobRgTf+C4tzMbHHdXyJqaH4cdcHzvG3s9QE/nYK7FcU9y9APg+UCgYAoAyqpWcyh0cLywDA+oTV+sJ1V2cl+xF0CSeFG7P1vNp/+RcwZUAPHbcWXAOT4jkwWdRrhutwIhQ1miaUO6DKkGa2VID/1sZdKK4LOZgK537EUDW8M0wBXqcDbAsTyFwHFh7camDY16SkOy+VkjA4h5rRqtXxN4SDdeAfoqriOyQKBgEkLX7YU4JGiuT4U7WlM/VZuzW5+QdAPrl6VvZyOwIpnLvFUXJ/x6cjyNVz55ccV9T1pa9pE4F9RhVQwrAQGAYmoxcp58Xf88srl35nF0xECXSj1mz+sW5G+ZuVP7GHEByq6j6m6UdlfCZ3MMDrhokOVm40TmCTvTyKUUH4Fd3wtAoGBAIbfymY4QDM9seiSoOg9gSN85Ak1VbjQfwOId0JhcRN3GBq7GSyIrv+Lcs97+hkxa8DH+8fcapjwPBQcBsbKRfonasSILXm1y/N8ErlIjQjoaVJRAftRBHKcsQoTzo3R8erSyMbHiA98vNeSmP9KyRGBRDGjEsjVMh2Ye53KU3lZ`
    kServerPort   = "8005"
    kServerDomain = "https://这里写你的服务地址/server"
    pubKey        = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkLCI+JgynJE6Nl2zH+8eXZLIsQiUBm+AgWHWv4Jc94k8jor7v7hc3yskVOOWcRfF2DpCBG/+S55VNOOnIVOK4aYfzuI3cYbnVr0ttgrPZ+2Zb3HbOW2O1i4P+wZQ14mcv2p/jbt2Jr+RgrTfaRQHq4HZUbZGAEmJVa0RSdJHuGXyFdq+KPOGcSy2BCPxrAQEBKhNJabmfsiPMe7VsJPxb577eFaDyDKvs1ymyETkKTSj9b9qLFYm3ZkPz6eHYLbmJofJRp19StaKLZBzFZt1BTE7CvnH+GTsPGjwXvtZ8mP5XdvNCNrrq7h6QKfXoBBoRMfjZHvK6P9NCXD932W6QQIDAQAB`
)

var client *AlipayService

func main() {
    conf := Config{
       Appid:      kAppId,
       MchId:      "2088721060395791",
       PrivateKey: kPrivateKey,
       NotifyUrl:  kServerDomain + "/alipay/notify",
       RefundUrl:  kServerDomain + "/alipay/refund",
       ReturnUrl:  kServerDomain + "/alipay/callback",
       PublicKey:  pubKey,
       QuitUrl:    "",
    }
    client = NewAlipayService(context.Background(), conf)

    http.HandleFunc("/v1/server/alipay/pay", pay)
    //http.HandleFunc("/v1/server/alipay/callback", callback)
    //http.HandleFunc("/v1/server/alipay/notify", notify)
    http.HandleFunc("/v1/server/alipay/refund", refund)
    log.Println(`启动成功`)
    http.ListenAndServe(":"+kServerPort, nil)
}

func pay(writer http.ResponseWriter, request *http.Request) {
    var tradeNo = fmt.Sprintf("%d", time.Now().UnixNano())
    // 获取 GET 请求中的 tradeNo 参数
    tradeNoParam := request.URL.Query().Get("tradeNo")
    if tradeNoParam != "" {
       tradeNo = tradeNoParam // 如果 tradeNo 参数非空,则使用其值
    }
    charge := &Charge{
       MoneyFee: 1000, //10yuan
       TradeNum: tradeNo,
       Describe: "测试",
    }
    url, err := client.PcPay(charge)
    if err != nil {
       writer.WriteHeader(http.StatusBadRequest)
       writer.Write([]byte(fmt.Sprintf("订单 %s 创建失败", tradeNoParam)))
       return
    }
    writer.WriteHeader(http.StatusOK)
    writer.Write([]byte(url))
}

func refund(writer http.ResponseWriter, request *http.Request) {
    refunds := &RefundCharge{
       TradeNum:     "1741690269602282000",
       MoneyFee:     500,
       OutRefundNo:  "1",
       RefundReason: "测试退款",
    }
    err := client.Refund(refunds)
    if err != nil {
       writer.WriteHeader(http.StatusBadRequest)
       writer.Write([]byte(fmt.Sprintf("订单 %s 退款失败", refunds.OutRefundNo)))
       return
    }
    writer.WriteHeader(http.StatusOK)
    writer.Write([]byte(`success`))
}

type Config struct {
    Appid      string
    MchId      string
    PrivateKey string
    NotifyUrl  string
    RefundUrl  string
    ReturnUrl  string
    PublicKey  string
    QuitUrl    string
}

type AlipayService struct {
    ctx    context.Context
    config Config
    alipay *alipay.Client
}

func NewAlipayService(ctx context.Context, config Config) *AlipayService {
    alipayCli, err := alipay.NewClient(config.Appid, config.PrivateKey, false)
    if err != nil {
       panic(err)
    }
    alipayCli.DebugSwitch = gopay.DebugOn // 开启debug
    alipayCli.SetCharset(alipay.UTF8).SetSignType(alipay.RSA2)

    return &AlipayService{
       ctx:    ctx,
       config: config,
       alipay: alipayCli,
    }
}

// 添加 Charge 结构体定义
type Charge struct {
    MoneyFee int64  // 金额(单位:分)
    TradeNum string // 交易号
    Describe string // 商品描述
}

// PcPay PC端支付
func (a *AlipayService) PcPay(charge *Charge) (string, error) {
    a.alipay = a.alipay.SetReturnUrl(a.config.ReturnUrl).SetNotifyUrl(a.config.NotifyUrl)
    // 生成单号
    amount := decimal.NewFromInt(charge.MoneyFee).DivRound(decimal.NewFromInt(100), 2).String()
    // 构造参数
    bm := make(gopay.BodyMap)
    bm.Set("subject", charge.Describe).
       Set("out_trade_no", charge.TradeNum).
       Set("total_amount", amount).
       Set("qr_pay_mode", 4).
       Set("qrcode_width", 120)
    // 发起支付
    return a.alipay.TradePagePay(a.ctx, bm)
}

type RefundCharge struct {
    TradeNum     string `json:"tradeNum,omitempty"`
    MoneyFee     int64  `json:"MoneyFee,omitempty"`
    OutRefundNo  string `json:"out_refund_no,omitempty"`
    RefundReason string `json:"refund_reason,omitempty"`
}

func (a *AlipayService) Refund(charge *RefundCharge) error {
    amount := decimal.NewFromInt(charge.MoneyFee).DivRound(decimal.NewFromInt(100), 2).String()
    // 构造参数
    bm := make(gopay.BodyMap)
    bm.Set("out_trade_no", charge.TradeNum).
       Set("refund_reason", charge.RefundReason).
       Set("out_request_no", charge.OutRefundNo).
       Set("refund_amount", amount)
    // 发起支付
    res, err := a.alipay.TradeRefund(a.ctx, bm)
    //TODO 处理错误信息,处理结果
    log.Println(`TradeRefund:`, res, err)
    return err
}

// AliWebPayResult 支付宝支付结果回调
type AliWebPayResult struct {
    OutTradeNo  string `json:"out_trade_no"`
    TradeStatus string `json:"trade_status"`
}

// GetNotifyResult  解析支付回调
func (a *AlipayService) GetNotifyResult(r *http.Request) (result *AliWebPayResult, err error) {
    // 解析异步通知的参数
    notifyReq, err := alipay.ParseNotifyToBodyMap(r)
    if err != nil {
       // TODO 处理错误信息
       return
    }

    // 支付宝异步通知验签(公钥模式)
    _, err = alipay.VerifySign(a.config.PublicKey, notifyReq)
    if err != nil {
       // TODO 处理错误信息
       return
    }
    if err = notifyReq.Unmarshal(result); err != nil {
       // TODO 处理错误信息
       return
    }
    return
}