def toJson()

in membership-attribute-service/app/models/AccountDetails.scala [42:188]


    def toJson(catalog: Catalog)(implicit logPrefix: LogPrefix): JsObject = {

      val product = accountDetails.subscription.plan(catalog).product(catalog)

      val paymentMethod = paymentDetails.paymentMethod match {
        case Some(payPal: PayPalMethod) =>
          Json.obj(
            "paymentMethod" -> "PayPal",
            "payPalEmail" -> payPal.email,
          )
        case Some(card: PaymentCard) =>
          Json.obj(
            "paymentMethod" -> "Card",
            "card" -> {
              Json.obj(
                "last4" -> card.paymentCardDetails.map(_.lastFourDigits).getOrElse[String]("••••"),
                "expiry" -> card.paymentCardDetails.map(cardDetails =>
                  Json.obj(
                    "month" -> cardDetails.expiryMonth,
                    "year" -> cardDetails.expiryYear,
                  ),
                ),
                "type" -> card.cardType.getOrElse[String]("unknown"),
                "stripePublicKeyForUpdate" -> stripePublicKey,
                "email" -> email,
              )
            },
          )
        case Some(dd: GoCardless) =>
          Json.obj(
            "paymentMethod" -> "DirectDebit",
            "account" -> Json.obj( // DEPRECATED
              "accountName" -> dd.accountName,
            ),
            "mandate" -> Json.obj(
              "accountName" -> dd.accountName,
              "accountNumber" -> dd.accountNumber,
              "sortCode" -> dd.sortCode,
            ),
          )
        case Some(sepa: Sepa) =>
          Json.obj(
            "paymentMethod" -> "Sepa",
            "sepaMandate" -> Json.obj(
              "accountName" -> sepa.accountName,
              "iban" -> sepa.accountNumber,
            ),
          )
        case _ if accountHasMissedRecentPayments && safeToUpdatePaymentMethod =>
          Json.obj(
            "paymentMethod" -> "ResetRequired",
            "stripePublicKeyForCardAddition" -> stripePublicKey,
          )
        case _ => Json.obj()
      }

      def externalisePlanName(plan: RatePlan): Option[String] = plan.product(catalog) match {
        case _: Product.Weekly => if (plan.name(catalog).contains("Six for Six")) Some("currently on '6 for 6'") else None
        case _: Product.Paper => Some(plan.name(catalog).replace("+", " plus Digital Subscription"))
        case _ => None
      }

      def maybePaperDaysOfWeek(plan: RatePlan) = {
        val dayIndexes = for {
          charge <- plan.ratePlanCharges.list.toList
            .filterNot(_.pricing.isFree) // note 'Echo Legacy' rate plan has all days of week but some are zero price, this filters those out
          catalogZuoraPlan <- catalog.productRatePlans.get(plan.productRatePlanId)
          dayName <- catalogZuoraPlan.productRatePlanCharges
            .get(charge.productRatePlanChargeId)
            .collect { case benefit: PaperDay => benefit.dayOfTheWeekIndex }
        } yield dayName

        val dayNames = dayIndexes.sorted.map(DayOfWeek.of(_).getDisplayName(TextStyle.FULL, Locale.ENGLISH))

        if (dayNames.nonEmpty) Json.obj("daysOfWeek" -> dayNames) else Json.obj()
      }

      def jsonifyPlan(plan: RatePlan) = Json.obj(
        "name" -> externalisePlanName(plan),
        "start" -> plan.effectiveStartDate,
        "end" -> plan.effectiveEndDate,
        // if the customer acceptance date is future dated (e.g. 6for6) then always display, otherwise only show if starting less than 30 days from today
        "shouldBeVisible" -> (subscription.customerAcceptanceDate.isAfter(now) || plan.effectiveStartDate.isBefore(now.plusDays(30))),
        "chargedThrough" -> plan.chargedThroughDate,
        "price" -> (plan.chargesPrice.prices.head.amount * 100).toInt,
        "currency" -> plan.chargesPrice.prices.head.currency.glyph,
        "currencyISO" -> plan.chargesPrice.prices.head.currency.iso,
        "billingPeriod" -> (plan.billingPeriod
          .leftMap(e => logger.warn("unknown billing period: " + e))
          .map(_.noun)
          .getOrElse("unknown_billing_period"): String),
        "features" -> plan.features.map(_.featureCode).mkString(","),
      ) ++ maybePaperDaysOfWeek(plan)

      val subscriptionData = new FilterPlans(subscription, catalog)

      val selfServiceCancellation = SelfServiceCancellation(product, billingCountry)

      val start = subscriptionData.startDate.getOrElse(paymentDetails.customerAcceptanceDate)
      val end = subscriptionData.endDate.getOrElse(paymentDetails.termEndDate)
      Json.obj(
        "tier" -> getTier(catalog, subscription.plan(catalog)),
        "isPaidTier" -> (paymentDetails.plan.price.amount > 0f),
        "selfServiceCancellation" -> Json.obj(
          "isAllowed" -> selfServiceCancellation.isAllowed,
          "shouldDisplayEmail" -> selfServiceCancellation.shouldDisplayEmail,
          "phoneRegionsToDisplay" -> selfServiceCancellation.phoneRegionsToDisplay,
        ),
      ) ++
        regNumber.fold(Json.obj())({ reg => Json.obj("regNumber" -> reg) }) ++
        billingCountry.fold(Json.obj())({ bc => Json.obj("billingCountry" -> bc.name) }) ++
        Json.obj(
          "joinDate" -> paymentDetails.startDate,
          "optIn" -> !paymentDetails.pendingCancellation,
          "subscription" -> (paymentMethod ++ Json.obj(
            "contactId" -> accountDetails.contactId,
            "deliveryAddress" -> accountDetails.deliveryAddress,
            "safeToUpdatePaymentMethod" -> safeToUpdatePaymentMethod,
            "start" -> start,
            "end" -> end,
            "nextPaymentPrice" -> paymentDetails.nextPaymentPrice,
            "nextPaymentDate" -> paymentDetails.nextPaymentDate,
            "potentialCancellationDate" -> paymentDetails.nextInvoiceDate,
            "lastPaymentDate" -> paymentDetails.lastPaymentDate,
            "chargedThroughDate" -> paymentDetails.chargedThroughDate,
            "renewalDate" -> paymentDetails.termEndDate,
            "anniversaryDate" -> anniversary(start),
            "cancelledAt" -> paymentDetails.pendingCancellation,
            "subscriptionId" -> paymentDetails.subscriberId,
            "trialLength" -> paymentDetails.remainingTrialLength,
            "autoRenew" -> isAutoRenew,
            "plan" -> Json.obj( // TODO remove once nothing is using this key (same time as removing old deprecated endpoints)
              "name" -> paymentDetails.plan.name,
              "price" -> (paymentDetails.plan.price.amount * 100).toInt,
              "currency" -> paymentDetails.plan.price.currency.glyph,
              "currencyISO" -> paymentDetails.plan.price.currency.iso,
              "billingPeriod" -> paymentDetails.plan.interval.mkString,
            ),
            "currentPlans" -> subscriptionData.currentPlans.map(jsonifyPlan),
            "futurePlans" -> subscriptionData.futurePlans.map(jsonifyPlan),
            "readerType" -> accountDetails.subscription.readerType.value,
            "accountId" -> accountDetails.accountId,
            "cancellationEffectiveDate" -> cancellationEffectiveDate,
          )),
        ) ++ alertText.map(text => Json.obj("alertText" -> text)).getOrElse(Json.obj())

    }