feat(pay-v2): P8 Task4 续费入账 invoice.paid→renewal order + subscription.renewed
This commit is contained in:
@@ -167,6 +167,56 @@ func TestVerifyWebhookWrongSecretFails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// invoice.paid + billing_reason=subscription_cycle → 归一化为 EventSubscriptionRenewal,
|
||||
// 携带 invoice/subscription/金额,供 gateway.settleRenewal 铸 renewal order。
|
||||
func TestVerifyInvoicePaidRenewal(t *testing.T) {
|
||||
ts := fakeStripeAPI(t)
|
||||
defer ts.Close()
|
||||
p := newStripe(t, ts)
|
||||
|
||||
payload := `{"id":"evt_r","object":"event","type":"invoice.paid","created":1700000000,"data":{"object":{"id":"in_123","object":"invoice","billing_reason":"subscription_cycle","total":2999,"currency":"usd","subscription":{"id":"sub_new","object":"subscription"}}}}`
|
||||
sig := signStripe(payload, whSecret, time.Now().Unix())
|
||||
|
||||
ev, err := p.VerifyCallback(context.Background(), provider.CallbackInput{
|
||||
Raw: []byte(payload),
|
||||
Headers: map[string]string{"Stripe-Signature": sig},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("verify: %v", err)
|
||||
}
|
||||
if ev.Kind != provider.EventSubscriptionRenewal {
|
||||
t.Fatalf("kind = %v, want EventSubscriptionRenewal", ev.Kind)
|
||||
}
|
||||
if ev.InvoiceRef != "in_123" || ev.SubscriptionRef != "sub_new" || ev.PaidAmountMinor != 2999 || ev.Status != provider.PaidSucceeded {
|
||||
t.Fatalf("event = %+v", ev)
|
||||
}
|
||||
if ev.PaidCurrency != "USD" {
|
||||
t.Fatalf("currency = %s, want USD", ev.PaidCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
// invoice.paid 但 billing_reason=subscription_create 是首期发票,与 checkout.session.completed
|
||||
// 是同一笔钱——由后者入账,这里必须跳过(归一化为 EventPayment,不触发续费铸单)。
|
||||
func TestVerifyInvoicePaidFirstPeriodSkipped(t *testing.T) {
|
||||
ts := fakeStripeAPI(t)
|
||||
defer ts.Close()
|
||||
p := newStripe(t, ts)
|
||||
|
||||
payload := `{"id":"evt_r2","object":"event","type":"invoice.paid","created":1700000000,"data":{"object":{"id":"in_first","object":"invoice","billing_reason":"subscription_create","total":2999,"currency":"usd","subscription":{"id":"sub_new","object":"subscription"}}}}`
|
||||
sig := signStripe(payload, whSecret, time.Now().Unix())
|
||||
|
||||
ev, err := p.VerifyCallback(context.Background(), provider.CallbackInput{
|
||||
Raw: []byte(payload),
|
||||
Headers: map[string]string{"Stripe-Signature": sig},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("verify: %v", err)
|
||||
}
|
||||
if ev.Kind != provider.EventPayment {
|
||||
t.Fatalf("kind = %v, want EventPayment(skip)", ev.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
// signStripe 复刻 Stripe webhook 签名头: t=<ts>,v1=hex(HMAC-SHA256(secret, "<ts>.<payload>"))
|
||||
func signStripe(payload, secret string, ts int64) string {
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
|
||||
Reference in New Issue
Block a user