def cardThatWontBeExpiredOnFirstTransaction()

in membership-attribute-service/app/controllers/ExistingPaymentOptionsController.scala [75:144]


  def cardThatWontBeExpiredOnFirstTransaction(cardDetails: PaymentCardDetails): Boolean =
    new LocalDate(cardDetails.expiryYear, cardDetails.expiryMonth, 1).isAfter(now.plusMonths(1))

  def existingPaymentOptions(currencyFilter: Option[String]): Action[AnyContent] =
    AuthorizeForRecentLogin(ContinueRegardlessOfSignInRecency, requiredScopes = List(completeReadSelf)).async { implicit request =>
      import request.logPrefix
      metrics.measureDuration("GET /user-attributes/me/existing-payment-options") {
        implicit val tp: TouchpointComponents = request.touchpoint
        val maybeUserId = request.redirectAdvice.userId
        val isSignedInRecently = request.redirectAdvice.signInStatus == SignedInRecently

        val eligibilityDate = now.minusMonths(3)

        val defaultMandateIdIfApplicable = "CLEARED"

        def paymentMethodStillValid(paymentMethodOption: Option[PaymentMethod]) = paymentMethodOption match {
          case Some(card: PaymentCard) => card.isReferenceTransaction && card.paymentCardDetails.exists(cardThatWontBeExpiredOnFirstTransaction)
          case Some(dd: GoCardless) =>
            dd.mandateId != defaultMandateIdIfApplicable // i.e. mandateId a real reference and hasn't been cleared in Zuora because of mandate failure
          case _ => false
        }

        def paymentMethodHasNoFailures(paymentMethodOption: Option[PaymentMethod]) =
          !paymentMethodOption.flatMap(_.numConsecutiveFailures).exists(_ > 0)

        def paymentMethodIsActive(paymentMethodOption: Option[PaymentMethod]) =
          !paymentMethodOption.flatMap(_.paymentMethodStatus).contains("Closed")

        def currencyMatchesFilter(accountCurrency: Option[Currency]) =
          (accountCurrency.map(_.iso), currencyFilter) match {
            case (Some(accountCurrencyISO), Some(currencyFilterValue)) => accountCurrencyISO == currencyFilterValue
            case (None, Some(_)) => false // if the account has no currency but there is filter the account is not eligible
            case _ => true
          }

        logger.info(s"Attempting to retrieve existing payment options for identity user: ${maybeUserId.mkString}")
        val futureEitherListExistingPaymentOption = (for {
          groupedSubsList <- ListTEither.fromEitherT(
            allSubscriptionsSince(eligibilityDate, maybeUserId, tp.contactRepository, tp.subscriptionService).map(_.toList),
          )
          (accountId, subscriptions) = groupedSubsList
          objectAccount <- ListTEither.singleDisjunction(tp.zuoraRestService.getObjectAccount(accountId).recover { case x =>
            -\/[String, ObjectAccount](s"error receiving OBJECT account with account id $accountId. Reason: $x")
          }) if currencyMatchesFilter(objectAccount.currency) &&
            objectAccount.defaultPaymentMethodId.isDefined
          account <- ListTEither.singleRightT(tp.zuoraSoapService.getAccount(accountId))
          paymentMethodOption <- ListTEither.single(
            tp.paymentService
              .getPaymentMethod(account.defaultPaymentMethodId, Some(defaultMandateIdIfApplicable))
              .map(Right(_))
              .recover { case x => Left(s"error retrieving payment method for account: $accountId. Reason: $x") },
          )
          if paymentMethodStillValid(paymentMethodOption) &&
            paymentMethodHasNoFailures(paymentMethodOption) &&
            paymentMethodIsActive(paymentMethodOption)
        } yield ExistingPaymentOption(isSignedInRecently, objectAccount, paymentMethodOption, subscriptions)).run.run.map(_.toEither)

        for {
          catalog <- tp.futureCatalog
          result <- futureEitherListExistingPaymentOption.map {
            case Right(existingPaymentOptions) =>
              logger.info(s"Successfully retrieved eligible existing payment options for identity user: ${maybeUserId.mkString}")
              Ok(Json.toJson(consolidatePaymentMethod(existingPaymentOptions.toList).map(_.toJson(catalog))))
            case Left(message) =>
              logger.warn(s"Unable to retrieve eligible existing payment options for identity user ${maybeUserId.mkString} due to $message")
              InternalServerError("Failed to retrieve eligible existing payment options due to an internal error")
          }
        } yield result

      }