优化结构
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
"fmt"
|
||||
integrationbiz "kra/internal/biz/integration"
|
||||
bizpayment "kra/internal/biz/payment"
|
||||
"net/url"
|
||||
"kra/internal/paymentkit"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -21,10 +21,11 @@ type paymentRepo struct {
|
||||
data Provider
|
||||
config integrationbiz.PaymentConfigReader
|
||||
factory bizpayment.PaymentAdapterFactory
|
||||
orders bizpayment.PaymentOrderRepo
|
||||
}
|
||||
|
||||
func NewPaymentRepo(data Provider, config integrationbiz.PaymentConfigReader, factory bizpayment.PaymentAdapterFactory) bizpayment.PaymentRepo {
|
||||
return &paymentRepo{data: data, config: config, factory: factory}
|
||||
func NewPaymentRepo(data Provider, config integrationbiz.PaymentConfigReader, factory bizpayment.PaymentAdapterFactory, orders bizpayment.PaymentOrderRepo) bizpayment.PaymentRepo {
|
||||
return &paymentRepo{data: data, config: config, factory: factory, orders: orders}
|
||||
}
|
||||
|
||||
func (r *paymentRepo) values(ctx context.Context, provider string) (map[string]any, error) {
|
||||
@@ -71,7 +72,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*bizpa
|
||||
add("config", "failed", err.Error(), "", started)
|
||||
return test, err
|
||||
}
|
||||
test.Mode = strings.ToLower(strings.TrimSpace(text(values, "environment")))
|
||||
test.Mode = strings.ToLower(strings.TrimSpace(paymentkit.Text(values, "environment")))
|
||||
configStart := time.Now()
|
||||
if err = integrationbiz.ValidateIntegrationConfig(integrationbiz.IntegrationKindPayment, provider, values); err != nil {
|
||||
add("config", "failed", err.Error(), "", configStart)
|
||||
@@ -94,12 +95,16 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*bizpa
|
||||
add("adapter", "failed", err.Error(), "", time.Now())
|
||||
return test, err
|
||||
}
|
||||
if r.orders == nil {
|
||||
err = errors.New("支付订单仓储未接入")
|
||||
add("local_order", "failed", err.Error(), "", time.Now())
|
||||
return test, err
|
||||
}
|
||||
req := paymentTestRequest(provider, values)
|
||||
test.TradeNo = req.TradeNo
|
||||
orders := &paymentOrderRepo{data: r.data}
|
||||
extra, _ := json.Marshal(req.Extra)
|
||||
localStart := time.Now()
|
||||
order, _, err := orders.CreatePaymentOrder(ctx, &bizpayment.PaymentOrder{
|
||||
order, _, err := r.orders.CreatePaymentOrder(ctx, &bizpayment.PaymentOrder{
|
||||
TradeNo: req.TradeNo, Provider: provider, BusinessType: req.BusinessType, BusinessID: req.BusinessID,
|
||||
Subject: req.Subject, PaymentMode: bizpayment.PaymentModeExternal, OriginalAmount: req.Amount, Amount: req.Amount,
|
||||
Currency: req.Currency, PaymentStatus: bizpayment.PaymentStatusInitialized, FulfillmentStatus: bizpayment.FulfillmentStatusPending,
|
||||
@@ -127,7 +132,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*bizpa
|
||||
add("create", "failed", err.Error(), req.TradeNo, createStart)
|
||||
return test, err
|
||||
}
|
||||
if order, err = orders.RecordPaymentCreate(ctx, provider, req.TradeNo, paymentTestProviderUpdate(created)); err != nil {
|
||||
if order, err = r.orders.RecordPaymentCreate(ctx, provider, req.TradeNo, paymentTestProviderUpdate(created)); err != nil {
|
||||
add("local_order", "failed", "记录第三方下单结果失败: "+err.Error(), req.TradeNo, createStart)
|
||||
return test, err
|
||||
}
|
||||
@@ -142,7 +147,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*bizpa
|
||||
queryID = req.TradeNo
|
||||
}
|
||||
if provider == bizpayment.PaymentApple {
|
||||
queryID = strings.TrimSpace(text(values, "test_transaction_id"))
|
||||
queryID = strings.TrimSpace(paymentkit.Text(values, "test_transaction_id"))
|
||||
if queryID == "" {
|
||||
err = errors.New("Apple 连通性测试需要配置 test_transaction_id(沙箱交易 ID)")
|
||||
add("query", "failed", err.Error(), req.TradeNo, time.Now())
|
||||
@@ -162,7 +167,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*bizpa
|
||||
return test, err
|
||||
}
|
||||
if provider != bizpayment.PaymentApple {
|
||||
if order, err = orders.ApplyPaymentResult(ctx, provider, req.TradeNo, paymentTestProviderUpdate(queried)); err != nil {
|
||||
if order, err = r.orders.ApplyPaymentResult(ctx, provider, req.TradeNo, paymentTestProviderUpdate(queried)); err != nil {
|
||||
add("local_order", "failed", "回写测试查单结果失败: "+err.Error(), req.TradeNo, queryStart)
|
||||
return test, err
|
||||
}
|
||||
@@ -185,7 +190,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*bizpa
|
||||
return test, nil
|
||||
}
|
||||
refundStart := time.Now()
|
||||
order, refundToken, beginErr := orders.BeginPaymentRefund(ctx, provider, req.TradeNo, req.Amount, time.Minute)
|
||||
order, refundToken, beginErr := r.orders.BeginPaymentRefund(ctx, provider, req.TradeNo, req.Amount, time.Minute)
|
||||
if beginErr != nil {
|
||||
add("refund", "failed", beginErr.Error(), req.TradeNo, refundStart)
|
||||
return test, beginErr
|
||||
@@ -201,7 +206,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*bizpa
|
||||
add("refund", "failed", err.Error(), req.TradeNo, refundStart)
|
||||
return test, err
|
||||
}
|
||||
if _, err = orders.CompletePaymentRefundRequest(ctx, provider, req.TradeNo, refundToken, true, ""); err != nil {
|
||||
if _, err = r.orders.CompletePaymentRefundRequest(ctx, provider, req.TradeNo, refundToken, true, ""); err != nil {
|
||||
add("local_order", "failed", "回写测试退款结果失败: "+err.Error(), req.TradeNo, refundStart)
|
||||
return test, err
|
||||
}
|
||||
@@ -301,19 +306,19 @@ func (r *paymentRepo) testRow(ctx context.Context, provider string) (map[string]
|
||||
|
||||
func paymentTestRequest(provider string, values map[string]any) *bizpayment.PaymentRequest {
|
||||
tradeNo := "kra-test-" + time.Now().UTC().Format("20060102150405.000000000")
|
||||
amount := configuredInt64(values, "test_amount", 1)
|
||||
amount := paymentkit.ConfiguredInt64(values, "test_amount", 1)
|
||||
if amount <= 0 {
|
||||
amount = 1
|
||||
}
|
||||
req := &bizpayment.PaymentRequest{Provider: provider, TradeNo: strings.ReplaceAll(tradeNo, ".", ""), Subject: "Kra 支付渠道连通性测试", Amount: amount, Currency: strings.ToUpper(firstAny(values, "test_currency", "currency", "fee_type")), NotifyURL: text(values, "notify_url"), ReturnURL: text(values, "return_url"), BusinessType: "system_payment_test", BusinessID: uuid.NewString(), Extra: map[string]any{}}
|
||||
req := &bizpayment.PaymentRequest{Provider: provider, TradeNo: strings.ReplaceAll(tradeNo, ".", ""), Subject: "Kra 支付渠道连通性测试", Amount: amount, Currency: strings.ToUpper(paymentkit.FirstText(values, "test_currency", "currency", "fee_type")), NotifyURL: paymentkit.Text(values, "notify_url"), ReturnURL: paymentkit.Text(values, "return_url"), BusinessType: "system_payment_test", BusinessID: uuid.NewString(), Extra: map[string]any{}}
|
||||
if req.Currency == "" {
|
||||
req.Currency = "CNY"
|
||||
}
|
||||
if provider == bizpayment.PaymentApple {
|
||||
req.TradeNo = uuid.NewString()
|
||||
req.Extra["product_id"] = firstAny(values, "product_id", "test_product_id")
|
||||
req.Extra["product_id"] = paymentkit.FirstText(values, "product_id", "test_product_id")
|
||||
}
|
||||
if raw := strings.TrimSpace(text(values, "test_extra")); raw != "" {
|
||||
if raw := strings.TrimSpace(paymentkit.Text(values, "test_extra")); raw != "" {
|
||||
var extra map[string]any
|
||||
if json.Unmarshal([]byte(raw), &extra) == nil {
|
||||
for key, value := range extra {
|
||||
@@ -322,7 +327,7 @@ func paymentTestRequest(provider string, values map[string]any) *bizpayment.Paym
|
||||
}
|
||||
}
|
||||
for _, key := range []string{"trade_type", "method", "pay_type", "channel", "openid", "open_id", "auth_code", "authcode", "barcode"} {
|
||||
if value := text(values, key); value != "" {
|
||||
if value := paymentkit.Text(values, key); value != "" {
|
||||
req.Extra[key] = value
|
||||
}
|
||||
}
|
||||
@@ -333,16 +338,16 @@ func validatePaymentTestSettings(provider string, values map[string]any) error {
|
||||
if !testModeEnabled(values) {
|
||||
return errors.New("请先打开 test_mode(允许执行渠道测试)")
|
||||
}
|
||||
if raw := strings.TrimSpace(text(values, "test_extra")); raw != "" {
|
||||
if raw := strings.TrimSpace(paymentkit.Text(values, "test_extra")); raw != "" {
|
||||
var extra map[string]any
|
||||
if err := json.Unmarshal([]byte(raw), &extra); err != nil {
|
||||
return fmt.Errorf("test_extra 必须是 JSON 对象: %w", err)
|
||||
}
|
||||
}
|
||||
if provider == bizpayment.PaymentApple && strings.TrimSpace(text(values, "test_transaction_id")) == "" {
|
||||
if provider == bizpayment.PaymentApple && strings.TrimSpace(paymentkit.Text(values, "test_transaction_id")) == "" {
|
||||
return errors.New("Apple 测试需要 test_transaction_id(沙箱交易 ID)")
|
||||
}
|
||||
if provider == bizpayment.PaymentApple && strings.TrimSpace(firstAny(values, "test_product_id", "product_id")) == "" {
|
||||
if provider == bizpayment.PaymentApple && strings.TrimSpace(paymentkit.FirstText(values, "test_product_id", "product_id")) == "" {
|
||||
return errors.New("Apple 测试需要 test_product_id(沙箱商品 ID)")
|
||||
}
|
||||
return nil
|
||||
@@ -374,8 +379,8 @@ func (r *paymentRepo) Create(ctx context.Context, req *bizpayment.PaymentRequest
|
||||
return nil, err
|
||||
}
|
||||
effective := *req
|
||||
effective.NotifyURL = strings.TrimSpace(text(c, "notify_url"))
|
||||
effective.ReturnURL = strings.TrimSpace(text(c, "return_url"))
|
||||
effective.NotifyURL = strings.TrimSpace(paymentkit.Text(c, "notify_url"))
|
||||
effective.ReturnURL = strings.TrimSpace(paymentkit.Text(c, "return_url"))
|
||||
if paymentCreateRequiresNotifyURL(req.Provider, req.Extra, c) && effective.NotifyURL == "" {
|
||||
return nil, fmt.Errorf("支付渠道 %s 未配置服务端 notify_url", req.Provider)
|
||||
}
|
||||
@@ -412,9 +417,9 @@ func paymentCreateRequiresNotifyURL(provider string, extra, config map[string]an
|
||||
case bizpayment.PaymentLakala:
|
||||
keys = []string{"method", "pay_method", "trade_type"}
|
||||
}
|
||||
value := firstAny(extra, keys...)
|
||||
value := paymentkit.FirstText(extra, keys...)
|
||||
if value == "" {
|
||||
value = firstAny(config, keys...)
|
||||
value = paymentkit.FirstText(config, keys...)
|
||||
}
|
||||
normalized := strings.NewReplacer(".", "_", "-", "_", " ", "_").Replace(strings.ToLower(strings.TrimSpace(value)))
|
||||
switch provider {
|
||||
@@ -484,8 +489,8 @@ func paymentCallbackEventID(callback *bizpayment.PaymentCallback, result *bizpay
|
||||
if callback == nil {
|
||||
return ""
|
||||
}
|
||||
fields := callbackFields(callback)
|
||||
if eventID := strings.TrimSpace(first(fields, "event_id", "notify_id", "notificationUUID", "id")); eventID != "" {
|
||||
fields := paymentkit.CallbackFields(callback.Query, callback.Headers, callback.Body)
|
||||
if eventID := strings.TrimSpace(paymentkit.FirstString(fields, "event_id", "notify_id", "notificationUUID", "id")); eventID != "" {
|
||||
return eventID
|
||||
}
|
||||
hash := sha256.Sum256(append([]byte(callback.Provider+"\x00"), callback.Body...))
|
||||
@@ -498,12 +503,12 @@ func paymentCallbackAck(provider string, values map[string]any, success bool) bi
|
||||
if !success {
|
||||
prefix = "callback_failure_"
|
||||
}
|
||||
if configured := strings.TrimSpace(text(values, prefix+"status")); configured != "" {
|
||||
if configured := strings.TrimSpace(paymentkit.Text(values, prefix+"status")); configured != "" {
|
||||
if status, err := strconv.Atoi(configured); err == nil && status >= 200 && status <= 599 {
|
||||
ack.StatusCode = status
|
||||
}
|
||||
}
|
||||
if contentType := strings.TrimSpace(text(values, prefix+"content_type")); contentType != "" {
|
||||
if contentType := strings.TrimSpace(paymentkit.Text(values, prefix+"content_type")); contentType != "" {
|
||||
ack.ContentType = contentType
|
||||
}
|
||||
if body, exists := values[prefix+"body"]; exists {
|
||||
@@ -512,41 +517,6 @@ func paymentCallbackAck(provider string, values map[string]any, success bool) bi
|
||||
return ack
|
||||
}
|
||||
|
||||
func callbackFields(callback *bizpayment.PaymentCallback) map[string]string {
|
||||
fields := map[string]string{}
|
||||
for key, value := range callback.Query {
|
||||
fields[key] = value
|
||||
}
|
||||
contentType := strings.ToLower(callback.Headers["Content-Type"])
|
||||
if strings.Contains(contentType, "application/x-www-form-urlencoded") {
|
||||
if values, err := url.ParseQuery(string(callback.Body)); err == nil {
|
||||
for key, value := range values {
|
||||
if len(value) > 0 {
|
||||
fields[key] = value[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var object map[string]any
|
||||
if json.Unmarshal(callback.Body, &object) == nil {
|
||||
for key, value := range object {
|
||||
if text, ok := value.(string); ok {
|
||||
fields[key] = text
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func first(values map[string]string, keys ...string) string {
|
||||
for _, key := range keys {
|
||||
if values[key] != "" {
|
||||
return values[key]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func contains(values []string, value string) bool {
|
||||
for _, item := range values {
|
||||
if item == value {
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package payment
|
||||
|
||||
import "kra/internal/paymentkit"
|
||||
|
||||
func text(values map[string]any, key string) string {
|
||||
return paymentkit.Text(values, key)
|
||||
}
|
||||
|
||||
func firstAny(values map[string]any, keys ...string) string {
|
||||
return paymentkit.FirstText(values, keys...)
|
||||
}
|
||||
|
||||
// configuredInt64 keeps repository-side validation independent from the
|
||||
// provider adapter package while sharing the canonical payment parser.
|
||||
func configuredInt64(values map[string]any, key string, fallback int64) int64 {
|
||||
return paymentkit.ConfiguredInt64(values, key, fallback)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
bizpayment "kra/internal/biz/payment"
|
||||
"kra/internal/paymentkit"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -476,12 +477,12 @@ func applyProviderIdentity(tx *gorm.DB, po *paymentOrderPO, update *bizpayment.P
|
||||
}
|
||||
|
||||
func normalizeOrderPaymentStatus(value string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||
case "success", "paid", "fulfilled":
|
||||
switch paymentkit.NormalizeStatus(value, "") {
|
||||
case "success":
|
||||
return bizpayment.PaymentStatusPaid
|
||||
case "pending", "created", "client_pending", "processing":
|
||||
case "pending":
|
||||
return bizpayment.PaymentStatusPending
|
||||
case "failed", "closed", "cancelled", "canceled":
|
||||
case "failed":
|
||||
return bizpayment.PaymentStatusFailed
|
||||
default:
|
||||
return ""
|
||||
|
||||
Reference in New Issue
Block a user