You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

593 lines
17 KiB

6 months ago
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, &params)
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, &params)
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(&notifyBody)
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(&notifyBody)
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, &params)
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, &params)
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(&notifyBody)
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
}