mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-01-24 11:36:27 +08:00
196 lines
4.8 KiB
Go
196 lines
4.8 KiB
Go
|
package alipay
|
|||
|
|
|||
|
import (
|
|||
|
"context"
|
|||
|
"github.com/go-pay/gopay"
|
|||
|
"github.com/go-pay/gopay/alipay"
|
|||
|
"github.com/gogf/gf/v2/errors/gerror"
|
|||
|
"github.com/gogf/gf/v2/net/ghttp"
|
|||
|
"github.com/gogf/gf/v2/os/gfile"
|
|||
|
"github.com/gogf/gf/v2/util/gconv"
|
|||
|
"hotgo/internal/consts"
|
|||
|
"hotgo/internal/model"
|
|||
|
"hotgo/internal/model/input/payin"
|
|||
|
)
|
|||
|
|
|||
|
func New(config *model.PayConfig) *aliPay {
|
|||
|
return &aliPay{
|
|||
|
config: config,
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
type aliPay struct {
|
|||
|
config *model.PayConfig
|
|||
|
}
|
|||
|
|
|||
|
// Refund 订单退款
|
|||
|
func (h *aliPay) Refund(ctx context.Context, in payin.RefundInp) (res *payin.RefundModel, err error) {
|
|||
|
client, err := GetClient(h.config)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
bm := make(gopay.BodyMap)
|
|||
|
bm.Set("out_trade_no", in.Pay.OutTradeNo).
|
|||
|
Set("refund_amount", in.RefundMoney).
|
|||
|
Set("out_request_no", in.RefundSn).
|
|||
|
Set("refund_reason", in.Remark)
|
|||
|
|
|||
|
refund, err := client.TradeRefund(ctx, bm)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if refund.Response.FundChange != "Y" {
|
|||
|
err = gerror.New("支付宝本次退款未发生资金变化!")
|
|||
|
return
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// Notify 异步通知
|
|||
|
func (h *aliPay) Notify(ctx context.Context, in payin.NotifyInp) (res *payin.NotifyModel, err error) {
|
|||
|
notifyReq, err := alipay.ParseNotifyToBodyMap(ghttp.RequestFromCtx(ctx).Request)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// 支付宝异步通知验签(公钥证书模式)
|
|||
|
ok, err := alipay.VerifySignWithCert(h.config.AliPayCertPublicKeyRSA2, notifyReq)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if !ok {
|
|||
|
err = gerror.New("支付宝验签不通过!")
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
var notify *NotifyRequest
|
|||
|
if err = gconv.Scan(notifyReq, ¬ify); err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if notify == nil {
|
|||
|
err = gerror.New("解析订单参数失败!")
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if notify.TradeStatus != "TRADE_SUCCESS" {
|
|||
|
err = gerror.New("非交易支付成功状态,无需处理!")
|
|||
|
// 这里如果相对非交易支付成功状态进行处理,可自行调整此处逻辑
|
|||
|
// ...
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if notify.OutTradeNo == "" {
|
|||
|
err = gerror.New("订单中没有找到商户单号!")
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
res = new(payin.NotifyModel)
|
|||
|
res.TransactionId = notify.TradeNo
|
|||
|
res.OutTradeNo = notify.OutTradeNo
|
|||
|
res.PayAt = notify.GmtPayment
|
|||
|
res.ActualAmount = gconv.Float64(notify.ReceiptAmount)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// CreateOrder 创建订单
|
|||
|
func (h *aliPay) CreateOrder(ctx context.Context, in payin.CreateOrderInp) (res *payin.CreateOrderModel, err error) {
|
|||
|
client, err := GetClient(h.config)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
// 设置回调地址
|
|||
|
client.SetReturnUrl(in.Pay.ReturnUrl).SetNotifyUrl(in.Pay.NotifyUrl)
|
|||
|
|
|||
|
switch in.Pay.TradeType {
|
|||
|
case consts.TradeTypeAliScan, consts.TradeTypeAliWeb:
|
|||
|
return h.scan(ctx, in)
|
|||
|
case consts.TradeTypeAliWap:
|
|||
|
return h.wap(ctx, in)
|
|||
|
default:
|
|||
|
err = gerror.Newf("暂未支持的交易方式:%v", in.Pay.TradeType)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func GetClient(config *model.PayConfig) (client *alipay.Client, err error) {
|
|||
|
client, err = alipay.NewClient(config.AliPayAppId, gfile.GetContents(config.AliPayPrivateKey), true)
|
|||
|
if err != nil {
|
|||
|
err = gerror.Newf("创建支付宝客户端失败:%+v", err.Error())
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// 打开Debug开关,输出日志,默认关闭
|
|||
|
if config.Debug {
|
|||
|
client.DebugSwitch = gopay.DebugOn
|
|||
|
}
|
|||
|
|
|||
|
client.SetLocation(alipay.LocationShanghai) // 设置时区,不设置或出错均为默认服务器时间
|
|||
|
|
|||
|
// 证书路径
|
|||
|
err = client.SetCertSnByPath(config.AliPayAppCertPublicKey, config.AliPayRootCert, config.AliPayCertPublicKeyRSA2)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// scan 扫码支付
|
|||
|
func (h *aliPay) scan(ctx context.Context, in payin.CreateOrderInp) (res *payin.CreateOrderModel, err error) {
|
|||
|
client, err := GetClient(h.config)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
// 设置回调地址
|
|||
|
client.SetReturnUrl(in.Pay.ReturnUrl).SetNotifyUrl(in.Pay.NotifyUrl)
|
|||
|
|
|||
|
bm := make(gopay.BodyMap)
|
|||
|
bm.Set("subject", in.Pay.Subject).
|
|||
|
Set("out_trade_no", in.Pay.OutTradeNo).
|
|||
|
Set("total_amount", in.Pay.PayAmount)
|
|||
|
|
|||
|
payUrl, err := client.TradePagePay(ctx, bm)
|
|||
|
if err != nil {
|
|||
|
if bizErr, ok := alipay.IsBizError(err); ok {
|
|||
|
return nil, bizErr
|
|||
|
}
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
res = new(payin.CreateOrderModel)
|
|||
|
res.TradeType = in.Pay.TradeType
|
|||
|
res.PayURL = payUrl
|
|||
|
res.OutTradeNo = in.Pay.OutTradeNo
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func (h *aliPay) wap(ctx context.Context, in payin.CreateOrderInp) (res *payin.CreateOrderModel, err error) {
|
|||
|
client, err := GetClient(h.config)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
// 设置回调地址
|
|||
|
client.SetReturnUrl(in.Pay.ReturnUrl).SetNotifyUrl(in.Pay.NotifyUrl)
|
|||
|
|
|||
|
bm := make(gopay.BodyMap)
|
|||
|
bm.Set("subject", in.Pay.Subject).
|
|||
|
Set("out_trade_no", in.Pay.OutTradeNo).
|
|||
|
Set("total_amount", in.Pay.PayAmount).
|
|||
|
Set("product_code", "QUICK_WAP_WAY")
|
|||
|
|
|||
|
// 手机网站支付请求
|
|||
|
payUrl, err := client.TradeWapPay(ctx, bm)
|
|||
|
if err != nil {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
res = new(payin.CreateOrderModel)
|
|||
|
res.TradeType = in.Pay.TradeType
|
|||
|
res.PayURL = payUrl
|
|||
|
res.OutTradeNo = in.Pay.OutTradeNo
|
|||
|
return
|
|||
|
}
|