package payment import ( "bytes" "encoding/json" "fmt" "net/http" "strings" "kra/internal/biz" "github.com/go-pay/gopay" "github.com/go-pay/gopay/douyin" ) // The adapters in this package intentionally keep GoPay request/response // models below the data boundary. The business layer only sees PaymentResult. func callbackRequest(callback *biz.PaymentCallback) (*http.Request, error) { request, err := http.NewRequest(http.MethodPost, "http://payment-callback.local", bytes.NewReader(callback.Body)) if err != nil { return nil, err } for key, value := range callback.Headers { request.Header.Set(key, value) } return request, nil } func mergeGoPayExtras(dst gopay.BodyMap, src map[string]any, protected ...string) { protectedKeys := make(map[string]struct{}, len(protected)) for _, key := range protected { protectedKeys[key] = struct{}{} } for key, value := range src { if _, ok := protectedKeys[key]; ok { continue } dst[key] = value } } func mergeGoPayConfigExtras(dst gopay.BodyMap, config map[string]any, key string, protected ...string) { extra, ok := config[key].(map[string]any) if !ok || extra == nil { return } mergeGoPayExtras(dst, extra, protected...) } func anyConfig(c map[string]any, keys ...string) any { for _, key := range keys { if value, ok := c[key]; ok && fmt.Sprint(value) != "" { return value } } return nil } func firstAnyOr(values map[string]any, fallback string, keys ...string) string { if value := firstAny(values, keys...); value != "" { return value } return fallback } func mustJSON(value any) []byte { encoded, _ := json.Marshal(value) return encoded } func douyinError(rsp any) error { return fmt.Errorf("抖音支付请求失败: %v", rsp) } func qqOK(returnCode, resultCode string) bool { return strings.EqualFold(returnCode, "SUCCESS") && (resultCode == "" || strings.EqualFold(resultCode, "SUCCESS")) } func rspMap(value any) map[string]string { encoded, _ := json.Marshal(value) var object map[string]any _ = json.Unmarshal(encoded, &object) result := make(map[string]string, len(object)) for key, val := range object { result[key] = fmt.Sprint(val) } return result } func normalizeDouyinState(state string) string { switch strings.ToUpper(strings.TrimSpace(state)) { case douyin.TradeStateSuccess: return "success" case douyin.TradeStatePayErr: return "failed" case douyin.TradeStateClosed: return "failed" default: return "pending" } } func normalizeRefundState(state string) string { switch strings.ToUpper(strings.TrimSpace(state)) { case douyin.RefundStatusSuccess, "REFUNDED": return "success" case douyin.RefundStatusClosed, douyin.RefundStatusAbnormal, "FAILED": return "failed" default: return "pending" } } func normalizeQQState(state string) string { switch strings.ToUpper(strings.TrimSpace(state)) { case "SUCCESS", "REFUND": return "success" case "CLOSED", "PAYERROR", "REVOKED", "NOPAY": return "failed" default: return "pending" } } func normalizeAllinState(state string) string { switch strings.ToUpper(strings.TrimSpace(state)) { case "SUCCESS", "0000", "2000", "3040": return "success" case "FAIL", "FAILED", "3000", "3045": return "failed" default: return "pending" } } func normalizeLakalaState(state string) string { switch strings.ToUpper(strings.TrimSpace(state)) { case "SUCCESS", "PAID", "PAY_SUCCESS", "1": return "success" case "CLOSED", "FAILED", "FAIL", "CANCELLED": return "failed" default: return "pending" } } func normalizeSaobeiState(state string) string { switch strings.ToUpper(strings.TrimSpace(state)) { case "SUCCESS": return "success" case "CLOSED", "PAYERROR", "REVOKED", "FAILED", "NOPAY": return "failed" default: return "pending" } }