Receiving 400 Bad Requests when using Stripe Webhooks with Ruby on Rails 7

Viewed 32

I've been having this issue for a while and decided to work on other projects instead. However, I revisited this project today and the issue is still unresolved.

This is the current flow that I'm working on:

  1. User submits screenplay form
  2. Redirects to Stripe Checkout
  3. Webhooks listen for checkout.session.completed
  4. When the session is completed, the is_paid field is updated to TRUE

1 and 2 works. 3 and 4 don't. Whenever I go through the above flow I get the following errors:

  • [400] POST http://localhost:3000/webhooks
  • Completed 400 Bad Request

I figured this may be an issue with the webhook secret. But I'm definitely using the correct one. I copy and pasted the exact webhook signing secret that Stripe CLI gives you when you stripe listen --forward-to localhost:3000/webhooks. So I'm not sure what the issue is. Also, when I run stripe trigger checkout.session.completed the trigger succeeds. Which is why I thought I may have just had the wrong webhook secret. But it's the right one. So I have no idea what's wrong. If someone could point me in the right direction it would be much appreciated. Here's the relevant code:

create method in ScreenplaysController

def create
  @screenplay = current_user.screenplays.new(screenplay_params)

  if @screenplay.save
    session = Stripe::Checkout::Session.create({
      line_items: [{
        price: "notrealforobviousreasons",
        quantity: 1,
      }],
      mode: "payment",
      metadata: { screenplay_id: @screenplay.id },
      customer_email: current_user.email,
      success_url: dashboard_url,
      cancel_url: dashboard_url,
      })
      redirect_to session.url, allow_other_host: true
  else
    render :new, status: :unprocessable_entity
  end
end

WebhooksController

class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    event = nil
    sig_header = request.env["HTTP_STRIPE_SIGNATURE"]
    payload = request.body.read
    endpoint_secret = Rails.application.credentials.dig(:stripe, :webhook_secret)

    begin
      event = Stripe::Webhook.construct_event(
        sig_header, payload, endpoint_secret
      )
    rescue JSON::ParserError => e
      head 400
      return
    rescue Stripe::SignatureVerificationError => e
      head 400
      return
    end

    case event.type
    when "checkout.session.completed"
      session = event.data.object

      @screenplay = Screenplay.find_by(id: session.metadata.screenplay_id)
      @screenplay.update(is_paid: true)
    end
  end
end

Development credentials

stripe:
  publishable_key: notrealforobviousreasons
  secret_key: notrealforobviousreasons
  webhook_secret: notrealforobviousreasons
0 Answers
Related