feat(v2): outbox 退款感知(refund_id 进唯一键)+ 投递门禁放行退款态 + Enqueue 贯通 refundID

This commit is contained in:
wangjia
2026-07-10 16:26:32 +08:00
parent 0912027c69
commit ed5ec5c894
12 changed files with 84 additions and 28 deletions
+30 -5
View File
@@ -11,11 +11,11 @@ import (
func TestWebhookOutboxEnqueueIdempotent(t *testing.T) {
ws := store.NewWebhookStore(model.OpenTestDB(t))
if err := ws.EnqueueDelivery("PAY-1", "pangolin", "payment.succeeded", `{"a":1}`); err != nil {
if err := ws.EnqueueDelivery("PAY-1", "pangolin", "payment.succeeded", "", `{"a":1}`); err != nil {
t.Fatalf("enqueue#1: %v", err)
}
// 幂等:同 (out_trade_no,event_type) 再入队不新增行、不报错。
if err := ws.EnqueueDelivery("PAY-1", "pangolin", "payment.succeeded", `{"a":1}`); err != nil {
// 幂等:同 (out_trade_no,event_type,refund_id) 再入队不新增行、不报错。
if err := ws.EnqueueDelivery("PAY-1", "pangolin", "payment.succeeded", "", `{"a":1}`); err != nil {
t.Fatalf("enqueue#2: %v", err)
}
list, _ := ws.ListUndelivered(10)
@@ -33,7 +33,7 @@ func TestWebhookOutboxEnqueueIdempotent(t *testing.T) {
func TestWebhookMarkFailed(t *testing.T) {
ws := store.NewWebhookStore(model.OpenTestDB(t))
_ = ws.EnqueueDelivery("PAY-2", "jiu", "payment.succeeded", `{}`)
_ = ws.EnqueueDelivery("PAY-2", "jiu", "payment.succeeded", "", `{}`)
list, _ := ws.ListUndelivered(10)
if err := ws.MarkFailed(list[0].ID, "boom"); err != nil {
t.Fatalf("markFailed: %v", err)
@@ -46,7 +46,7 @@ func TestWebhookMarkFailed(t *testing.T) {
func TestWebhookMarkFailedUTF8Truncation(t *testing.T) {
ws := store.NewWebhookStore(model.OpenTestDB(t))
_ = ws.EnqueueDelivery("PAY-3", "pangolin", "payment.succeeded", `{}`)
_ = ws.EnqueueDelivery("PAY-3", "pangolin", "payment.succeeded", "", `{}`)
list, _ := ws.ListUndelivered(10)
// Create a long Chinese error message that exceeds 255 bytes
@@ -79,3 +79,28 @@ func TestWebhookMarkFailedUTF8Truncation(t *testing.T) {
t.Logf("UTF-8 truncated error (%d bytes): %q", len(stored), stored)
}
func TestEnqueueDeliveryRefundIDUnique(t *testing.T) {
ws := store.NewWebhookStore(model.OpenTestDB(t))
// payment:refund_id="" —— 同单同事件第二次幂等丢弃
must := func(err error) {
if err != nil {
t.Fatalf("enqueue: %v", err)
}
}
must(ws.EnqueueDelivery("PAY-1", "pangolin", "payment.succeeded", "", `{"a":1}`))
must(ws.EnqueueDelivery("PAY-1", "pangolin", "payment.succeeded", "", `{"a":2}`)) // 幂等 no-op
// refund:同单同事件、不同 refund_id —— 各自成行(部分退多次)
must(ws.EnqueueDelivery("PAY-1", "pangolin", "refund.succeeded", "rf-A", `{"r":"A"}`))
must(ws.EnqueueDelivery("PAY-1", "pangolin", "refund.succeeded", "rf-B", `{"r":"B"}`))
must(ws.EnqueueDelivery("PAY-1", "pangolin", "refund.succeeded", "rf-A", `{"r":"A2"}`)) // 同 refund_id 幂等
rows, err := ws.ListUndelivered(50)
if err != nil {
t.Fatal(err)
}
// 期望 3 行:1 payment + 2 refund(rf-A / rf-B)
if len(rows) != 3 {
t.Fatalf("undelivered rows = %d want 3: %+v", len(rows), rows)
}
}