Proposal: Recurring Donations Platform
Now that Soapbox FE 1.0 is nearly complete, I'm getting ready to incorporate the recurring donations platform into the new system. I have already implemented this into legacy Mastodon+Soapbox, and now I'd like to incorporate the same functionality into Pleroma.
I'm planning to develop this over the course of about 1 month. I'll be working on this feature full-time.
First I'd like to gauge interest in having such a feature mainlined into Pleroma. Before I knew that Pleroma was open to merging contributions of this nature, my plan was to create a middleware service in Node.js that would intercept API calls between the frontend and Pleroma. I haven't spent much time on it, but the work I've done can be found here: https://gitlab.com/soapbox-pub/soapbox-patron
I think it is still possible that way, but it's a bit messy, as it requires keeping its own database. I'm interesting in building with Elixir going forward so I can improve my skills hacking on Pleroma. I also believe this is something every Pleroma server would benefit from.
Funding is still a barrier to expanding the Fediverse, and it would take a burden off admins if their servers were self-funded. Every server I run this on is self-funded, and I even have a little extra money left over. I believe this will do a lot to help the Fediverse grow.
Below are some screenshots showing the feature in action. I'm using Neenster.org as an example. Note that this is just how it works currently, and there are a number of things I plan to improve.
Workflow
An instance funding widget displays funding goals and progress:
Clicking "Donate" takes you to the form where you can choose your donation amount:
A pre-selected option may be chosen. Note that the "Pro" functionality pictured here would change:
Custom donations are also supported:
The user is directed to a Stripe-hosted page to enter their information:
Afterwards they're redirected back to a confirmation page with their subscription:
The subscription can be edited any time:
The subscription can be cancelled any time:
The user has the option to cancel at the end of the pay period or to refund their last donation automatically (good to avoid disputes):
Cancel confirmation:
The user can have a badge showing they're a donor to the instance. Note: This is purely a frontend feature of the local server, and I plan to change "Pro" to "Patron" in Soapbox FE:
How it works technically
-
The server admin needs to configure the server with Stripe API keys and run a Rake task the first time to preload the Stripe database. This could be done with a Mix task in Pleroma.
-
There's a donations controller (seen in the screenshots above) to CRUD the subscription. It talks to the Stripe API behind the scenes. In the new implementation it would be converted into REST API endpoints.
-
There's a webhook exposed at
/webhooks/stripe
(could be any name) configured by Stripe to receive and react to Stripe events. -
Each user has a Stripe customer ID associated with it to match it with Stripe's database.
-
Tests (I would be more thorough in the new implementation)
New API Proposal
/pleroma/patron/v1/funding
GET Public endpoint.
Returns funding data for the server.
Can be used to display a funding bar.
Example 200 response:
{
"amount": 6050,
"amount_formatted": "$60.50",
"patrons": 4,
"currency": "usd",
"interval": "monthly",
"goals": [{
"amount": 4500,
"amount_formatted": "$45.00",
"text": "Monthly server costs covered."
}, {
"amount": 7500,
"amount_formatted": "$75.00",
"text": "All operating expenses covered, including servers, media storage, email, and DDoS protection."
}, {
"amount": 50000,
"amount_formatted": "$500.00",
"text": "@alex will be able to work part-time implementing new features. Thank you for your generosity!"
}]
}
/pleroma/patron/v1/accounts/:id
GET Public endpont.
Returns Patron-related metadata about the given Account.
The ID must be a valid Account ID in the backend service.
Example 200 response:
{
"id": "9tSK9gZfIRaCbMN3ey",
"is_patron": true,
"customizations": {
"background": "floral"
}
}
/pleroma/patron/v1/me
GET Requires Authorization
HTTP header.
Returns plan (if any) and payment history for the given user.
Example 200 response:
{
"plan": {
"amount": 500,
"amount_formatted": "$5.00",
"currency": "usd",
"interval": "monthly",
"created": 1583792424,
"expires": 1586715018
},
"history": [{
"type": "charge",
"amount": 500,
"amount_formatted": "$5.00",
"created": 1583792424
}]
}
/pleroma/patron/v1/plan
POST Requires Authorization
HTTP header.
Creates a new plan for the user.
Returns 409 (conflict) if the user already has a plan.
Request parameters:
-
amount
Number -
currency
String ("usd") -
interval
String ("monthly") -
payment
Object
The payment
object looks like this:
{
"type": "stripe",
"source": "tok_xxx"
}
The token must be generated on the frontend using Stripe.js. Credit card data is sent directly from the user's browser to Stripe's server, and we perform logic on the token received in return.
Example 200 response (it returns the plan):
{
"amount": 500,
"amount_formatted": "$5.00",
"currency": "usd",
"interval": "monthly",
"created": 1583792424,
"expires": 1586715018
}
/pleroma/patron/v1/plan
PATCH Requires Authorization
HTTP header.
Updates the user's plan with the given parameters.
Request parameters: Same as POST /patron/v1/plan
, but all optional.
200 response: Same as POST /patron/v1/plan
.
pleroma/patron/v1/plan
DELETE Requires Authorization
HTTP header.
Deletes the user's plan.
/pleroma/patron/v1/goals
PUT Requires Authorization
HTTP header.
The authorized user must be an admin.
Updates the site goals.
Challenges
-
I have only supported USD so far. It will definitely be possible to support others, though.
-
This system is not payment-processor agnostic. It only works with Stripe. I could do my best to separate out the logic, but feature-set between payment processors are very different.
-
The frontend will need to load a proprietary
stripe.js
script in order to launch the Stripe Checkout page. There might be a way around this, but I couldn't figure it out. On that note, this feature also relies on a proprietary 3rd-party backend service. However, this is payment processing, and unless we're using Bitcoin this isn't likely to change. The feature would not require any proprietary code to be added to Pleroma itself, and the frontends would hotlink tostripe.js
, loading it only when needed (it could even display a warning).