增加支付逻辑
This commit is contained in:
1 parent
4ac786aafb
commit
dfb2c91b8d
77 files changed
+6370
-45
No files matched your search
@@ -0,0 +1,172 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/payments"
|
||||
)
|
||||
|
||||
type PaymentRefundService interface {
|
||||
AdminList(context.Context, string, int, int) (payments.AdminOrdersPage, error)
|
||||
CreateRefund(context.Context, string, string, string, string, int64) (payments.Refund, error)
|
||||
GetRefund(context.Context, string) (payments.Refund, error)
|
||||
ListRefunds(context.Context, string) ([]payments.Refund, error)
|
||||
RefundNotify(context.Context, *http.Request) error
|
||||
}
|
||||
|
||||
func (h *paymentHandler) adminPayments(w http.ResponseWriter, r *http.Request) {
|
||||
parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/api/payments/admin/"), "/")
|
||||
list := len(parts) == 1 && parts[0] == "orders"
|
||||
order := len(parts) == 3 && parts[0] == "orders" && parts[2] == "refunds" && len(parts[1]) <= 32 && paymentIdentifier.MatchString(parts[1])
|
||||
refund := len(parts) == 2 && parts[0] == "refunds" && len(parts[1]) <= 64 && paymentIdentifier.MatchString(parts[1])
|
||||
if !list && !order && !refund {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if r.Method != http.MethodGet && !(order && r.Method == http.MethodPost) {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
session, err := h.authorizer.Authorize(r, PlatformSuperAdmin)
|
||||
if err != nil {
|
||||
writePaymentError(w, err)
|
||||
return
|
||||
}
|
||||
service, ok := h.service.(PaymentRefundService)
|
||||
if !ok {
|
||||
writeRefundError(w, payments.ErrUnavailable)
|
||||
return
|
||||
}
|
||||
if list {
|
||||
limit, offset := 50, 0
|
||||
if raw := r.URL.Query().Get("limit"); raw != "" {
|
||||
limit, err = strconv.Atoi(raw)
|
||||
if err != nil || limit < 1 || limit > 100 {
|
||||
writeAPIError(w, 400, "分页参数无效。")
|
||||
return
|
||||
}
|
||||
}
|
||||
if raw := r.URL.Query().Get("offset"); raw != "" {
|
||||
offset, err = strconv.Atoi(raw)
|
||||
if err != nil || offset < 0 || offset > 10000000 {
|
||||
writeAPIError(w, 400, "分页参数无效。")
|
||||
return
|
||||
}
|
||||
}
|
||||
org := r.URL.Query().Get("organizationId")
|
||||
if len(org) > 128 {
|
||||
writeAPIError(w, 400, "组织参数无效。")
|
||||
return
|
||||
}
|
||||
page, err := service.AdminList(r.Context(), org, limit, offset)
|
||||
if err != nil {
|
||||
writeRefundError(w, err)
|
||||
return
|
||||
}
|
||||
if page.Orders == nil {
|
||||
page.Orders = []payments.AdminOrder{}
|
||||
}
|
||||
for i := range page.Orders {
|
||||
page.Orders[i].CodeURL = ""
|
||||
}
|
||||
writeJSON(w, 200, page)
|
||||
return
|
||||
}
|
||||
if refund {
|
||||
value, err := service.GetRefund(r.Context(), parts[1])
|
||||
if err != nil {
|
||||
writeRefundError(w, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"refund": value})
|
||||
return
|
||||
}
|
||||
if r.Method == http.MethodGet {
|
||||
values, err := service.ListRefunds(r.Context(), parts[1])
|
||||
if err != nil {
|
||||
writeRefundError(w, err)
|
||||
return
|
||||
}
|
||||
if values == nil {
|
||||
values = []payments.Refund{}
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"refunds": values})
|
||||
return
|
||||
}
|
||||
if !h.sameOrigin(r) {
|
||||
writeAPIError(w, 403, "请从本站发起退款。")
|
||||
return
|
||||
}
|
||||
mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
if err != nil || mediaType != "application/json" {
|
||||
writeAPIError(w, 415, "请使用 JSON 请求。")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
AmountFen int64 `json:"amountFen"`
|
||||
Reason string `json:"reason"`
|
||||
RequestKey string `json:"requestKey"`
|
||||
}
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&body); err != nil {
|
||||
writeAPIError(w, 400, "退款参数无效,金额必须是整数分。")
|
||||
return
|
||||
}
|
||||
body.Reason = strings.TrimSpace(body.Reason)
|
||||
if err := decoder.Decode(&struct{}{}); err != io.EOF || body.AmountFen <= 0 || !paymentIdentifier.MatchString(body.RequestKey) || body.Reason == "" || len(body.Reason) > 80 || !utf8.ValidString(body.Reason) {
|
||||
writeAPIError(w, 400, "退款金额、请求标识或原因无效(原因最多 80 字节)。")
|
||||
return
|
||||
}
|
||||
value, err := service.CreateRefund(r.Context(), parts[1], session.User.ID, body.RequestKey, body.Reason, body.AmountFen)
|
||||
if err != nil {
|
||||
writeRefundError(w, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"refund": value})
|
||||
}
|
||||
|
||||
func (h *paymentHandler) refundNotify(w http.ResponseWriter, r *http.Request) {
|
||||
if !allow(w, r, http.MethodPost) {
|
||||
return
|
||||
}
|
||||
service, ok := h.service.(PaymentRefundService)
|
||||
if !ok {
|
||||
writeJSON(w, 503, map[string]string{"code": "FAIL", "message": "退款服务未启用"})
|
||||
return
|
||||
}
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 64<<10)
|
||||
if err := service.RefundNotify(r.Context(), r); err != nil {
|
||||
status := http.StatusInternalServerError
|
||||
if errors.Is(err, payments.ErrVerification) {
|
||||
status = http.StatusBadRequest
|
||||
}
|
||||
writeJSON(w, status, map[string]string{"code": "FAIL", "message": "退款通知处理失败"})
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func writeRefundError(w http.ResponseWriter, err error) {
|
||||
status, message := 500, "退款服务暂时不可用,请稍后刷新记录。"
|
||||
switch {
|
||||
case errors.Is(err, payments.ErrNotFound):
|
||||
status, message = 404, "充值或退款记录不存在。"
|
||||
case errors.Is(err, payments.ErrConflict):
|
||||
status, message = 409, "订单状态、可退金额或退款请求已变化,请刷新记录后重试。"
|
||||
case errors.Is(err, payments.ErrInvalidAmount):
|
||||
status, message = 400, "退款金额超过可退金额或参数无效。"
|
||||
case errors.Is(err, payments.ErrUnavailable):
|
||||
status, message = 503, "微信退款尚未启用。"
|
||||
}
|
||||
writeAPIError(w, status, message)
|
||||
}
|
||||
Reference in new issue
Block a user