添加支付宝和微信充值功能,重构交易处理逻辑,优化资源创建与支付链接生成
This commit is contained in:
@@ -1,14 +1,19 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
g "platform/web/globals"
|
||||
q "platform/web/queries"
|
||||
s "platform/web/services"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/smartwalle/alipay/v3"
|
||||
"github.com/valyala/fasthttp/fasthttpadaptor"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
|
||||
)
|
||||
|
||||
// region AlipayCallback
|
||||
@@ -16,73 +21,87 @@ import (
|
||||
func AlipayCallback(c *fiber.Ctx) error {
|
||||
|
||||
// 解析请求
|
||||
req := make(map[string][]string)
|
||||
c.Context().QueryArgs().VisitAll(func(key, value []byte) {
|
||||
req[string(key)] = append(req[string(key)], string(value))
|
||||
})
|
||||
c.Context().PostArgs().VisitAll(func(key, value []byte) {
|
||||
req[string(key)] = append(req[string(key)], string(value))
|
||||
})
|
||||
|
||||
notification, err := g.Alipay.DecodeNotification(req)
|
||||
httpRequest := new(http.Request)
|
||||
if err := fasthttpadaptor.ConvertRequest(c.Context(), httpRequest, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := httpRequest.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
notification, err := g.Alipay.DecodeNotification(httpRequest.Form)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Debug("支付宝支付回调", "notification", fmt.Sprintf("%+v", notification))
|
||||
|
||||
// todo 退款通知
|
||||
if isRefund(notification) {
|
||||
return act(c)
|
||||
}
|
||||
|
||||
// 查询交易信息
|
||||
trade, err := q.Q.Trade.Where(q.Trade.InnerNo.Eq(notification.OutTradeNo)).Take()
|
||||
if err != nil {
|
||||
// 跳过测试通知
|
||||
return act(c)
|
||||
}
|
||||
switch notification.NotifyType {
|
||||
|
||||
// 支付成功
|
||||
case string(alipay.TradeStatusSuccess):
|
||||
|
||||
if isRefund(notification) {
|
||||
break
|
||||
}
|
||||
|
||||
// 收集交易状态
|
||||
payment, err := strconv.ParseFloat(notification.TotalAmount, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
paidAt, err := time.Parse("2006-01-02 15:04:05", notification.GmtPayment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verified := &s.TransactionVerifyResult{
|
||||
TransId: notification.TradeNo,
|
||||
Payment: payment,
|
||||
Time: paidAt,
|
||||
}
|
||||
switch trade.Type {
|
||||
|
||||
err = s.Resource.CreateResourcePrepared(
|
||||
c.Context(),
|
||||
notification.OutTradeNo,
|
||||
notification.TradeNo,
|
||||
payment,
|
||||
paidAt,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
// 余额充值
|
||||
case 0:
|
||||
err := s.User.RechargeConfirm(c.Context(), notification.OutTradeNo, verified)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 购买产品
|
||||
case 1:
|
||||
err = s.Resource.CompleteResource(c.Context(), notification.OutTradeNo, verified)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 支付关闭
|
||||
case string(alipay.TradeStatusClosed):
|
||||
switch trade.Type {
|
||||
|
||||
if isRefund(notification) {
|
||||
break
|
||||
// 购买产品
|
||||
case 1:
|
||||
|
||||
cancelAt, err := time.Parse("2006-01-02 15:04:05", notification.GmtClose)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.Resource.CancelResource(c.Context(), notification.OutTradeNo, cancelAt, s.TransactionMethodAlipay)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cancelAt, err := time.Parse("2006-01-02 15:04:05", notification.GmtClose)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.Resource.CancelResource(c.Context(), notification.OutTradeNo, cancelAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
g.Alipay.ACKNotification(AdapterWriter{
|
||||
c: c,
|
||||
})
|
||||
return nil
|
||||
return act(c)
|
||||
}
|
||||
|
||||
type AdapterWriter struct {
|
||||
@@ -105,12 +124,72 @@ func isRefund(notification *alipay.Notification) bool {
|
||||
return notification.OutBizNo != "" || notification.RefundFee != "" || notification.GmtRefund != ""
|
||||
}
|
||||
|
||||
func act(c *fiber.Ctx) error {
|
||||
g.Alipay.ACKNotification(AdapterWriter{c: c})
|
||||
return nil
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region WechatPayCallback
|
||||
|
||||
func WechatPayCallback(c *fiber.Ctx) error {
|
||||
|
||||
// 解析请求参数
|
||||
req := new(http.Request)
|
||||
if err := fasthttpadaptor.ConvertRequest(c.Context(), req, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content := new(payments.Transaction)
|
||||
_, err := g.WechatPay.Notify.ParseNotifyRequest(c.Context(), req, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Debug("微信支付回调", "content", fmt.Sprintf("%+v", content))
|
||||
|
||||
// 查询交易信息
|
||||
trade, err := q.Q.Trade.Where(q.Trade.InnerNo.Eq(*content.OutTradeNo)).Take()
|
||||
if err != nil {
|
||||
// 跳过测试通知
|
||||
return nil
|
||||
}
|
||||
switch *content.TradeState {
|
||||
|
||||
// 支付成功
|
||||
case "SUCCESS":
|
||||
|
||||
// 收集交易状态
|
||||
payment := float64(*content.Amount.PayerTotal) / 100
|
||||
paidAt, err := time.Parse(time.RFC3339, *content.SuccessTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verified := &s.TransactionVerifyResult{
|
||||
TransId: *content.TransactionId,
|
||||
Payment: payment,
|
||||
Time: paidAt,
|
||||
}
|
||||
|
||||
switch {
|
||||
|
||||
// 余额充值
|
||||
case trade.Type == 0:
|
||||
err := s.User.RechargeConfirm(c.Context(), *content.OutTradeNo, verified)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 购买产品
|
||||
case trade.Type == 1:
|
||||
err = s.Resource.CompleteResource(c.Context(), *content.OutTradeNo, verified)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user