membership-attribute-service/app/services/GuardianPatronService.scala (131 lines of code) (raw):

package services import _root_.services.stripe.BasicStripeService import com.github.nscala_time.time.Imports.DateTimeFormat import com.gu.memsub.Subscription._ import com.gu.memsub._ import com.gu.memsub.subsv2.ReaderType.Direct import com.gu.memsub.subsv2.{Subscription, _} import com.gu.monitoring.SafeLogger.LogPrefix import com.gu.services.model.PaymentDetails import com.gu.services.model.PaymentDetails.PersonalPlan import com.gu.stripe.Stripe import models.{AccountDetails, DynamoSupporterRatePlanItem} import monitoring.CreateMetrics import scalaz.std.scalaFuture._ import scalaz.{EitherT, NonEmptyList} import utils.SimpleEitherT.SimpleEitherT import scala.concurrent.{ExecutionContext, Future} class GuardianPatronService( supporterProductDataService: SupporterProductDataService, patronsStripeService: BasicStripeService, stripePatronsPublicKey: String, createMetrics: CreateMetrics, )(implicit executionContext: ExecutionContext) { private val metrics = createMetrics.forService(classOf[GuardianPatronService]) def getGuardianPatronAccountDetails(userId: String)(implicit logPrefix: LogPrefix): SimpleEitherT[List[AccountDetails]] = { metrics.measureDurationEither("getGuardianPatronAccountDetails") { for { supporterRatePlanItems <- supporterProductDataService.getSupporterRatePlanItems(userId) stripeDetails <- EitherT.rightT(getListDetailsFromStripe(supporterRatePlanItems)) } yield stripeDetails } } private def getListDetailsFromStripe(items: List[DynamoSupporterRatePlanItem])(implicit logPrefix: LogPrefix): Future[List[AccountDetails]] = { Future.sequence( items .filter(isGuardianPatronProduct) .map(_.subscriptionName) .map(fetchAccountDetailsFromStripe), ) } private def isGuardianPatronProduct(item: DynamoSupporterRatePlanItem) = item.productRatePlanId == Catalog.guardianPatronProductRatePlanId.get private def fetchAccountDetailsFromStripe(subscriptionId: String)(implicit logPrefix: LogPrefix): Future[AccountDetails] = metrics.measureDuration("fetchAccountDetailsFromStripe") { for { subscription <- patronsStripeService.fetchSubscription(subscriptionId) paymentDetails <- patronsStripeService.fetchPaymentMethod(subscription.customer.id) } yield accountDetailsFromStripeSubscription(subscription, paymentDetails, stripePatronsPublicKey) } private def billingPeriodFromInterval(interval: String): ZBillingPeriod = interval match { case "year" => ZYear case _ => ZMonth } private def accountDetailsFromStripeSubscription( subscription: Stripe.Subscription, paymentDetails: Stripe.CustomersPaymentMethods, stripePublicKey: String, ): AccountDetails = { val price = Price(subscription.plan.amount.toFloat, subscription.plan.currency) AccountDetails( contactId = "Guardian Patrons don't have a Salesforce contactId", regNumber = None, email = Some(subscription.customer.email), deliveryAddress = None, subscription = Subscription( id = Id(subscription.id), subscriptionNumber = SubscriptionNumber(subscription.id), accountId = AccountId(subscription.customer.id), contractEffectiveDate = subscription.created, customerAcceptanceDate = subscription.created, termEndDate = subscription.currentPeriodEnd, isCancelled = subscription.isCancelled, ratePlans = List( RatePlan( id = RatePlanId("guardian_patron_unused"), // only used for contribution amount change productRatePlanId = Catalog.guardianPatronProductRatePlanId, productName = "guardianpatron", lastChangeType = None, features = Nil, ratePlanCharges = NonEmptyList( RatePlanCharge( id = SubscriptionRatePlanChargeId(""), // only used for update contribution amount productRatePlanChargeId = ProductRatePlanChargeId(""), // benefit is only used for paper days (was Benefit.GuardianPatron) pricing = PricingSummary(Map(subscription.plan.currency -> price)), zBillingPeriod = Some(billingPeriodFromInterval(subscription.plan.interval)), specificBillingPeriod = None, // only used for fixed period e.g. GW 6 for 6 endDateCondition = SubscriptionEnd, upToPeriods = None, // only used for fixed periods upToPeriodsType = None, // only used for fixed periods chargedThroughDate = Some(subscription.currentPeriodEnd), effectiveStartDate = subscription.currentPeriodStart, effectiveEndDate = subscription.currentPeriodEnd, ), ), ), ), readerType = Direct, autoRenew = true, ), paymentDetails = PaymentDetails( subscriberId = subscription.id, startDate = subscription.currentPeriodStart, customerAcceptanceDate = subscription.created, chargedThroughDate = Some(subscription.currentPeriodEnd), termEndDate = subscription.currentPeriodEnd, nextPaymentPrice = Some(subscription.plan.amount), lastPaymentDate = Some(subscription.currentPeriodStart), nextPaymentDate = subscription.nextPaymentDate, nextInvoiceDate = subscription.nextPaymentDate, remainingTrialLength = 0, pendingCancellation = subscription.isPastDue, paymentMethod = paymentDetails.cardStripeList.data.headOption.map(card => PaymentCard( isReferenceTransaction = false, cardType = Some(card.`type`), paymentCardDetails = Some(PaymentCardDetails(card.last4, card.exp_month, card.exp_year)), ), ), plan = PersonalPlan("guardianpatron", price, subscription.plan.interval), ), billingCountry = None, stripePublicKey = stripePublicKey, accountHasMissedRecentPayments = subscription.isPastDue, safeToUpdatePaymentMethod = false, // TODO, this will require quite a few changes so for now we won't allow it isAutoRenew = true, alertText = None, accountId = subscription.customer.id, cancellationEffectiveDate = subscription.cancellationEffectiveDate.map(_.toString(DateTimeFormat.forPattern("yyyy-MM-dd"))), ) } }