2016-07-12 11:19:30 -06:00
|
|
|
package lila.plan
|
2016-06-06 03:36:21 -06:00
|
|
|
|
2016-06-06 08:41:04 -06:00
|
|
|
import lila.db.dsl._
|
2016-07-18 15:24:32 -06:00
|
|
|
import lila.memo._
|
2016-06-06 08:41:04 -06:00
|
|
|
import lila.user.{ User, UserRepo }
|
2016-06-06 03:36:21 -06:00
|
|
|
|
2016-06-06 08:41:04 -06:00
|
|
|
import org.joda.time.DateTime
|
2016-07-14 13:50:18 -06:00
|
|
|
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
|
2016-07-18 15:24:32 -06:00
|
|
|
import scala.concurrent.duration._
|
2016-06-06 03:36:21 -06:00
|
|
|
|
2016-07-12 11:19:30 -06:00
|
|
|
final class PlanApi(
|
|
|
|
stripeClient: StripeClient,
|
2016-07-11 10:01:44 -06:00
|
|
|
patronColl: Coll,
|
2016-07-12 10:33:22 -06:00
|
|
|
chargeColl: Coll,
|
2016-07-14 04:58:34 -06:00
|
|
|
notifier: PlanNotifier,
|
2016-07-18 14:00:57 -06:00
|
|
|
uncacheLightUser: String => Funit,
|
2016-07-14 15:11:42 -06:00
|
|
|
bus: lila.common.Bus,
|
|
|
|
payPalIpnKey: PayPalIpnKey) {
|
2016-06-06 08:41:04 -06:00
|
|
|
|
2016-07-12 10:58:39 -06:00
|
|
|
import BsonHandlers._
|
|
|
|
import PatronHandlers._
|
|
|
|
import ChargeHandlers._
|
2016-06-06 08:41:04 -06:00
|
|
|
|
2016-07-13 15:02:04 -06:00
|
|
|
def checkout(userOption: Option[User], data: Checkout): Funit =
|
2016-07-13 08:14:20 -06:00
|
|
|
getOrMakePlan(data.cents) flatMap { plan =>
|
2016-07-13 15:25:01 -06:00
|
|
|
userOption.fold(setAnonPlan(plan, data, renew = data.isMonthly)) { user =>
|
|
|
|
setUserPlan(user, plan, data, renew = data.isMonthly)
|
2016-07-13 08:46:25 -06:00
|
|
|
}
|
2016-07-13 15:02:04 -06:00
|
|
|
} void
|
|
|
|
|
2016-07-13 08:14:20 -06:00
|
|
|
def switch(user: User, cents: Cents): Fu[StripeSubscription] =
|
2016-06-06 08:41:04 -06:00
|
|
|
userCustomer(user) flatMap {
|
2016-06-06 12:18:40 -06:00
|
|
|
case None => fufail(s"Can't switch non-existent customer ${user.id}")
|
2016-06-06 08:41:04 -06:00
|
|
|
case Some(customer) =>
|
|
|
|
customer.firstSubscription match {
|
2016-07-13 08:14:20 -06:00
|
|
|
case None => fufail(s"Can't switch non-existent subscription of ${user.id}")
|
|
|
|
case Some(sub) if sub.plan.cents == cents => fuccess(sub)
|
|
|
|
case Some(sub) =>
|
|
|
|
getOrMakePlan(cents) flatMap { plan =>
|
|
|
|
stripeClient.updateSubscription(sub, plan, none)
|
|
|
|
}
|
2016-06-06 08:41:04 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-06 12:18:40 -06:00
|
|
|
def cancel(user: User): Funit =
|
|
|
|
userCustomer(user) flatMap {
|
|
|
|
case None => fufail(s"Can't cancel non-existent customer ${user.id}")
|
|
|
|
case Some(customer) =>
|
|
|
|
customer.firstSubscription match {
|
|
|
|
case None => fufail(s"Can't cancel non-existent subscription of ${user.id}")
|
2016-07-12 11:19:30 -06:00
|
|
|
case Some(sub) => stripeClient.cancelSubscription(sub) >>
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user, user.plan.disable) >>
|
2016-07-16 03:31:26 -06:00
|
|
|
patronColl.update($id(user.id), $unset("stripe", "payPal", "expiresAt")).void >>-
|
|
|
|
logger.info(s"Canceled subscription $sub of ${user.username}")
|
2016-06-06 12:18:40 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-12 11:19:30 -06:00
|
|
|
def onStripeCharge(charge: StripeCharge): Funit =
|
2016-07-12 10:33:22 -06:00
|
|
|
customerIdPatron(charge.customer) flatMap { patronOption =>
|
|
|
|
chargeColl.insert(Charge.make(
|
2016-07-14 04:03:04 -06:00
|
|
|
userId = patronOption.map(_.userId),
|
2016-07-12 10:58:39 -06:00
|
|
|
stripe = Charge.Stripe(charge.id, charge.customer).some,
|
|
|
|
cents = charge.amount)) >> {
|
2016-07-12 10:33:22 -06:00
|
|
|
patronOption match {
|
2016-07-13 08:46:25 -06:00
|
|
|
case None =>
|
|
|
|
logger.info(s"Charged anon customer $charge")
|
|
|
|
lila.mon.plan.amount(charge.amount.value)
|
|
|
|
bus.publish(lila.hub.actorApi.plan.ChargeEvent(
|
|
|
|
username = "Anonymous",
|
2016-07-16 03:49:10 -06:00
|
|
|
amount = charge.amount.value), 'plan)
|
2016-07-13 08:46:25 -06:00
|
|
|
funit
|
2016-07-14 14:43:04 -06:00
|
|
|
case Some(patron) =>
|
|
|
|
logger.info(s"Charged $charge $patron")
|
|
|
|
lila.mon.plan.amount(charge.amount.value)
|
2016-07-14 04:03:04 -06:00
|
|
|
UserRepo byId patron.userId flatten s"Missing user for $patron" flatMap { user =>
|
2016-07-14 14:43:04 -06:00
|
|
|
bus.publish(lila.hub.actorApi.plan.ChargeEvent(
|
|
|
|
username = user.username,
|
2016-07-16 03:49:10 -06:00
|
|
|
amount = charge.amount.value), 'plan)
|
2016-07-14 14:43:04 -06:00
|
|
|
val p2 = patron.copy(
|
|
|
|
stripe = Patron.Stripe(charge.customer).some
|
2016-07-14 14:51:00 -06:00
|
|
|
).levelUpIfPossible
|
2016-07-14 14:43:04 -06:00
|
|
|
patronColl.update($id(patron.id), p2) >>
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user,
|
2016-07-14 14:43:04 -06:00
|
|
|
if (patron.canLevelUp) user.plan.incMonths
|
|
|
|
else user.plan.enable)
|
2016-07-10 14:12:22 -06:00
|
|
|
}
|
2016-06-06 08:41:04 -06:00
|
|
|
}
|
2016-07-12 10:33:22 -06:00
|
|
|
}
|
2016-06-06 08:41:04 -06:00
|
|
|
}
|
|
|
|
|
2016-07-14 13:31:41 -06:00
|
|
|
def onPaypalCharge(
|
2016-07-14 13:50:18 -06:00
|
|
|
userId: Option[String],
|
2016-07-14 13:31:41 -06:00
|
|
|
email: Option[Patron.PayPal.Email],
|
|
|
|
subId: Option[Patron.PayPal.SubId],
|
|
|
|
cents: Cents,
|
|
|
|
name: Option[String],
|
2016-07-14 14:52:38 -06:00
|
|
|
txnId: Option[String],
|
2016-07-14 15:11:42 -06:00
|
|
|
ip: String,
|
|
|
|
key: PayPalIpnKey): Funit =
|
|
|
|
if (key != payPalIpnKey) {
|
|
|
|
logger.error(s"Invalid PayPal IPN key $key from $ip $userId $cents")
|
|
|
|
funit
|
|
|
|
}
|
|
|
|
else (cents.value >= 100) ?? {
|
|
|
|
chargeColl.insert(Charge.make(
|
|
|
|
userId = userId,
|
|
|
|
payPal = Charge.PayPal(
|
|
|
|
name = name,
|
|
|
|
email = email.map(_.value),
|
|
|
|
txnId = txnId,
|
|
|
|
subId = subId.map(_.value),
|
|
|
|
ip = ip.some).some,
|
|
|
|
cents = cents)) >>
|
2016-07-16 03:49:10 -06:00
|
|
|
(userId ?? UserRepo.named) flatMap { userOption =>
|
|
|
|
bus.publish(lila.hub.actorApi.plan.ChargeEvent(
|
|
|
|
username = userOption.fold("Anonymous")(_.username),
|
|
|
|
amount = cents.value), 'plan)
|
|
|
|
userOption ?? { user =>
|
2016-07-14 15:11:42 -06:00
|
|
|
val payPal = Patron.PayPal(email, subId, DateTime.now)
|
|
|
|
userPatron(user).flatMap {
|
|
|
|
case None => patronColl.insert(Patron(
|
|
|
|
_id = Patron.UserId(user.id),
|
|
|
|
payPal = payPal.some,
|
|
|
|
lastLevelUp = DateTime.now
|
|
|
|
).expireInOneMonth) >>
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user, lila.user.Plan.start) >>
|
2016-07-14 15:11:42 -06:00
|
|
|
notifier.onStart(user)
|
|
|
|
case Some(patron) =>
|
|
|
|
val p2 = patron.copy(
|
|
|
|
payPal = payPal.some
|
|
|
|
).levelUpIfPossible.expireInOneMonth
|
|
|
|
patronColl.update($id(patron.id), p2) >>
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user,
|
2016-07-14 15:11:42 -06:00
|
|
|
if (patron.canLevelUp) user.plan.incMonths
|
|
|
|
else user.plan.enable)
|
|
|
|
} >>- {
|
|
|
|
logger.info(s"Charged ${user.username} with paypal: $cents")
|
|
|
|
lila.mon.plan.amount(cents.value)
|
|
|
|
}
|
2016-07-12 11:19:30 -06:00
|
|
|
}
|
2016-07-12 10:33:22 -06:00
|
|
|
}
|
2016-07-14 15:11:42 -06:00
|
|
|
}
|
2016-07-11 10:01:44 -06:00
|
|
|
|
2016-06-06 08:41:04 -06:00
|
|
|
def onSubscriptionDeleted(sub: StripeSubscription): Funit =
|
2016-07-11 10:01:44 -06:00
|
|
|
customerIdPatron(sub.customer) flatMap {
|
2016-07-16 03:31:26 -06:00
|
|
|
case None =>
|
|
|
|
logger.warn(s"Deleted subscription of unknown patron $sub")
|
|
|
|
funit
|
2016-07-11 10:01:44 -06:00
|
|
|
case Some(patron) =>
|
2016-07-14 04:03:04 -06:00
|
|
|
UserRepo byId patron.userId flatten s"Missing user for $patron" flatMap { user =>
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user, user.plan.disable) >>
|
2016-07-14 03:26:57 -06:00
|
|
|
patronColl.update($id(user.id), patron.removeStripe).void >>-
|
2016-07-14 04:03:04 -06:00
|
|
|
logger.info(s"Unsubed ${user.username} ${sub}")
|
2016-06-06 08:41:04 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-12 11:19:30 -06:00
|
|
|
def getEvent = stripeClient.getEvent _
|
2016-06-06 08:55:05 -06:00
|
|
|
|
2016-07-14 10:27:07 -06:00
|
|
|
def customerInfo(user: User, customer: StripeCustomer): Fu[Option[CustomerInfo]] =
|
|
|
|
stripeClient.getNextInvoice(customer.id) zip
|
|
|
|
stripeClient.getPastInvoices(customer.id) map {
|
|
|
|
case (Some(nextInvoice), pastInvoices) =>
|
|
|
|
customer.firstSubscription match {
|
|
|
|
case Some(sub) => MonthlyCustomerInfo(sub, nextInvoice, pastInvoices).some
|
|
|
|
case None =>
|
|
|
|
logger.warn(s"Can't identify ${user.username} monthly subscription $customer")
|
|
|
|
none
|
|
|
|
}
|
|
|
|
case (None, _) =>
|
|
|
|
customer.firstSubscription match {
|
|
|
|
case Some(sub) => OneTimeCustomerInfo(customer, sub).some
|
|
|
|
case None =>
|
|
|
|
logger.warn(s"Can't identify ${user.username} one-time subscription $customer")
|
2016-07-11 10:01:44 -06:00
|
|
|
none
|
|
|
|
}
|
2016-06-06 11:50:13 -06:00
|
|
|
}
|
|
|
|
|
2016-07-14 03:26:57 -06:00
|
|
|
import PlanApi.SyncResult.{ ReloadUser, Synced }
|
|
|
|
|
|
|
|
def sync(user: User): Fu[PlanApi.SyncResult] = userPatron(user) flatMap {
|
2016-07-11 16:55:35 -06:00
|
|
|
|
2016-07-12 10:58:39 -06:00
|
|
|
case None if user.plan.active =>
|
2016-07-11 16:55:35 -06:00
|
|
|
logger.warn(s"sync: disable plan of non-patron")
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user, user.plan.disable) inject ReloadUser
|
2016-07-11 16:55:35 -06:00
|
|
|
|
2016-07-14 10:27:07 -06:00
|
|
|
case None => fuccess(Synced(none, none))
|
2016-07-11 16:55:35 -06:00
|
|
|
|
|
|
|
case Some(patron) => (patron.stripe, patron.payPal) match {
|
|
|
|
|
2016-07-12 11:19:30 -06:00
|
|
|
case (Some(stripe), _) => stripeClient.getCustomer(stripe.customerId) flatMap {
|
2016-07-11 16:55:35 -06:00
|
|
|
case None =>
|
|
|
|
logger.warn(s"sync: unset DB patron that's not in stripe")
|
2016-07-14 03:26:57 -06:00
|
|
|
patronColl.update($id(patron.id), patron.removeStripe) >> sync(user)
|
2016-07-11 16:55:35 -06:00
|
|
|
case Some(customer) if customer.firstSubscription.isEmpty =>
|
|
|
|
logger.warn(s"sync: unset DB patron of customer without a subscription")
|
2016-07-14 03:26:57 -06:00
|
|
|
patronColl.update($id(patron.id), patron.removeStripe) >> sync(user)
|
2016-07-11 16:55:35 -06:00
|
|
|
case Some(customer) if customer.firstSubscription.isDefined && !user.plan.active =>
|
|
|
|
logger.warn(s"sync: enable plan of customer with a subscription")
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user, user.plan.enable) inject ReloadUser
|
2016-07-14 10:27:07 -06:00
|
|
|
case customer => fuccess(Synced(patron.some, customer))
|
2016-07-11 16:55:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
case (_, Some(paypal)) =>
|
2016-07-14 11:24:45 -06:00
|
|
|
if (!user.plan.active) {
|
2016-07-11 16:55:35 -06:00
|
|
|
logger.warn(s"sync: enable plan of customer with paypal")
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user, user.plan.enable) inject ReloadUser
|
2016-07-11 16:55:35 -06:00
|
|
|
}
|
2016-07-14 10:27:07 -06:00
|
|
|
else fuccess(Synced(patron.some, none))
|
2016-07-11 16:55:35 -06:00
|
|
|
|
|
|
|
case (None, None) if user.plan.active =>
|
|
|
|
logger.warn(s"sync: disable plan of patron with no paypal or stripe")
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user, user.plan.disable) inject ReloadUser
|
2016-07-11 16:55:35 -06:00
|
|
|
|
2016-07-14 10:27:07 -06:00
|
|
|
case _ => fuccess(Synced(patron.some, none))
|
2016-06-06 11:50:13 -06:00
|
|
|
}
|
2016-07-11 10:01:44 -06:00
|
|
|
}
|
2016-06-06 11:50:13 -06:00
|
|
|
|
2016-07-18 15:24:32 -06:00
|
|
|
private val recentChargeUserIdsCache = AsyncCache[Int, List[User.ID]](
|
|
|
|
f = nb => chargeColl.primitive[User.ID]($empty, sort = $doc("date" -> -1), nb = nb, "userId"),
|
|
|
|
timeToLive = 1 hour)
|
|
|
|
|
|
|
|
def recentChargeUserIds(nb: Int): Fu[List[User.ID]] = recentChargeUserIdsCache(nb)
|
2016-07-14 08:41:51 -06:00
|
|
|
|
2016-07-14 09:44:18 -06:00
|
|
|
def recentChargesOf(user: User): Fu[List[Charge]] =
|
|
|
|
chargeColl.find($doc("userId" -> user.id)).sort($doc("date" -> -1)).list[Charge]()
|
|
|
|
|
2016-07-18 15:24:32 -06:00
|
|
|
private val topPatronUserIdsCache = AsyncCache[Int, List[User.ID]](
|
|
|
|
f = nb => chargeColl.aggregate(
|
|
|
|
Match($doc("userId" $exists true)), List(
|
|
|
|
GroupField("userId")("total" -> SumField("cents")),
|
|
|
|
Sort(Descending("total")),
|
|
|
|
Limit(nb))).map {
|
|
|
|
_.documents.flatMap { _.getAs[User.ID]("_id") }
|
|
|
|
},
|
|
|
|
timeToLive = 1 hour)
|
|
|
|
|
|
|
|
def topPatronUserIds(nb: Int): Fu[List[User.ID]] = topPatronUserIdsCache(nb)
|
2016-07-14 13:50:18 -06:00
|
|
|
|
2016-07-14 08:41:51 -06:00
|
|
|
private def getOrMakePlan(cents: Cents): Fu[StripePlan] =
|
2016-07-13 08:14:20 -06:00
|
|
|
stripeClient.getPlan(cents) getOrElse stripeClient.makePlan(cents)
|
|
|
|
|
2016-07-13 15:25:01 -06:00
|
|
|
private def setAnonPlan(plan: StripePlan, data: Checkout, renew: Boolean): Fu[StripeSubscription] =
|
2016-07-13 08:46:25 -06:00
|
|
|
stripeClient.createAnonCustomer(plan, data) map { customer =>
|
2016-07-13 15:25:01 -06:00
|
|
|
logger.info(s"Subed anon $customer to ${plan} renew=$renew")
|
2016-07-13 08:46:25 -06:00
|
|
|
customer.firstSubscription err s"Can't create anon $customer subscription to $plan"
|
2016-07-13 15:25:01 -06:00
|
|
|
} flatMap { subscription =>
|
|
|
|
if (renew) fuccess(subscription)
|
|
|
|
else stripeClient dontRenewSubscription subscription
|
2016-07-13 08:46:25 -06:00
|
|
|
}
|
|
|
|
|
2016-07-13 15:25:01 -06:00
|
|
|
private def setUserPlan(user: User, plan: StripePlan, data: Checkout, renew: Boolean): Fu[StripeSubscription] =
|
2016-06-06 08:41:04 -06:00
|
|
|
userCustomer(user) flatMap {
|
2016-07-13 15:25:01 -06:00
|
|
|
case None => createCustomer(user, data, plan) map { customer =>
|
2016-07-14 04:03:04 -06:00
|
|
|
customer.firstSubscription err s"Can't create ${user.username} subscription for customer $customer"
|
2016-06-06 08:41:04 -06:00
|
|
|
}
|
2016-07-14 08:19:29 -06:00
|
|
|
case Some(customer) => setCustomerPlan(customer, plan, data.source) flatMap { sub =>
|
|
|
|
saveStripePatron(user, customer.id, data.isMonthly) inject sub
|
|
|
|
}
|
2016-07-13 15:25:01 -06:00
|
|
|
} flatMap { subscription =>
|
|
|
|
logger.info(s"Subed user ${user.username} $subscription renew=$renew")
|
|
|
|
if (renew) fuccess(subscription)
|
|
|
|
else stripeClient dontRenewSubscription subscription
|
2016-06-06 08:41:04 -06:00
|
|
|
}
|
|
|
|
|
2016-07-18 14:00:57 -06:00
|
|
|
private def setDbUserPlan(user: User, plan: lila.user.Plan): Funit =
|
|
|
|
UserRepo.setPlan(user, plan) >> uncacheLightUser(user.id)
|
|
|
|
|
2016-07-13 15:25:01 -06:00
|
|
|
private def createCustomer(user: User, data: Checkout, plan: StripePlan): Fu[StripeCustomer] =
|
2016-07-13 15:02:04 -06:00
|
|
|
stripeClient.createCustomer(user, data, plan) flatMap { customer =>
|
2016-07-14 03:26:57 -06:00
|
|
|
saveStripePatron(user, customer.id, data.isMonthly) >>
|
2016-07-18 14:00:57 -06:00
|
|
|
setDbUserPlan(user, lila.user.Plan.start) >>
|
2016-07-14 04:58:34 -06:00
|
|
|
notifier.onStart(user) >>-
|
2016-07-13 15:25:01 -06:00
|
|
|
logger.info(s"Create ${user.username} customer $customer") inject customer
|
2016-06-06 08:41:04 -06:00
|
|
|
}
|
|
|
|
|
2016-07-14 03:26:57 -06:00
|
|
|
private def saveStripePatron(user: User, customerId: CustomerId, renew: Boolean): Funit = userPatron(user) flatMap {
|
2016-07-12 12:25:58 -06:00
|
|
|
case None => patronColl.insert(Patron(
|
|
|
|
_id = Patron.UserId(user.id),
|
|
|
|
stripe = Patron.Stripe(customerId).some,
|
2016-07-14 03:26:57 -06:00
|
|
|
lastLevelUp = DateTime.now
|
|
|
|
).expireInOneMonth(!renew))
|
2016-07-12 12:25:58 -06:00
|
|
|
case Some(patron) => patronColl.update(
|
|
|
|
$id(patron.id),
|
2016-07-14 08:05:49 -06:00
|
|
|
patron.copy(stripe = Patron.Stripe(customerId).some).expireInOneMonth(!renew))
|
2016-07-12 12:25:58 -06:00
|
|
|
} void
|
|
|
|
|
2016-06-06 08:41:04 -06:00
|
|
|
private def setCustomerPlan(customer: StripeCustomer, plan: StripePlan, source: Source): Fu[StripeSubscription] =
|
|
|
|
customer.subscriptions.data.find(_.plan == plan) match {
|
|
|
|
case Some(sub) => fuccess(sub)
|
|
|
|
case None => customer.firstSubscription match {
|
2016-07-12 11:19:30 -06:00
|
|
|
case None => stripeClient.createSubscription(customer, plan, source)
|
|
|
|
case Some(sub) => stripeClient.updateSubscription(sub, plan, source.some)
|
2016-06-06 08:41:04 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-11 10:01:44 -06:00
|
|
|
private def userCustomerId(user: User): Fu[Option[CustomerId]] =
|
2016-07-12 12:25:58 -06:00
|
|
|
userPatron(user) map {
|
|
|
|
_.flatMap { _.stripe.map(_.customerId) }
|
|
|
|
}
|
2016-07-11 10:01:44 -06:00
|
|
|
|
2016-06-06 08:41:04 -06:00
|
|
|
private def userCustomer(user: User): Fu[Option[StripeCustomer]] =
|
2016-07-11 10:01:44 -06:00
|
|
|
userCustomerId(user) flatMap {
|
2016-07-12 11:19:30 -06:00
|
|
|
_ ?? stripeClient.getCustomer
|
2016-06-06 08:41:04 -06:00
|
|
|
}
|
2016-07-11 10:01:44 -06:00
|
|
|
|
|
|
|
private def customerIdPatron(id: CustomerId): Fu[Option[Patron]] =
|
|
|
|
patronColl.uno[Patron](selectStripeCustomerId(id))
|
|
|
|
|
|
|
|
private def selectStripeCustomerId(id: CustomerId): Bdoc =
|
|
|
|
$doc("stripe.customerId" -> id)
|
|
|
|
|
|
|
|
private def userPatron(user: User): Fu[Option[Patron]] =
|
|
|
|
patronColl.uno[Patron]($id(user.id))
|
2016-06-06 03:36:21 -06:00
|
|
|
}
|
2016-07-14 03:26:57 -06:00
|
|
|
|
|
|
|
object PlanApi {
|
|
|
|
|
|
|
|
sealed trait SyncResult
|
|
|
|
object SyncResult {
|
|
|
|
case object ReloadUser extends SyncResult
|
2016-07-14 10:27:07 -06:00
|
|
|
case class Synced(patron: Option[Patron], customer: Option[StripeCustomer]) extends SyncResult
|
2016-07-14 03:26:57 -06:00
|
|
|
}
|
|
|
|
}
|