Bug Description
When a user has multiple active payment cards and subscribes to a new recurring plan using a specific card, the subscription is incorrectly associated with the first active card instead of the card used in the checkout.
Steps to Reproduce
- Subscribe to a monthly pro license using card ending in 4242
- Subscribe to an annual pro license using a different card ending in 3184 (3DS required)
- View the billing page
Expected: Annual subscription shows card 3184 (the card used during checkout)
Actual: Annual subscription shows card 4242 (the first active card)
Root Cause
In RecurringPaymentCommandHandler.scala (lines 104-107), the card selection uses:
paymentAccount.cards
.filterNot(_.expired)
.find(_.getActive) // Returns FIRST active card, not the intended one
.map(_.id)
The RegisterRecurringPayment command has no cardId field, so there is no way to communicate which card was used in the checkout flow. By contrast, UpdateRecurringCardPaymentRegistration correctly accepts an optional cardId.
Proposed Fix
- Add
cardId: Option[String] = None to RegisterRecurringPayment in PaymentMessages.scala
- In
RecurringPaymentCommandHandler, use cmd.cardId.orElse(paymentAccount.cards.filterNot(_.expired).find(_.getActive).map(_.id)) for backward compatibility
- Pass the selected card ID from the portal checkout flow when calling the register endpoint
Files Involved
common/src/main/scala/app/softnetwork/payment/message/PaymentMessages.scala — missing cardId field
core/src/main/scala/app/softnetwork/payment/persistence/typed/RecurringPaymentCommandHandler.scala — buggy card selection (line 104-107)
core/src/main/scala/app/softnetwork/payment/service/RecurringPaymentEndpoints.scala — endpoint doesn't pass card info
stripe/src/main/scala/app/softnetwork/payment/spi/StripeRecurringPaymentApi.scala — correctly uses card ID when passed
Bug Description
When a user has multiple active payment cards and subscribes to a new recurring plan using a specific card, the subscription is incorrectly associated with the first active card instead of the card used in the checkout.
Steps to Reproduce
Expected: Annual subscription shows card 3184 (the card used during checkout)
Actual: Annual subscription shows card 4242 (the first active card)
Root Cause
In
RecurringPaymentCommandHandler.scala(lines 104-107), the card selection uses:paymentAccount.cards .filterNot(_.expired) .find(_.getActive) // Returns FIRST active card, not the intended one .map(_.id)The
RegisterRecurringPaymentcommand has nocardIdfield, so there is no way to communicate which card was used in the checkout flow. By contrast,UpdateRecurringCardPaymentRegistrationcorrectly accepts an optionalcardId.Proposed Fix
cardId: Option[String] = NonetoRegisterRecurringPaymentinPaymentMessages.scalaRecurringPaymentCommandHandler, usecmd.cardId.orElse(paymentAccount.cards.filterNot(_.expired).find(_.getActive).map(_.id))for backward compatibilityFiles Involved
common/src/main/scala/app/softnetwork/payment/message/PaymentMessages.scala— missingcardIdfieldcore/src/main/scala/app/softnetwork/payment/persistence/typed/RecurringPaymentCommandHandler.scala— buggy card selection (line 104-107)core/src/main/scala/app/softnetwork/payment/service/RecurringPaymentEndpoints.scala— endpoint doesn't pass card infostripe/src/main/scala/app/softnetwork/payment/spi/StripeRecurringPaymentApi.scala— correctly uses card ID when passed