remove donation module
parent
89fedd19a9
commit
1c24602a60
|
@ -133,7 +133,6 @@ object Env {
|
|||
def coordinate = lila.coordinate.Env.current
|
||||
def tv = lila.tv.Env.current
|
||||
def blog = lila.blog.Env.current
|
||||
def donation = lila.donation.Env.current
|
||||
def qa = lila.qa.Env.current
|
||||
def history = lila.history.Env.current
|
||||
def worldMap = lila.worldMap.Env.current
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import play.api.mvc._, Results._
|
||||
|
||||
import lila.app._
|
||||
import views._
|
||||
|
||||
object Donation extends LilaController {
|
||||
|
||||
def index = Open { implicit ctx =>
|
||||
OptionFuOk(Prismic.getBookmark("donate")) {
|
||||
case (doc, resolver) => Env.donation.api.list(100) zip
|
||||
Env.donation.api.top(10) map {
|
||||
case (donations, top) =>
|
||||
views.html.donation.index(doc, resolver, donations, top)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def thanks = Open { implicit ctx =>
|
||||
Redirect(routes.Plan.index).fuccess
|
||||
}
|
||||
|
||||
def thanksRedirect = Action(Redirect(routes.Donation.thanks))
|
||||
|
||||
def ipn = Action.async { implicit req =>
|
||||
Env.donation.forms.ipn.bindFromRequest.fold(
|
||||
err => {
|
||||
println(err)
|
||||
fuccess(Ok)
|
||||
},
|
||||
ipn => {
|
||||
val donation = lila.donation.Donation.make(
|
||||
payPalTnx = ipn.txnId,
|
||||
payPalSub = ipn.subId,
|
||||
userId = ipn.userId,
|
||||
email = ipn.email,
|
||||
name = ipn.name,
|
||||
gross = ipn.grossCents,
|
||||
fee = ipn.feeCents,
|
||||
message = "")
|
||||
ipn.userId.?? { userId =>
|
||||
import lila.plan.Patron.PayPal
|
||||
Env.plan.api.onPaypalCharge(
|
||||
userId = userId,
|
||||
email = ipn.email map PayPal.Email.apply,
|
||||
subId = ipn.subId map PayPal.SubId.apply,
|
||||
cents = lila.plan.Cents(ipn.grossCents),
|
||||
name = ipn.name,
|
||||
txnId = ipn.txnId)
|
||||
} >>
|
||||
Env.donation.api.create(donation) inject Ok
|
||||
})
|
||||
}
|
||||
}
|
|
@ -96,4 +96,22 @@ object Plan extends LilaController {
|
|||
def webhook = Action.async(parse.json) { req =>
|
||||
Env.plan.webhook(req.body) map { _ => Ok("kthxbye") }
|
||||
}
|
||||
|
||||
def payPalIpn = Action.async { implicit req =>
|
||||
import lila.plan.Patron.PayPal
|
||||
lila.plan.DataForm.ipn.bindFromRequest.fold(
|
||||
err => {
|
||||
println(err)
|
||||
fuccess(Ok)
|
||||
},
|
||||
ipn => Env.plan.api.onPaypalCharge(
|
||||
userId = ipn.userId,
|
||||
email = ipn.email map PayPal.Email.apply,
|
||||
subId = ipn.subId map PayPal.SubId.apply,
|
||||
cents = lila.plan.Cents(ipn.grossCents),
|
||||
name = ipn.name,
|
||||
txnId = ipn.txnId
|
||||
) inject Ok
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
@(doc: io.prismic.Document, resolver: io.prismic.DocumentLinkResolver, donations: List[lila.donation.Donation], top: List[User.ID])(implicit ctx: Context)
|
||||
|
||||
@payPalVars = {
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="custom" value="@ctx.userId">
|
||||
}
|
||||
|
||||
@side = {
|
||||
<h2>Top Donors</h2>
|
||||
<div class="donors">
|
||||
@top.map { userId =>
|
||||
<div>@userIdLink(userId.some)</div>
|
||||
}
|
||||
</div>
|
||||
<h2>Recent Donors</h2>
|
||||
<div class="donors">
|
||||
@donations.take(10).map { donation =>
|
||||
<div>@userIdLink(donation.userId)</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@moreCss = {
|
||||
@cssTag("page.css")
|
||||
@cssTag("donation.css")
|
||||
}
|
||||
|
||||
@site.layout(
|
||||
moreCss = moreCss,
|
||||
side = side.some,
|
||||
title = s"${~doc.getText("doc.title")}",
|
||||
openGraph = lila.app.ui.OpenGraph(
|
||||
title = "Donate to lichess.org",
|
||||
url = s"$netBaseUrl${routes.Donation.index.url}",
|
||||
description = "Your donations help pay for the web servers, show that lichess is appreciated, and keep the developer motivated.").some) {
|
||||
|
||||
<div class="content_box small_box doc_box donate_box">
|
||||
|
||||
<h1 class="lichess_title">@doc.getText("doc.title")</h1>
|
||||
|
||||
<div class="body">
|
||||
@Html(~doc.getHtml("doc.content", resolver))
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<! -- $5 -->
|
||||
@payPalVars
|
||||
<input type="hidden" name="hosted_button_id" value="JBSUYNPRCMT9U">
|
||||
<strong>$5</strong> = <strong>3.5 hours</strong> of lichess costs:
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
</form>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<! -- $10 -->
|
||||
@payPalVars
|
||||
<input type="hidden" name="hosted_button_id" value="ZZ96PGW3UNRPQ">
|
||||
<strong>$10</strong> = <strong>7 hours</strong> of lichess costs:
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
</form>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<! -- $20 -->
|
||||
@payPalVars
|
||||
<input type="hidden" name="hosted_button_id" value="VTKN4LNQZ2FNL">
|
||||
<strong>$20</strong> = <strong>14 hours</strong> of lichess costs:
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
</form>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<! -- $50 -->
|
||||
@payPalVars
|
||||
<input type="hidden" name="hosted_button_id" value="XXYXZXY7GJQYJ">
|
||||
<strong>$50</strong> = <strong>1.5 days</strong> of lichess costs:
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
</form>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<! -- any -->
|
||||
@payPalVars
|
||||
<input type="hidden" name="hosted_button_id" value="EDV5QHA9HYGJS">
|
||||
Or specify any other amount:
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
</form>
|
||||
|
||||
<h2>Even better: monthly donation</h2>
|
||||
|
||||
<p>Because I pay for lichess servers every month, please consider a recurring donation!</p>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<! -- recurring -->
|
||||
@payPalVars
|
||||
<input type="hidden" name="hosted_button_id" value="36KH4D465UB9G">
|
||||
<input type="hidden" name="on0" value="Monthly donation">Monthly donation
|
||||
<select name="os0">
|
||||
<option value="$5">3.5 hours: $5.00 USD - monthly</option>
|
||||
<option value="$10">7 hours: $10.00 USD - monthly</option>
|
||||
<option value="$20">14 hours: $20.00 USD - monthly</option>
|
||||
<option value="$50">1.5 days: $50.00 USD - monthly</option>
|
||||
</select>
|
||||
<input type="hidden" name="currency_code" value="USD">
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribe_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
</form>
|
||||
|
||||
<h2>Thank you to all lichess.org donors!</h2>
|
||||
|
||||
<table class="slist donors">
|
||||
<tbody>
|
||||
@donations.map { donation =>
|
||||
<tr>
|
||||
<td>@showDate(donation.date)</td>
|
||||
<td>@userIdLink(donation.userId)</td>
|
||||
<td>@donation.nonEmptyMessage.map { message =>
|
||||
<em>“@message”</em>
|
||||
}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
12
conf/routes
12
conf/routes
|
@ -140,6 +140,7 @@ GET /learn controllers.Learn.index
|
|||
POST /learn/score controllers.Learn.score
|
||||
POST /learn/reset controllers.Learn.reset
|
||||
|
||||
# Patron
|
||||
GET /patron controllers.Plan.index
|
||||
POST /patron/charge controllers.Plan.charge
|
||||
GET /patron/thanks controllers.Plan.thanks
|
||||
|
@ -147,6 +148,11 @@ POST /patron/switch controllers.Plan.switch
|
|||
POST /patron/cancel controllers.Plan.cancel
|
||||
POST /patron/webhook controllers.Plan.webhook
|
||||
GET /features controllers.Plan.features
|
||||
# Donate
|
||||
GET /donate controllers.Plan.index
|
||||
GET /donate/thanks controllers.Plan.thanks
|
||||
POST /donate/thanks controllers.Plan.thanks
|
||||
POST /donate/ipn controllers.Plan.payPalIpn
|
||||
|
||||
# Round
|
||||
GET /$gameId<\w{8}> controllers.Round.watcher(gameId: String, color: String = "white")
|
||||
|
@ -426,12 +432,6 @@ GET /network/stream controllers.WorldMap.stream
|
|||
POST /mobile/register/:platform/:deviceId controllers.Main.mobileRegister(platform: String, deviceId: String)
|
||||
POST /mobile/unregister controllers.Main.mobileUnregister
|
||||
|
||||
# Donate
|
||||
GET /donate controllers.Donation.index
|
||||
GET /donate/thanks controllers.Donation.thanks
|
||||
POST /donate/thanks controllers.Donation.thanksRedirect
|
||||
POST /donate/ipn controllers.Donation.ipn
|
||||
|
||||
# Pages
|
||||
GET /thanks controllers.Page.thanks
|
||||
GET /terms-of-service controllers.Page.tos
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
package lila.donation
|
||||
|
||||
import org.joda.time.DateTime
|
||||
|
||||
case class Donation(
|
||||
_id: String, // random ID
|
||||
payPalTnx: Option[String], // PayPal transaction ID
|
||||
payPalSub: Option[String], // PayPal subscription ID
|
||||
userId: Option[String],
|
||||
email: Option[String],
|
||||
name: Option[String],
|
||||
date: DateTime,
|
||||
gross: Int, // $ cents
|
||||
fee: Int, // $ cents
|
||||
net: Int, // $ cents
|
||||
message: String,
|
||||
public: Boolean,
|
||||
publicAmount: Boolean) {
|
||||
|
||||
def nonEmptyMessage = Some(message.trim) filter (_.nonEmpty)
|
||||
}
|
||||
|
||||
object Donation {
|
||||
|
||||
def make(
|
||||
payPalTnx: Option[String],
|
||||
payPalSub: Option[String],
|
||||
email: Option[String],
|
||||
name: Option[String],
|
||||
userId: Option[String],
|
||||
gross: Int,
|
||||
fee: Int,
|
||||
message: String) = Donation(
|
||||
_id = ornicar.scalalib.Random nextStringUppercase 8,
|
||||
payPalTnx = payPalTnx,
|
||||
payPalSub = payPalSub,
|
||||
email = email,
|
||||
name = name,
|
||||
userId = userId,
|
||||
date = DateTime.now,
|
||||
gross = gross,
|
||||
fee = fee,
|
||||
net = gross - fee,
|
||||
message = message,
|
||||
public = true,
|
||||
publicAmount = false)
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package lila.donation
|
||||
|
||||
import org.joda.time.{ DateTime, DateTimeConstants }
|
||||
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
|
||||
import reactivemongo.bson._
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.Try
|
||||
|
||||
import lila.db.BSON.BSONJodaDateTimeHandler
|
||||
import lila.db.dsl._
|
||||
|
||||
final class DonationApi(
|
||||
coll: Coll,
|
||||
weeklyGoal: Int,
|
||||
bus: lila.common.Bus) {
|
||||
|
||||
private implicit val donationBSONHandler = Macros.handler[Donation]
|
||||
|
||||
private val minAmount = 200
|
||||
|
||||
// in $ cents
|
||||
private def donatedByUser(userId: String): Fu[Int] =
|
||||
coll.aggregate(
|
||||
Match(decentAmount ++ BSONDocument("userId" -> userId)), List(
|
||||
Group(BSONNull)("net" -> SumField("net"))
|
||||
)).map {
|
||||
~_.documents.headOption.flatMap { _.getAs[Int]("net") }
|
||||
}
|
||||
|
||||
private val decentAmount = BSONDocument("gross" -> BSONDocument("$gte" -> BSONInteger(minAmount)))
|
||||
|
||||
def list(nb: Int) = coll.find(decentAmount)
|
||||
.sort(BSONDocument("date" -> -1))
|
||||
.cursor[Donation]()
|
||||
.gather[List](nb)
|
||||
|
||||
def top(nb: Int): Fu[List[lila.user.User.ID]] = coll.aggregate(
|
||||
Match(BSONDocument("userId" -> BSONDocument("$exists" -> true))), List(
|
||||
GroupField("userId")("total" -> SumField("net")),
|
||||
Sort(Descending("total")),
|
||||
Limit(nb))).map {
|
||||
_.documents.flatMap { _.getAs[String]("_id") }
|
||||
}
|
||||
|
||||
def create(donation: Donation) = {
|
||||
coll insert donation recover
|
||||
lila.db.recoverDuplicateKey(e => println(e.getMessage)) void
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package lila.donation
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import lila.common.PimpedConfig._
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
final class Env(
|
||||
config: Config,
|
||||
db: lila.db.Env,
|
||||
bus: lila.common.Bus) {
|
||||
|
||||
private val CollectionDonation = config getString "collection.donation"
|
||||
private val WeeklyGoal = config getInt "weekly_goal"
|
||||
|
||||
def forms = DataForm
|
||||
|
||||
lazy val api = new DonationApi(
|
||||
db(CollectionDonation),
|
||||
WeeklyGoal,
|
||||
bus = bus)
|
||||
}
|
||||
|
||||
object Env {
|
||||
|
||||
lazy val current = "donation" boot new Env(
|
||||
config = lila.common.PlayApp loadConfig "donation",
|
||||
db = lila.db.Env.current,
|
||||
bus = lila.common.PlayApp.system.lilaBus)
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package lila.donation
|
||||
|
||||
import org.joda.time._
|
||||
|
||||
case class Progress(
|
||||
from: DateTime,
|
||||
goal: Int,
|
||||
current: Int) {
|
||||
|
||||
val to = from plusWeeks 1
|
||||
|
||||
val remainingDays = Days.daysBetween(DateTime.now.toLocalDate, to.toLocalDate).getDays()
|
||||
|
||||
val percent = (current * 100) / goal
|
||||
|
||||
val complete = goal >= current
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
package lila
|
||||
|
||||
package object donation extends PackageObject with WithPlay
|
|
@ -1,4 +1,4 @@
|
|||
package lila.donation
|
||||
package lila.plan
|
||||
|
||||
import play.api.data._
|
||||
import play.api.data.Forms._
|
|
@ -4,6 +4,7 @@ import lila.db.dsl._
|
|||
import lila.user.{ User, UserRepo }
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
|
||||
|
||||
final class PlanApi(
|
||||
stripeClient: StripeClient,
|
||||
|
@ -79,21 +80,21 @@ final class PlanApi(
|
|||
}
|
||||
|
||||
def onPaypalCharge(
|
||||
userId: String,
|
||||
userId: Option[String],
|
||||
email: Option[Patron.PayPal.Email],
|
||||
subId: Option[Patron.PayPal.SubId],
|
||||
cents: Cents,
|
||||
name: Option[String],
|
||||
txnId: Option[String]): Funit = (cents.value >= 500) ?? {
|
||||
txnId: Option[String]): Funit = (cents.value >= 100) ?? {
|
||||
chargeColl.insert(Charge.make(
|
||||
userId = userId.some,
|
||||
userId = userId,
|
||||
payPal = Charge.PayPal(
|
||||
name = name,
|
||||
email = email.map(_.value),
|
||||
txnId = txnId,
|
||||
subId = subId.map(_.value)).some,
|
||||
cents = cents)) >> {
|
||||
UserRepo named userId flatMap {
|
||||
cents = cents)) >>
|
||||
(userId ?? UserRepo.named) flatMap {
|
||||
_ ?? { user =>
|
||||
userPatron(user).flatMap {
|
||||
case None => patronColl.insert(Patron(
|
||||
|
@ -113,7 +114,7 @@ final class PlanApi(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def onSubscriptionDeleted(sub: StripeSubscription): Funit =
|
||||
|
@ -194,6 +195,14 @@ final class PlanApi(
|
|||
def recentChargesOf(user: User): Fu[List[Charge]] =
|
||||
chargeColl.find($doc("userId" -> user.id)).sort($doc("date" -> -1)).list[Charge]()
|
||||
|
||||
def topPatrons(nb: Int): Fu[List[User.ID]] = chargeColl.aggregate(
|
||||
Match($doc("userId" $exists true)), List(
|
||||
GroupField("userId")("total" -> SumField("cents")),
|
||||
Sort(Descending("total")),
|
||||
Limit(nb))).map {
|
||||
_.documents.flatMap { _.getAs[String]("_id") }
|
||||
}
|
||||
|
||||
private def getOrMakePlan(cents: Cents): Fu[StripePlan] =
|
||||
stripeClient.getPlan(cents) getOrElse stripeClient.makePlan(cents)
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ object ApplicationBuild extends Build {
|
|||
gameSearch, timeline, forum, forumSearch, team, teamSearch,
|
||||
analyse, mod, site, round, lobby, setup,
|
||||
importer, tournament, simul, relation, report, pref, // simulation,
|
||||
evaluation, chat, puzzle, tv, coordinate, blog, donation, qa,
|
||||
evaluation, chat, puzzle, tv, coordinate, blog, qa,
|
||||
history, worldMap, opening, video, shutup, push,
|
||||
playban, insight, perfStat, slack, quote, challenge,
|
||||
study, fishnet, explorer, learn, plan)
|
||||
|
@ -105,11 +105,6 @@ object ApplicationBuild extends Build {
|
|||
libraryDependencies ++= provided(play.api, RM, prismic)
|
||||
)
|
||||
|
||||
lazy val donation = project("donation", Seq(
|
||||
common, db, user)).settings(
|
||||
libraryDependencies ++= provided(play.api, RM)
|
||||
)
|
||||
|
||||
lazy val evaluation = project("evaluation", Seq(
|
||||
common, hub, db, user, game, analyse)).settings(
|
||||
libraryDependencies ++= provided(play.api, RM)
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
.donate_box form {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.donate_box form input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
#site_header h2 {
|
||||
font-size: 1.4em;
|
||||
margin: 30px 0 10px 0;
|
||||
}
|
||||
#site_header .donors div {
|
||||
padding: 5px 0;
|
||||
}
|
||||
#site_header a.user_link {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.donate_box .donors em {
|
||||
font-style: italic;
|
||||
}
|
||||
.donate_box .block-img img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
#site_header .progress {
|
||||
color: #fff;
|
||||
display: block;
|
||||
}
|
||||
#site_header .progress p {
|
||||
color: #888;
|
||||
}
|
||||
#site_header .progress .progressbar {
|
||||
position: relative;
|
||||
height: 30px;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
text-shadow: 0px 0px 3px rgba(0,0,0,0.8);
|
||||
text-align: right;
|
||||
line-height: 32px;
|
||||
display: block;
|
||||
}
|
||||
#site_header .progress .current {
|
||||
position:absolute;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
z-index: 2;
|
||||
height: 30px;
|
||||
max-width: 100%;
|
||||
font-size: 15px;
|
||||
}
|
||||
#site_header .progress .current span {
|
||||
margin: 0 10px;
|
||||
}
|
||||
#site_header .progress .goal {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
#site_header .progress p {
|
||||
text-align: center;
|
||||
}
|
Loading…
Reference in New Issue