package service import ( "bkb-payment/internal/conf" "bkb-payment/pkg" "bkb-payment/pkg/util" "context" "encoding/json" "fmt" "github.com/go-kratos/kratos/v2/errors" "strconv" "time" "bkb-payment/api" "bkb-payment/internal/biz" "google.golang.org/protobuf/types/known/emptypb" ) // GreeterService is a greeter service. type GreeterService struct { api.UnimplementedGreeterServer uc *biz.GreeterUsecase third *conf.Third } // NewGreeterService new a greeter service. func NewGreeterService(uc *biz.GreeterUsecase, third *conf.Third) *GreeterService { return &GreeterService{uc: uc, third: third} } // SayHello implements helloworld.GreeterServer. func (s *GreeterService) Ping(ctx context.Context, in *emptypb.Empty) (*api.PingReply, error) { return &api.PingReply{Message: "Hello"}, nil } func (s *GreeterService) CountryList(ctx context.Context, in *emptypb.Empty) (*api.CountryReply, error) { var result api.CountryReply list, err := s.uc.CountList(ctx) if err != nil { return nil, err } result.List = list return &result, nil } func (s *GreeterService) DistrictCascade(ctx context.Context, in *api.DistrictRequest) (*api.DistrictReply, error) { var result api.DistrictReply cascadeList, err := s.uc.DistrictCascadeList(ctx, uint(in.Country)) if err != nil { return nil, err } result.List = cascadeList return &result, nil } func (s *GreeterService) PayConsult(ctx context.Context, in *api.PayConsultRequest) (*api.PayConsultReply, error) { var ( err error result api.PayConsultReply ) bytes, _ := json.Marshal(in) fmt.Println(time.Now(), "参数:", string(bytes)) if in.PayChannel == "" { err = errors.New(400, "", "pay channel is null") return nil, err } var ( resp []biz.PayMethod payBot biz.PayBot params biz.ConsultPay ) payBot, err = s.uc.NewPayBot(ctx, in.PayChannel) if err != nil { return nil, err } params.ProductCode = "CASHIER_PAYMENT" params.Currency = in.Currency switch in.PayChannel { case "paypal": //params.Amount = strconv.FormatFloat(bill.Amount, 'f', -1, 64) params.Amount = "0.01" case "alipay-g": // 支付宝国际 params.Amount = util.MoneySmallestCurrency(in.Amount, in.Currency) // 货币的最小单位 MYR,THB,MMK,JYP,VND params.UserRegion = in.UserRegion // MY-马来西亚,TH-泰国,MM-缅甸,JP-日本,VN-越南 switch in.UserRegion { case "MY": params.Currency = "MYR" case "TH": params.Currency = "THB" case "JP": params.Currency = "JPY" case "VN": params.Currency = "VND" } params.OsType = in.OsType params.TerminalType = in.TerminalType default: err = errors.New(400, "", "pay channel not opened") return nil, err } resp, err = payBot.Consult(ctx, ¶ms) if err != nil { return nil, err } var list = make([]*api.PaymentMethod, 0) for _, method := range resp { list = append(list, &api.PaymentMethod{LogoName: method.LogoName, LogoUrl: method.LogoUrl, TypeValue: method.Type, Category: method.Category, Enabled: method.Enabled}) } result.List = list return &result, err } func (s *GreeterService) PayTransactionWebUrl(ctx context.Context, in *api.PayTransRequest) (*api.WebPayResponse, error) { var ( err error bill *biz.Bill result api.WebPayResponse ) start := time.Now() bytes, _ := json.Marshal(in) fmt.Println(time.Now(), "参数:", string(bytes)) if in.PayChannel == "" { err = errors.New(400, "", "pay channel is null") return nil, err } bill, err = s.uc.PayTransaction(ctx, &biz.CreateBillReq{ PayTransRequest: api.PayTransRequest{ Appid: in.Appid, Mchid: in.Mchid, OutTradeNo: in.OutTradeNo, Attach: in.Attach, NotifyUrl: in.NotifyUrl, Amount: in.Amount, Currency: in.Currency, PayChannel: in.PayChannel, Description: in.Description, }, OsType: in.OsType, TerminalType: in.TerminalType, TradeType: "PAY", PaymentMethod: in.PaymentMethod, }) if err != nil { return nil, err } result.PayChannel = in.PayChannel var ( payBot biz.PayBot params biz.CreateOrder resp biz.PayResp ) payBot, err = s.uc.NewPayBot(ctx, in.PayChannel) if err != nil { return nil, err } params.Currency = bill.Currency params.ReturnUrl = in.ReturnUrl params.CancelUrl = in.CancelUrl params.TransactionId = bill.TransactionId params.Description = in.Description switch in.PayChannel { case "paypal": //params.Amount = strconv.FormatFloat(bill.Amount, 'f', -1, 64) params.Amount = "0.01" case "alipay-g": // 支付宝国际 params.Amount = util.MoneySmallestCurrency(bill.Amount, bill.Currency) // 货币的最小单位 params.NotifyUrl = s.third.AlipayG.NotifyUrl if in.PaymentMethod != "" { params.PaymentMethodType = in.PaymentMethod // TODO https://global.alipay.com/docs/ac/ref/payment_method#xm58z } else { params.PaymentMethodType = "CONNECT_WALLET" // TODO https://global.alipay.com/docs/ac/ref/payment_method#xm58z } params.OsType = in.OsType params.TerminalType = in.TerminalType default: err = errors.New(400, "", "pay channel not opened") return nil, err } resp, err = payBot.CreateOrder(ctx, ¶ms) if err != nil { return nil, err } // 更新第三方支付id err = s.uc.UpdateBillPayId(ctx, in.Mchid, bill.TransactionId, resp.ID) if err != nil { return nil, err } result.PayId = resp.ID result.PayReturn = resp.Link result.PayStatus = resp.Status fmt.Println("总耗时:", time.Now().Sub(start).Nanoseconds()) return &result, nil } func (s *GreeterService) GetPayTransaction(ctx context.Context, in *emptypb.Empty) (*api.GetPayTransactionResp, error) { var data = api.GetPayTransaction{} data.Appid = "123" data.Mchid = "123444" return &api.GetPayTransactionResp{ Data: &data, }, nil } func (s *GreeterService) PaypalPayback(ctx context.Context, in *api.PaypalWebhook) (*api.PaypalPaybackResp, error) { var ( err error payFlag bool payId, message string resourceType, captureId string result api.PaypalPaybackResp ) //bytes, _ := json.Marshal(in) //fmt.Println(time.Now(), "支付回调body:", string(bytes)) switch in.EventType { case "CHECKOUT.ORDER.APPROVED": // 订单支付已完成 商家确认订单 if in.Resource.Status != "APPROVED" { // 支付未成功 payFlag = false break } else { payFlag = true } var paypal biz.PayBot paypal, err = s.uc.NewPayBot(ctx, "paypal") if err != nil { return nil, err } payFlag, captureId, message = paypal.CaptureOrder(ctx, &biz.Order{OrderId: in.Resource.Id}) payId = in.Resource.Id resourceType = "pay" case "PAYMENT.PAYOUTSBATCH.SUCCESS": // 提现完成 if in.Resource.BatchHeader.BatchStatus != "SUCCESS" { payFlag = false break } else { payFlag = true } var paypal biz.PayBot paypal, err = s.uc.NewPayBot(ctx, "paypal") if err != nil { return nil, err } payFlag, message = paypal.ShowPayoutsDetail(ctx, &biz.Payouts{PayoutBatchId: in.Resource.BatchHeader.PayoutBatchId}) payId = in.Resource.BatchHeader.PayoutBatchId resourceType = "payout" default: //fmt.Println() return nil, err } if payFlag { err = s.uc.UpdateBillTradeStatusByPayId(ctx, "paypal", payId, "SUCCESS") if err != nil { return nil, err } } func() { // 支付结果回调通知 billAttach, err := s.uc.GetBillAttachByPayId(ctx, "paypal", payId) if err != nil { fmt.Println("获取通知地址失败,", in.Resource.Id) return } var notifyBody = api.PaybackBody{} notifyBody.ResourceType = resourceType notifyBody.Attach = billAttach.Attach notifyBody.OutTradeNo = billAttach.OutTradeNo notifyBody.TransactionId = billAttach.TransactionId notifyBody.PayId = in.Resource.Id notifyBody.CaptureId = captureId notifyBody.Status = billAttach.Attach notifyBody.Message = message if payFlag { notifyBody.Status = "SUCCESS" } else { notifyBody.Status = "FAIL" } var notifyStatus int err = pkg.NotifyService(billAttach.NotifyUrl, notifyBody) if err != nil { bytes, _ := json.Marshal(¬ifyBody) fmt.Println("通知失败,", billAttach.NotifyUrl, string(bytes)) notifyStatus = 2 } else { notifyStatus = 1 } err = s.uc.UpdateBillAttachNotifyStatus(ctx, billAttach.TransactionId, notifyStatus) if err != nil { fmt.Println(err.Error()) } return }() return &result, err } func (s *GreeterService) AlipayGPayback(ctx context.Context, in *api.AlipayGWebhook) (*api.AlipayGPaybackResp, error) { var ( err error payFlag bool payId, message, resourceType string captureId string alipayResult api.AlipayGResult result api.AlipayGPaybackResp ) bytes, _ := json.Marshal(in) fmt.Println(time.Now(), "AlipayGPayback支付回调body:", string(bytes)) alipayResult.ResultCode = "FAIL" alipayResult.ResultStatus = "F" alipayResult.ResultMessage = "Fail" result.Result = &alipayResult switch in.NotifyType { case "PAYMENT_RESULT": resourceType = "pay" payId = in.PaymentId if in.Result.ResultStatus == "S" { payFlag = true } //var bot biz.PayBot //bot, err = s.uc.NewPayBot(ctx, "alipay-g") //if err != nil { // return nil, err //} //payFlag, captureId, message = bot.CaptureOrder(ctx, &biz.Order{OrderId: in.PaymentRequestId, PayId: in.PaymentId, Currency: in.PaymentAmount.Currency, Value: in.PaymentAmount.Value}) captureId = "1" case "REFUND_RESULT": resourceType = "refund" payId = in.RefundId if in.Result.ResultStatus == "S" || in.RefundStatus == "SUCCESS" { payFlag = true } case "PAYMENT_PENDING": fmt.Println("PAYMENT_PENDING-----", in) return &result, nil } if payFlag { err = s.uc.UpdateBillTradeStatusByPayId(ctx, "alipay-g", payId, "SUCCESS") if err != nil { return &result, err } } func() { // 支付结果回调通知 billAttach, err := s.uc.GetBillAttachByPayId(ctx, "alipay-g", payId) if err != nil { fmt.Println("获取通知地址失败,", payId) return } var notifyBody = api.PaybackBody{} notifyBody.ResourceType = resourceType notifyBody.Attach = billAttach.Attach notifyBody.OutTradeNo = billAttach.OutTradeNo notifyBody.TransactionId = billAttach.TransactionId notifyBody.PayId = payId notifyBody.CaptureId = captureId notifyBody.Status = billAttach.Attach notifyBody.Message = message if payFlag { notifyBody.Status = "SUCCESS" } else { notifyBody.Status = "FAIL" } var notifyStatus int err = pkg.NotifyService(billAttach.NotifyUrl, notifyBody) if err != nil { bytes, _ := json.Marshal(¬ifyBody) fmt.Println("通知失败,", billAttach.NotifyUrl, string(bytes)) notifyStatus = 2 } else { notifyStatus = 1 } err = s.uc.UpdateBillAttachNotifyStatus(ctx, billAttach.TransactionId, notifyStatus) if err != nil { fmt.Println(err.Error()) } return }() alipayResult.ResultCode = "SUCCESS" alipayResult.ResultStatus = "S" alipayResult.ResultMessage = "Success" result.Result = &alipayResult return &result, err } func (s *GreeterService) AlipayGCapture(ctx context.Context, in *api.AlipayGWebhook) (*api.AlipayGPaybackResp, error) { var ( err error result api.AlipayGPaybackResp ) bytes, _ := json.Marshal(in) fmt.Println(time.Now(), "AlipayGCapture支付回调body:", string(bytes)) return &result, err } func (s *GreeterService) PayoutsWebUrl(ctx context.Context, in *api.PayoutRequest) (*api.WebPayResponse, error) { var ( err error bill *biz.Bill result api.WebPayResponse ) start := time.Now() bytes, _ := json.Marshal(in) fmt.Println(time.Now(), "PayoutAppUrl参数:", string(bytes)) if in.PayChannel == "" { err = errors.New(400, "", "pay channel is null") return nil, err } bill, err = s.uc.PayTransaction(ctx, &biz.CreateBillReq{ PayTransRequest: api.PayTransRequest{ Appid: in.Appid, Mchid: in.Mchid, OutTradeNo: in.OutTradeNo, Attach: in.Attach, NotifyUrl: in.NotifyUrl, Amount: in.Amount, Currency: in.Currency, PayChannel: in.PayChannel, }, OsType: "", TerminalType: "", TradeType: "PAYOUT", PayAccount: in.PaypalName, }) if err != nil { return nil, err } result.PayChannel = in.PayChannel switch in.PayChannel { case "paypal": var ( paypal biz.PayBot params biz.CreatePayout resp biz.PayResp ) paypal, err = s.uc.NewPayBot(ctx, in.PayChannel) if err != nil { return nil, err } params.Currency = bill.Currency //params.Amount = strconv.FormatFloat(bill.Amount, 'f', -1, 64) params.Amount = "0.01" params.SenderBatchId = in.OutTradeNo params.PayerName = in.PaypalName resp, err = paypal.Payouts(ctx, ¶ms) if err != nil { return nil, err } // 更新第三方支付id err = s.uc.UpdateBillPayId(ctx, in.Mchid, bill.TransactionId, resp.ID) if err != nil { return nil, err } result.PayId = resp.ID result.PayReturn = resp.Link result.PayStatus = resp.Status default: err = errors.New(400, "", "pay channel not opened") return nil, err } fmt.Println("PayoutAppUrl参数总耗时:", time.Now().Sub(start).Nanoseconds()) return &result, err } func (s *GreeterService) CancelBill(ctx context.Context, in *api.CancelBillRequest) (*api.PingReply, error) { var ( err error result *api.PingReply ) bill, err := s.uc.GetBillByTransactionId(ctx, in.Mchid, in.TransactionId) if err != nil { err = errors.New(400, "", "pay channel is null") return nil, err } if bill.TradeState != "SUCCESS" { err = errors.New(400, "", "bill unpaid") return nil, err } var ( payBot biz.PayBot resp bool ) payBot, err = s.uc.NewPayBot(ctx, bill.PayChannel) if err != nil { return nil, err } switch bill.PayChannel { case "paypal": case "alipay-g": } resp, err = payBot.CancelOrder(ctx, in.TransactionId) if err != nil { return nil, err } fmt.Println(resp) //err = s.uc.CloseBill(ctx, in) return result, err } func (s *GreeterService) RefundBill(ctx context.Context, in *api.RefundRequest) (*api.RefundResponse, error) { var ( err error checkBill *biz.Bill result api.RefundResponse ) checkBill, err = s.uc.GetBillByOutTradeNoPayId(ctx, in.Mchid, in.OutTradeNo, in.PayId) if err != nil { err = errors.New(400, "", "pay channel is null") return nil, err } if checkBill.TradeState != "SUCCESS" { err = errors.New(400, "", "bill unpaid") return nil, err } if in.Amount > checkBill.Amount { err = errors.New(400, "", "refund amount is too large") return nil, err } bill, err := s.uc.PayTransaction(ctx, &biz.CreateBillReq{ PayTransRequest: api.PayTransRequest{ Appid: in.Appid, Mchid: in.Mchid, OutTradeNo: "R-" + checkBill.OutTradeNo, Attach: in.Attach, NotifyUrl: in.NotifyUrl, Amount: in.Amount, Currency: checkBill.Currency, PayChannel: checkBill.PayChannel, Description: in.Description, }, TradeType: "REFUND", PaymentMethod: checkBill.PaymentMethod, }) if err != nil { return nil, err } var ( payBot biz.PayBot params biz.Refund resp biz.RefundResp ) payBot, err = s.uc.NewPayBot(ctx, bill.PayChannel) if err != nil { return nil, err } params.TransactionId = bill.TransactionId params.PayId = checkBill.PayId params.Reason = in.Description params.Currency = bill.Currency switch bill.PayChannel { case "paypal": params.Amount = strconv.FormatFloat(bill.Amount, 'f', -1, 64) case "alipay-g": params.Amount = util.MoneySmallestCurrency(bill.Amount, bill.Currency) // 货币的最小单位 params.NotifyUrl = s.third.AlipayG.NotifyUrl default: err = errors.New(400, "", "pay channel not opened") return nil, err } resp, err = payBot.RefundOrder(ctx, ¶ms) if err != nil { return nil, err } // 更新第三方支付id err = s.uc.UpdateBillPayId(ctx, in.Mchid, bill.TransactionId, resp.RefundId) if err != nil { return nil, err } if resp.Status != "UNKNOWN" { func() { // 支付结果回调通知 billAttach, err := s.uc.GetBillAttachByPayId(ctx, "alipay-g", resp.RefundId) if err != nil { fmt.Println("获取通知地址失败,", resp.RefundId) return } var notifyBody = api.PaybackBody{} notifyBody.ResourceType = "refund" notifyBody.Attach = billAttach.Attach notifyBody.OutTradeNo = billAttach.OutTradeNo notifyBody.TransactionId = billAttach.TransactionId notifyBody.PayId = resp.RefundId notifyBody.CaptureId = "1" notifyBody.Status = billAttach.Attach notifyBody.Message = "" notifyBody.Status = resp.Status var notifyStatus int err = pkg.NotifyService(billAttach.NotifyUrl, notifyBody) if err != nil { bytes, _ := json.Marshal(¬ifyBody) fmt.Println("通知失败,", billAttach.NotifyUrl, string(bytes)) notifyStatus = 2 } else { notifyStatus = 1 } err = s.uc.UpdateBillAttachNotifyStatus(ctx, billAttach.TransactionId, notifyStatus) if err != nil { fmt.Println(err.Error()) } return }() } result.RefundId = resp.RefundId result.RefundStatus = resp.Status return &result, err }