travisf
I’m working through this tutorial trying to get payments working with Stripe.
I’ve implemented a Stripe webhook handler:
defmodule Postbox.StripeHandler do
@behaviour Plug
alias Plug.Conn
def init(config), do: config
def call(%{request_path: "/webhook/payments"} = conn, _params) do
signing_secret = Application.get_env(:stripity_stripe, :webhook_key)
[stripe_signature] = Plug.Conn.get_req_header(conn, "stripe-signature")
with {:ok, body, _} = Plug.Conn.read_body(conn),
{:ok, stripe_event} =
Stripe.Webhook.construct_event(body, stripe_signature, signing_secret) do
Plug.Conn.assign(conn, :stripe_event, stripe_event)
else
err ->
conn
|> Conn.send_resp(:bad_request, err)
|> Conn.halt()
end
end
end
I’ve copied my webhook secret (:webhook_key directly from the signing secret) but I’m still, continuously getting this error from Stripe: ** (MatchError) no match of right hand side value: {:error, "No signatures found matching the expected signature for payload"}.
This is a test account and I’m using Ngrok for the Webhook address.
Initial question aside I’m wondering if I’m just using Stripe.Checkout.Session do I need to even bother with Webhooks? Willl Stripe returning a successful URL be sufficient?
Trending in Questions
Hello!
Suppose you are building workflow (order / task / payment) processing system with the following requirements:
Each workflow con...
New
I’m aware of 2 such projects, are there others?
git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
Hi everyone.
My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
Hello folks!
So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Hi,
we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
Hi all, I’m new here and would like to hear peoples views on my Elixir/Phoenix based game-engine! :smiley:
Mutonex gamedesign was finish...
New
Do we have maintainer of absinthe-graphql/dataloader here? We have a PR that we’d like to merge to the library. Relatively small additive...
New
Other Trending Topics
Hey there,
I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first.
...
New
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with.
But I don’t use it anymore.
Serverless was the promis...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #ai
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #blog-post
- #ecto-query
- #elixirconf-us
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #security
- #hex










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
al2o3cr
I don’t believe it’s directly related to your issue, but note that there’s a specific callout in the docs for
Plug.Conn.read_bodysaying NOT to discard theconn:dimitarvp
Never ignore a returned
conn, that’s the new state you must use from then on! Start with that.…Oh, I see @al2o3cr beat me to it.
travisf
Thanks for pointing that out, I will be sure to use the new conn!
That said I don’t think this is causing the issue, I should have been more clear I believe the
withchain is failing atStripe.Webhook.construct_event(body, stripe_signature, signing_secret). It seems like a simple authentication error but I have checked the:webhook_key/:signing_secretand it’s the one from stripe.mize85
When is your plug called?
I think it has to run before all other parsers (plug Plug.Parsers in endpoint.ex) that may change the body, to verify the body as received from stripe.
al2o3cr
The code you posted matches what’s in the
stripity_stripedocs, so the next thing to check is all the inputs. At least one ofsigning_secret,stripe_signature, orbodyisn’t getting the value thatconstruct_eventis expecting.One thing I’d particularly watch out for is “test mode” vs “production mode”; IIRC the test mode uses a different signing secret.
Lucassifoni
You need to access the body before it has been modified by a subsequent Plug.
Here is an example in Plug.Parsers docs that exposes this very use case
Edit : note that you can also do that with a custom Parser that runs before other Plug.Parsers. You can pattern match on the request path if you wish to avoid copying the raw body for other kind of requests.
Check for exhaustiveness with the docs, because I cannot verify it today.
After that, the
%{raw_body: r}map gets merged intoconn.params. You can access it by pattern matching on conn.params in your controller.def handle_webhook(%{params: %{raw_body: raw_body}} = conn, params) doThere are many ways to get to the desired result, but the goal is the same : preserve the raw request body.
travisf
I saw that but if you have
plug App.StripeHandlerbeforeplug Plug.Parserswouldn’t that resolve the issue without writing a custom parser?For what it’s worth: the payload from
Plug.Conn.read_body(conn)looks like:Essentially just stringifyed JSON. Should it be raw JSON?
wojtekmach
This blog post might be helpful: https://dashbit.co/blog/how-we-verify-webhooks.
focused
Look at the tutorial for
stripity_stripe: https://tolc.io/blog/stripe-with-elixir-and-phoenixMaybe you don’t need to implement a plug at all because it’s already implemented in the package? Just use the behaviours in your handler
@behaviour Stripe.WebhookHandler.Yes, you need to handle the event after the checkout like I did for setup payment methods:
It’s required to handle all checkout session events. Look at the Stripe API and handle your events according to entity statuses and event types.
In my case I needed to handle the “checkout.session.completed” and then update some payment method fields in the DB to make it active in my system.
Session object API
You should look at the events you need https://docs.stripe.com/api/events/types#event_types-checkout.session.async_payment_failed and handle them as you want, e.g. update a
Paymentschema entity status.I would not rely on synchronous API responses only and strongly advice to handle events in a webhook.
P.S.: some useful docs and recommendations about using webhooks - Receive Stripe events in your webhook endpoint | Stripe Documentation
travisf
Thanks! The Tolc tutorail was one of several I worked off of. I’ve gutted everything I’ve done on this and copied from their tutorial directly and this is the result whenever I call the webhook in Stripe:
There seems to be two problems here first is that there is no route for
/webhook/paymentswhich is not specified in the tutorial.The second (related?) problem is with parsing the response, I assume if it was reaching the correct endpoint this wouldn’t be a problem (assuming everything else works correctly)?
Sorry for my slow response, I’ve been moving and this is a side project.