@@ -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
113114func 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