Skip to content

Commit 7a93792

Browse files
authored
fix(account): the processing logic for downgraded invoices webhook (#6629)
fix the processing logic for downgraded invoices webhook
1 parent 5bd384e commit 7a93792

File tree

1 file changed

+74
-9
lines changed

1 file changed

+74
-9
lines changed

service/account/api/handler_stripe_event.go

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ func parseAndValidateEvent(event *stripe.Event) (*stripe.Invoice, *stripe.Subscr
110110
// waitForInvoiceMetadata polls the invoice until metadata is set or timeout occurs
111111
// This handles the race condition in 100% discount cases where invoice.paid webhook
112112
// arrives before CreateUpgradeInvoice updates the invoice metadata
113+
// For downgrade operations, metadata is set on the subscription, not the invoice
113114
func waitForInvoiceMetadata(invoiceID string) (*stripe.Invoice, error) {
114115
const (
115116
maxAttempts = 10 // Maximum number of polling attempts
@@ -136,28 +137,92 @@ func waitForInvoiceMetadata(invoiceID string) (*stripe.Invoice, error) {
136137
}
137138

138139
// Check if critical metadata fields are present
139-
hasMetadata := inv.Metadata["subscription_operator"] != "" &&
140-
inv.Metadata["new_plan_name"] != "" &&
141-
inv.Metadata["payment_id"] != ""
140+
// For upgrade: metadata is on the invoice (subscription_operator, new_plan_name, payment_id)
141+
// For downgrade: metadata is on the subscription (subscription_operator, new_plan_name, transaction_id)
142+
operator := inv.Metadata["subscription_operator"]
143+
hasOperator := operator != ""
144+
hasPlan := inv.Metadata["new_plan_name"] != ""
145+
hasPayment := inv.Metadata["payment_id"] != ""
146+
147+
var hasMetadata bool
148+
149+
// Check if this might be a downgrade (invoice metadata is empty or missing subscription_operator)
150+
// For downgrade, DowngradePlan sets metadata on subscription, not invoice
151+
mightBeDowngrade := !hasOperator || operator == string(types.SubscriptionTransactionTypeDowngraded)
152+
153+
if mightBeDowngrade && inv.Parent != nil && inv.Parent.SubscriptionDetails != nil &&
154+
inv.Parent.SubscriptionDetails.Subscription != nil {
155+
// Check subscription metadata for downgrade operations
156+
subID := inv.Parent.SubscriptionDetails.Subscription.ID
157+
sub, err := services.StripeServiceInstance.GetSubscription(subID)
158+
if err != nil {
159+
logrus.Warnf("Failed to get subscription %s for metadata check: %v", subID, err)
160+
} else {
161+
subOperator := sub.Metadata["subscription_operator"]
162+
subHasOperator := subOperator != ""
163+
subHasPlan := sub.Metadata["new_plan_name"] != ""
164+
subHasTransactionID := sub.Metadata["transaction_id"] != ""
165+
166+
// Check if this is actually a downgrade operation
167+
if subOperator == string(types.SubscriptionTransactionTypeDowngraded) {
168+
// Downgrade requires: subscription_operator, new_plan_name, transaction_id
169+
hasMetadata = subHasOperator && subHasPlan && subHasTransactionID
170+
171+
if hasMetadata {
172+
logrus.Infof(
173+
"Subscription metadata found for downgrade after %d attempts (invoice=%s, subscription=%s)",
174+
attempt+1,
175+
invoiceID,
176+
subID,
177+
)
178+
return inv, nil
179+
}
180+
181+
logrus.Debugf(
182+
"Waiting for subscription metadata for downgrade, attempt %d/%d (invoice=%s, subscription=%s, sub_operator=%s, sub_has_operator=%v, sub_has_plan=%v, sub_has_transaction=%v)",
183+
attempt+1,
184+
maxAttempts,
185+
invoiceID,
186+
subID,
187+
subOperator,
188+
subHasOperator,
189+
subHasPlan,
190+
subHasTransactionID,
191+
)
192+
// Continue waiting for subscription metadata
193+
time.Sleep(delay)
194+
delay = time.Duration(float64(delay) * 1.5)
195+
if delay > maxDelay {
196+
delay = maxDelay
197+
}
198+
continue
199+
}
200+
}
201+
}
202+
203+
// For upgrade and other operations, check invoice metadata
204+
// Upgrade requires: subscription_operator, new_plan_name, payment_id
205+
hasMetadata = hasOperator && hasPlan && hasPayment
142206

143207
if hasMetadata {
144208
logrus.Infof(
145-
"Invoice metadata found after %d attempts (invoice=%s)",
209+
"Invoice metadata found after %d attempts (invoice=%s, operator=%s)",
146210
attempt+1,
147211
invoiceID,
212+
operator,
148213
)
149214
return inv, nil
150215
}
151216

152-
// Log waiting status
153217
logrus.Debugf(
154-
"Waiting for invoice metadata, attempt %d/%d (invoice=%s, has_operator=%v, has_plan=%v, has_payment=%v)",
218+
"Waiting for invoice metadata, attempt %d/%d (invoice=%s, operator=%s, has_operator=%v, has_plan=%v, has_payment=%v)",
155219
attempt+1,
156220
maxAttempts,
157221
invoiceID,
158-
inv.Metadata["subscription_operator"] != "",
159-
inv.Metadata["new_plan_name"] != "",
160-
inv.Metadata["payment_id"] != "",
222+
operator,
223+
hasOperator,
224+
hasPlan,
225+
hasPayment,
161226
)
162227

163228
// Wait before next poll with exponential backoff

0 commit comments

Comments
 (0)