Account owner then selects a plan from list of plans
Account owner enters credit card information to pay for the plan
Stripe handles monthly subscription billing from that point on
Your app receives webhooks from Stripe indicating if a subscription’s billing was successful or not.
So I think you’re on the right track with Guardian/Uberauth and Stripity-stripe. What you should also look into is Stripe Checkout, and Stripe’s webhooks.
I know you said you don’t have any experience with Ruby, but I wrote a book called Multitenancy with Rails (https://leanpub.com/multi-tenancy-rails-2) which provides a walkthrough in the last chapter for setting up subscriptions with Stripe. Perhaps if you read that you could then figure out how to accomplish the same thing in Elixir?
I would imagine you’d have a controller to receive the requests and then probably a module that would pattern match on the request params to determine what to do. I don’t have an example in front of me atm, but I think you would be able to figure it out.
I added a route to accept webhooks, but I am getting errors from CSRF protection, and I’m not sure what the canonical and secure solution to that would be. After temporarily disabling CSRF protection, I was able to get a web hook test response, but it returned an error, No signatures found matching the expected signature for payload.
Here’s how I structured my solution using stripity stripe, I wrote it around the beginning of the year with phx 1.2 and haven’t revisited the code so I’m not sure of any breaking changes.
Router:
scope "/webhook", David do
pipe_through :api
resources "/", WebhookController, only: [:index, :create]
end
Controller:
def index(conn, _params) do
conn
|> put_status(200)
|> json("hello")
end
def create(conn, params) do
case Stripe.Events.get params["id"] do
{:ok, event} ->
case event.type do
"invoice.payment_succeeded" ->
user = Repo.get_by(User, card_token: event.data["object"]["customer"])
case user do
nil ->
conn
|> put_status(200)
|> json("received")
_ ->
conn
|> put_status(200)
|> json("renewed")
end
"customer.subscription.deleted" ->
user = Repo.get_by(User, card_token: event.data["object"]["customer"])
David.Subscription.hook_cancel(user, David.Repo)
IO.inspect "#{user.email} Found and deleted"
conn
|> put_status(200)
|> json("received")
_ ->
conn
|> put_status(200)
|> json("received")
end
{:error, message} ->
conn
|> put_status(403)
|> json(message)
end
end
def hook_cancel(user, repo) do
user
|> User.nonpass_changeset
|> put_change(:sub_id, nil)
|> put_change(:card_token, nil)
|> put_change(:membership, "Free")
|> repo.update
end
You probably want to disable CSRF for that particular route, and then set an endpoint secret on stripe.
When you receive the webhook call on your controller, use the Stripe.Webhook.construct_event/3 to verify the post.
In Rails I used this:
def webhooks
payload = request.body.read
sig_header = request.env['HTTP_STRIPE_SIGNATURE']
endpoint_secret = ENV['STRIPE_WH_SECRET']
begin
event = Stripe::Webhook.construct_event(
payload, sig_header, endpoint_secret
)
rescue JSON::ParserError => e
head 403
return
rescue Stripe::SignatureVerificationError => e
head 403
return
end
pl = JSON.parse(payload)
SubscriptionManageWorker.perform_async(pl)
head :ok
end
It should be similar with Stripity, and probably you can write it more concisely