secure stripe webhooks and handle unsub

pull/1979/head
Thibault Duplessis 2016-06-06 16:55:05 +02:00
parent c4dff1cc1e
commit f57864a5db
3 changed files with 29 additions and 16 deletions

View File

@ -33,7 +33,8 @@ final class StripeApi(
case Some(cus) if cus.canLevelUp =>
UserRepo byId cus.userId.value flatten s"Missing user for $cus" flatMap { user =>
customerColl.updateField($id(cus.id), "lastLevelUp", DateTime.now) >>
UserRepo.setPlan(user, user.plan.fold(lila.user.Plan.init)(_.incMonths))
UserRepo.setPlan(user, user.plan.fold(lila.user.Plan.init)(_.incMonths)) >>-
logger.info(s"Charged ${charge} ${cus}")
}
case Some(cus) => fufail(s"Too early to level up $charge $cus")
}
@ -43,10 +44,13 @@ final class StripeApi(
case None => fufail(s"Deleted subscription of unknown customer $sub")
case Some(cus) =>
UserRepo byId cus.userId.value flatten s"Missing user for $cus" flatMap { user =>
UserRepo.setPlan(user, user.plan.|(lila.user.Plan.init).disable)
UserRepo.setPlan(user, user.plan.|(lila.user.Plan.init).disable) >>-
logger.info(s"Unsubed ${user.id} ${sub}")
}
}
def getEvent = client.getEvent _
private def setUserPlan(user: User, plan: StripePlan, source: Source): Fu[StripeSubscription] =
userCustomer(user) flatMap {
case None => createCustomer(user, plan, source) map { customer =>
@ -61,7 +65,8 @@ final class StripeApi(
_id = Customer.Id(customer.id),
userId = Customer.UserId(user.id),
lastLevelUp = DateTime.now)) >>
UserRepo.setPlan(user, lila.user.Plan.init) inject customer
UserRepo.setPlan(user, lila.user.Plan.init) >>-
logger.info(s"Subed ${user.id} ${plan}") inject customer
}
private def setCustomerPlan(customer: StripeCustomer, plan: StripePlan, source: Source): Fu[StripeSubscription] =

View File

@ -36,6 +36,9 @@ private final class StripeClient(config: StripeClient.Config) {
'source -> source.map(_.value),
'prorate -> false)
def getEvent(id: String): Fu[Option[JsObject]] =
getOne[JsObject](s"events/$id")
private def getOne[A: Reads](url: String, queryString: (Symbol, Any)*): Fu[Option[A]] =
get[A](url, queryString) map Some.apply recover {
case _: NotFoundException => None

View File

@ -6,19 +6,24 @@ private final class WebhookHandler(api: StripeApi) {
import JsonHandlers._
def apply(js: JsValue): Funit = ~{
def apply(js: JsValue): Funit = {
logger.debug(s"Webhook ${js.toString.take(80)}")
for {
name <- (js \ "type").asOpt[String]
data <- (js \ "data" \ "object").asOpt[JsObject]
} yield (name match {
case "charge.succeeded" =>
val charge = data.asOpt[StripeCharge] err s"Invalid charge $data"
api onCharge charge
case "customer.subscription.deleted" =>
val sub = data.asOpt[StripeSubscription] err s"Invalid subscription $data"
api onSubscriptionDeleted sub
case typ => funit
})
(js \ "id").asOpt[String] ?? api.getEvent flatMap {
case None =>
logger.warn(s"Forged webhook $js")
funit
case Some(event) => ~(for {
name <- (event \ "type").asOpt[String]
data <- (event \ "data" \ "object").asOpt[JsObject]
} yield (name match {
case "charge.succeeded" =>
val charge = data.asOpt[StripeCharge] err s"Invalid charge $data"
api onCharge charge
case "customer.subscription.deleted" =>
val sub = data.asOpt[StripeSubscription] err s"Invalid subscription $data"
api onSubscriptionDeleted sub
case typ => funit
}))
}
}
}