package alipaycli import ( "context" "errors" "fmt" "github.com/go-pay/gopay" "github.com/go-pay/gopay/alipay" "math/rand" "net/http" "service/bizcommon/util" "service/library/configcenter" "service/library/logger" "time" ) const ( DefaultOrderTimeoutSeconds = 900 // 默认订单超时时间,单位: s ) const ( AppIdXinYiDaoLe = "2021004115647165" // 心意到了 AppIdMiYuanTianShi = "2021004135664261" // 觅缘天使 AppIdLanXing01 = "2021004153681591" // 揽星01 AppIdLanXing02 = "2021004153620449" // 揽星02 ) var allAlipayClients = map[string]*AlipayClient{} func GetDefaultAlipayClient() *AlipayClient { rate := rand.Intn(10) if rate < 9 { return allAlipayClients[AppIdMiYuanTianShi] } return allAlipayClients[AppIdLanXing01] } func GetAlipayClientByAppId(appId string) *AlipayClient { return allAlipayClients[appId] } type AlipayClient struct { *alipay.Client alipayPublicCertPath string } func InitMulti(cfgList ...*configcenter.AlipayClientConfig) (err error) { for _, cfg := range cfgList { var cli *AlipayClient cli, err = NewAlipayClient(cfg) if err != nil { return } if cli == nil { err = errors.New("NewAlipayClient fail") return } allAlipayClients[cli.AppId] = cli } return } func NewAlipayClient(cfg *configcenter.AlipayClientConfig) (ret *AlipayClient, err error) { alipayCli, err := alipay.NewClient(cfg.Appid, cfg.PrivateKey, true) if err != nil { logger.Error("NewClient fail, cfg: %v, err: %v", util.ToJson(cfg), err) return } alipayCli.SetNotifyUrl(cfg.NotifyUrl) err = alipayCli.SetCertSnByPath( cfg.AppCertPath, cfg.AlipayRootCertPath, cfg.AlipayPublicCertPath, ) if err != nil { logger.Error("SetCertSnByPath fail, cfg: %v, err: %v", util.ToJson(cfg), err) return } ret = &AlipayClient{ Client: alipayCli, alipayPublicCertPath: cfg.AlipayPublicCertPath, } return } //// 解析回调参数 //func (c *AlipayClient) ParseNotify(req *http.Request) (notify gopay.BodyMap, err error) { // // 解析参数 // notifyTmp, err := alipay.ParseNotifyToBodyMap(req) // if err != nil { // logger.Error("ParseNotifyToBodyMap fail, req: %v, err: %v", util.ToJson(req), err) // return // } // logger.Info("Alipay ParseNotify, %v", util.ToJson(notifyTmp)) // // // 验签 // ok, err := alipay.VerifySignWithCert(c.alipayPublicCertPath, notifyTmp) // if !ok { // logger.Error("VerifySign fail, not ok, bm: %v", util.ToJson(notifyTmp)) // return // } // notify = notifyTmp // return //} // 解析回调参数 func ParseNotify(req *http.Request) (notify gopay.BodyMap, err error) { // 解析参数 notifyTmp, err := alipay.ParseNotifyToBodyMap(req) if err != nil { logger.Error("ParseNotifyToBodyMap fail, req: %v, err: %v", util.ToJson(req), err) return } logger.Info("Alipay ParseNotify, %v", util.ToJson(notifyTmp)) // 获取appId appId := notifyTmp.Get("app_id") c := GetAlipayClientByAppId(appId) if c == nil { err = fmt.Errorf("GetAlipayClientByAppId fail, appId: %v", appId) logger.Error("GetAlipayClientByAppId fail, appId: %v", appId) return } // 验签 ok, err := alipay.VerifySignWithCert(c.alipayPublicCertPath, notifyTmp) if !ok { logger.Error("VerifySign fail, not ok, bm: %v", util.ToJson(notifyTmp)) return } notify = notifyTmp return } // 支付宝 app支付 type AppPayParam struct { OutTradeNo string // 商家订单id,我们自己的订单id Subject string // 主题 TotalAmount int64 // 金额,单位:分 TimeOutSeconds int // 订单有效时间,单位:秒 } func (c *AlipayClient) AppPay(ctx context.Context, param *AppPayParam) (alipayAppParamStr string, err error) { if param.TimeOutSeconds <= 0 { param.TimeOutSeconds = DefaultOrderTimeoutSeconds } bm := gopay.BodyMap{ "out_trade_no": param.OutTradeNo, "subject": param.Subject, "total_amount": fmt.Sprintf("%.2f", float64(param.TotalAmount)/100.0), "time_expire": time.Now().Add(time.Second * time.Duration(param.TimeOutSeconds)).Format("2006-01-02 15:04:05"), "notify_url": c.NotifyUrl, } alipayAppParamStr, err = c.TradeAppPay(ctx, bm) if err != nil { return } logger.Info("alipay TradeAppPay param: %v", alipayAppParamStr) return } // 支付宝 手机web支付 type WapPayParam struct { OutTradeNo string // 商家订单id,我们自己的订单id Subject string // 主题 TotalAmount int64 // 金额,单位:分 TimeOutSeconds int // 订单有效时间,单位:秒 ReturnUrl string // 支付宝return_url } func (c *AlipayClient) WapPay(ctx context.Context, param *WapPayParam) (alipayWapParamStr string, err error) { if param.TimeOutSeconds <= 0 { param.TimeOutSeconds = DefaultOrderTimeoutSeconds } bm := gopay.BodyMap{ "out_trade_no": param.OutTradeNo, "subject": param.Subject, "total_amount": fmt.Sprintf("%.2f", float64(param.TotalAmount)/100.0), "time_expire": time.Now().Add(time.Second * time.Duration(param.TimeOutSeconds)).Format("2006-01-02 15:04:05"), "notify_url": c.NotifyUrl, } if len(param.ReturnUrl) > 0 { bm["return_url"] = param.ReturnUrl } alipayWapParamStr, err = c.TradeWapPay(ctx, bm) if err != nil { return } logger.Info("alipay TradeWapPay param: %v", alipayWapParamStr) return } // 支付宝单笔转账 type UniTransferParam struct { OutBizNo string // 商家订单id,我们自己的订单id Amount int64 // 金额,单位:分 Title string // 转账业务的标题,用于在支付宝用户的账单里显示。 AlipayLoginId string // 支付宝登录账号 AlipayName string // 支付宝真实姓名 } func (c *AlipayClient) UniTransfer(ctx context.Context, param *UniTransferParam) (resp *alipay.FundTransUniTransferResponse, err error) { bm := gopay.BodyMap{ "out_biz_no": param.OutBizNo, "trans_amount": fmt.Sprintf("%.2f", float64(param.Amount)/100.0), "biz_scene": "DIRECT_TRANSFER", "product_code": "TRANS_ACCOUNT_NO_PWD", "order_title": param.Title, "payee_info": map[string]interface{}{ "identity": param.AlipayLoginId, "identity_type": "ALIPAY_LOGON_ID", "name": param.AlipayName, }, } resp, err = c.FundTransUniTransfer(ctx, bm) if err != nil { return } logger.Info("alipay UniTransfer param: %v, resp: %v", bm.JsonBody(), util.ToJson(resp)) return } // 订单查询 type QueryOrderParam struct { OutTradeNo string // 商家订单id,我们自己的订单id } func (c *AlipayClient) QueryOrder(ctx context.Context, param *QueryOrderParam) (resp *alipay.TradeQueryResponse, err error) { bm := gopay.BodyMap{ "out_trade_no": param.OutTradeNo, } resp, err = c.TradeQuery(ctx, bm) if err != nil { return } return } // 退款申请 type RefundOneParam struct { OutTradeNo string // 商家订单id,我们自己的订单id RefundAmount int64 // 退款金额,单位:分 RefundReason string // 退款理由 } func (c *AlipayClient) RefundOne(ctx context.Context, param *RefundOneParam) (resp *alipay.TradeRefundResponse, err error) { if len(param.RefundReason) <= 0 { param.RefundReason = "退款" } bm := gopay.BodyMap{ "out_trade_no": param.OutTradeNo, "refund_amount": fmt.Sprintf("%.2f", float64(param.RefundAmount)/100.0), "refund_reason": param.RefundReason, } resp, err = c.TradeRefund(ctx, bm) logger.Info("RefundOne bm: %v, resp: %v", util.ToJson(bm), util.ToJson(resp)) if err != nil { return } return }