From b7ebdf1ce6ca9762074cd04413411fd18a9ba400 Mon Sep 17 00:00:00 2001 From: Rogee Date: Tue, 6 May 2025 19:21:41 +0800 Subject: [PATCH] feat: add refund --- backend/app/http/admin/orders.go | 30 ++++++++++++++- backend/database/fields/orders.go | 5 ++- backend/providers/wepay/config.go | 43 +++++++++++++++++++++ backend/providers/wepay/pay.go | 63 ++++++++++++++++++++++++++++--- 4 files changed, 133 insertions(+), 8 deletions(-) diff --git a/backend/app/http/admin/orders.go b/backend/app/http/admin/orders.go index 5c26d11..39e44ea 100644 --- a/backend/app/http/admin/orders.go +++ b/backend/app/http/admin/orders.go @@ -3,6 +3,7 @@ package admin import ( "quyun/app/models" "quyun/app/requests" + "quyun/database/fields" "quyun/providers/wepay" "github.com/gofiber/fiber/v3" @@ -37,5 +38,32 @@ func (ctl *orders) Refund(ctx fiber.Ctx, id int64) error { return err } - return ctx.JSON(order) + post, err := models.Posts.GetByID(ctx.Context(), order.PostID) + if err != nil { + return err + } + + refundTotal := order.Price * int64(order.Discount) / 100 + resp, err := ctl.wepay.Refund(ctx.Context(), func(bm *wepay.BodyMap) { + bm. + OutRefundNo(order.OrderNo). + OutTradeNo(order.OrderNo). + TransactionID(order.TransactionID). + CNYRefundAmount(refundTotal, refundTotal). + RefundReason("管理员退款"). + RefundGoodsInfo(post.Title) + }) + if err != nil { + return err + } + + meta := order.Meta.Data + meta.RefundResp = resp + order.Meta = fields.ToJson(meta) + order.RefundTransactionID = resp.RefundId + + if err := models.Orders.Update(ctx.Context(), order); err != nil { + return err + } + return nil } diff --git a/backend/database/fields/orders.go b/backend/database/fields/orders.go index a31f003..c4cf2dd 100644 --- a/backend/database/fields/orders.go +++ b/backend/database/fields/orders.go @@ -2,6 +2,8 @@ package fields import ( "quyun/providers/wepay" + + "github.com/go-pay/gopay/wechat/v3" ) // swagger:enum OrderStatus @@ -9,5 +11,6 @@ import ( type OrderStatus int16 type OrderMeta struct { - PayNotify wepay.PayNotify `json:"pay_notify"` + PayNotify wepay.PayNotify `json:"pay_notify"` + RefundResp *wechat.RefundOrderResponse `json:"refund_resp"` } diff --git a/backend/providers/wepay/config.go b/backend/providers/wepay/config.go index d0a214c..baf04fa 100644 --- a/backend/providers/wepay/config.go +++ b/backend/providers/wepay/config.go @@ -39,3 +39,46 @@ type PayNotify struct { PayerCurrency string `json:"payer_currency"` } `json:"amount"` } + +type RefundResponse struct { + RefundID string `json:"refund_id"` + OutRefundNo string `json:"out_refund_no"` + TransactionID string `json:"transaction_id"` + OutTradeNo string `json:"out_trade_no"` + Channel string `json:"channel"` + UserReceivedAccount string `json:"user_received_account"` + SuccessTime time.Time `json:"success_time"` + CreateTime time.Time `json:"create_time"` + Status string `json:"status"` + FundsAccount string `json:"funds_account"` + Amount struct { + Total int `json:"total"` + Refund int `json:"refund"` + From []struct { + Account string `json:"account"` + Amount int `json:"amount"` + } `json:"from"` + PayerTotal int `json:"payer_total"` + PayerRefund int `json:"payer_refund"` + SettlementRefund int `json:"settlement_refund"` + SettlementTotal int `json:"settlement_total"` + DiscountRefund int `json:"discount_refund"` + Currency string `json:"currency"` + RefundFee int `json:"refund_fee"` + } `json:"amount"` + PromotionDetail []struct { + PromotionID string `json:"promotion_id"` + Scope string `json:"scope"` + Type string `json:"type"` + Amount int `json:"amount"` + RefundAmount int `json:"refund_amount"` + GoodsDetail []struct { + MerchantGoodsID string `json:"merchant_goods_id"` + WechatpayGoodsID string `json:"wechatpay_goods_id"` + GoodsName string `json:"goods_name"` + UnitPrice int `json:"unit_price"` + RefundAmount int `json:"refund_amount"` + RefundQuantity int `json:"refund_quantity"` + } `json:"goods_detail"` + } `json:"promotion_detail"` +} diff --git a/backend/providers/wepay/pay.go b/backend/providers/wepay/pay.go index bf63a7e..bea0d77 100644 --- a/backend/providers/wepay/pay.go +++ b/backend/providers/wepay/pay.go @@ -78,10 +78,22 @@ type PrepayData struct { } // PaySignOfJSAPI -func (pay *PrepayData) PaySignOfJSAPI() (*wechat.JSAPIPayParams, error) { +func (pay *PrepayData) PaySignOfJSAPI() (*, error) { return pay.client.payClient.PaySignOfJSAPI(pay.AppID, pay.PrepayID) } +func (c *Client) Refund(ctx context.Context, f func(*BodyMap)) (*wechat.RefundOrderResponse,error ){ + bm := NewBodyMap(c.config) + f(bm) + + resp, err := c.payClient.V3Refund(ctx, bm.bm) + if err != nil { + return nil, err + } + + return resp.Response, nil +} + func (c *Client) V3TransactionJsapi(ctx context.Context, f func(*BodyMap)) (*PrepayData, error) { bm := NewBodyMap(c.config) f(bm) @@ -149,11 +161,12 @@ func NewBodyMap(c *w.Config) *BodyMap { bm := make(gopay.BodyMap) bm.Set("appid", c.AppID). Set("mchid", c.Pay.MchID). - Set("notify_url", c.Pay.NotifyURL). - SetBodyMap("amount", func(bm gopay.BodyMap) { - bm.Set("total", 1). - Set("currency", "CNY") - }) + Set("notify_url", c.Pay.NotifyURL) + // . + // SetBodyMap("amount", func(bm gopay.BodyMap) { + // bm.Set("total", 1). + // Set("currency", "CNY") + // }) return &BodyMap{ bm: bm, } @@ -184,6 +197,44 @@ func (b *BodyMap) OutTradeNo(outTradeNo string) *BodyMap { return b.Set("out_trade_no", outTradeNo) } +// TransactionID +func (b *BodyMap) TransactionID(transactionID string) *BodyMap { + return b.Set("transaction_id", transactionID) +} + +// OutRefundNo +func (b *BodyMap) OutRefundNo(outRefundNo string) *BodyMap { + return b.Set("out_refund_no", outRefundNo) +} + +// RefundReason +func (b *BodyMap) RefundReason(refundReason string) *BodyMap { + return b.Set("reason", refundReason) +} + +// RefundAmount +func (b *BodyMap) RefundAmount(total, refund int64, currency CURRENCY) *BodyMap { + return b.SetBodyMap("amount", func(bm gopay.BodyMap) { + bm. + Set("total", total). + Set("refund", refund). + Set("currency", currency.String()) + }) +} + +func (b *BodyMap) CNYRefundAmount(total, refund int64) *BodyMap { + return b.RefundAmount(total, refund, CNY) +} + +// RefundGoodsInfo +func (b *BodyMap) RefundGoodsInfo(name string) *BodyMap { + return b.Set("goods_detail", []map[string]any{ + { + "goods_name": name, + }, + }) +} + // Amount func (b *BodyMap) Amount(total int64, currency CURRENCY) *BodyMap { return b.SetBodyMap("amount", func(bm gopay.BodyMap) {