修复支付取消与成功统计
This commit is contained in:
@@ -34,7 +34,14 @@ func (r *Repository) updateChannelStatus(ctx context.Context, paymentID uint64,
|
||||
if source == channelSourceNotify {
|
||||
updates["notified_at"] = time.Now()
|
||||
}
|
||||
return r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", paymentID).Updates(updates).Error
|
||||
return r.db.WithContext(ctx).Model(&model.PaymentOrder{}).
|
||||
Where("id = ? AND status NOT IN ?", paymentID, []string{"paid", "closed", "failed"}).
|
||||
Updates(updates).Error
|
||||
}
|
||||
|
||||
// isOrderPaymentTerminalStatus 判断订单支付流水是否已进入终态,终态不再被查询或普通回调刷回支付中。
|
||||
func isOrderPaymentTerminalStatus(status string) bool {
|
||||
return status == "paid" || status == "closed" || status == "failed"
|
||||
}
|
||||
func (r *Repository) confirmPaid(ctx context.Context, payment *model.PaymentOrder, status string, paidAt time.Time, raw map[string]string, source string) error {
|
||||
var newConvID uint64
|
||||
@@ -71,6 +78,11 @@ func (r *Repository) confirmPaid(ctx context.Context, payment *model.PaymentOrde
|
||||
if newConvID > 0 && r.orderRepo != nil {
|
||||
r.orderRepo.NotifyNewConversation(newConvID)
|
||||
}
|
||||
if latest, err := r.findPaymentByID(ctx, payment.ID); err == nil {
|
||||
if runtimeConfig, err := r.runtimeConfigForPayment(ctx, latest); err == nil {
|
||||
r.recordConfigUsage(ctx, runtimeConfig, latest)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (r *Repository) markPaymentFailed(ctx context.Context, paymentID uint64, raw map[string]string, message string) error {
|
||||
|
||||
@@ -14,11 +14,15 @@ func (r *Repository) Query(ctx context.Context, userID uint64, orderID uint64) (
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if isOrderPaymentTerminalStatus(payment.Status) {
|
||||
dto := toDTO(payment)
|
||||
return &dto, nil
|
||||
}
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(ctx, &payment)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
if payment.Status == "paid" || runtimeConfig.isMockMode() {
|
||||
if runtimeConfig.isMockMode() {
|
||||
dto := toDTO(payment)
|
||||
return &dto, nil
|
||||
}
|
||||
|
||||
@@ -150,11 +150,14 @@ func runtimeConfigFromDTO(dto *paymentconfig.ConfigDTO) *runtimePaymentConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// recordConfigUsage 记录支付配置命中情况,失败只写日志不影响主流程。
|
||||
// recordConfigUsage 记录成功支付使用的配置,失败只写日志不影响主流程。
|
||||
func (r *Repository) recordConfigUsage(ctx context.Context, runtimeConfig *runtimePaymentConfig, payment *model.PaymentOrder) {
|
||||
if r.configRepo == nil || runtimeConfig == nil || payment == nil || runtimeConfig.ID == 0 {
|
||||
return
|
||||
}
|
||||
if payment.BizType != "order_pay" || payment.Status != "paid" {
|
||||
return
|
||||
}
|
||||
if err := r.configRepo.RecordUsage(ctx, runtimeConfig.ID, payment.ID, runtimeConfig.Provider, runtimeConfig.MerchantID, payment.AmountCent, payment.BizType); err != nil {
|
||||
log.Printf("[payment] record config usage failed config_id=%d payment_id=%d err=%v", runtimeConfig.ID, payment.ID, err)
|
||||
}
|
||||
|
||||
@@ -512,3 +512,63 @@ func TestConfirmPaidRollsBackOrderWhenPaymentUpdateFails(t *testing.T) {
|
||||
t.Fatalf("asset status = %q/%q, want rollback to published/published", latestListing.Status, latestAccount.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// TestQueryClosedPaymentReturnsLocalStatus 验证关闭状态不再依赖渠道配置或继续轮询渠道。
|
||||
func TestQueryClosedPaymentReturnsLocalStatus(t *testing.T) {
|
||||
db := setupPaymentTestDB(t)
|
||||
repo := NewRepository(db, nil, nil)
|
||||
payment := model.PaymentOrder{
|
||||
PaymentNo: "PAY20260614CLOSED",
|
||||
OrderID: 9001,
|
||||
OrderNo: "ORD20260614CLOSED",
|
||||
UserID: 8001,
|
||||
Provider: "lakala",
|
||||
ThirdOrderID: "PAY20260614CLOSED",
|
||||
AmountCent: 1000,
|
||||
BizType: "order_pay",
|
||||
Status: "closed",
|
||||
}
|
||||
if err := db.Create(&payment).Error; err != nil {
|
||||
t.Fatalf("create payment failed: %v", err)
|
||||
}
|
||||
|
||||
dto, err := repo.Query(t.Context(), payment.UserID, payment.OrderID)
|
||||
if err != nil {
|
||||
t.Fatalf("Query() error = %v", err)
|
||||
}
|
||||
if dto.Status != "closed" {
|
||||
t.Fatalf("status = %q, want closed", dto.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyChannelStatusKeepsTerminalPayment 验证已关闭支付单不会被渠道普通状态重新刷回支付中。
|
||||
func TestApplyChannelStatusKeepsTerminalPayment(t *testing.T) {
|
||||
db := setupPaymentTestDB(t)
|
||||
repo := NewRepository(db, nil, nil)
|
||||
payment := model.PaymentOrder{
|
||||
PaymentNo: "PAY20260614TERMINAL",
|
||||
OrderID: 9002,
|
||||
OrderNo: "ORD20260614TERMINAL",
|
||||
UserID: 8002,
|
||||
Provider: "lakala",
|
||||
ThirdOrderID: "PAY20260614TERMINAL",
|
||||
AmountCent: 1000,
|
||||
BizType: "order_pay",
|
||||
Status: "closed",
|
||||
}
|
||||
if err := db.Create(&payment).Error; err != nil {
|
||||
t.Fatalf("create payment failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.applyChannelStatus(t.Context(), &payment, "paying", "", map[string]string{"status": "paying"}, channelSourceNotify); err != nil {
|
||||
t.Fatalf("applyChannelStatus() error = %v", err)
|
||||
}
|
||||
|
||||
var latest model.PaymentOrder
|
||||
if err := db.First(&latest, payment.ID).Error; err != nil {
|
||||
t.Fatalf("find payment failed: %v", err)
|
||||
}
|
||||
if latest.Status != "closed" {
|
||||
t.Fatalf("status = %q, want closed", latest.Status)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user