使用 Heroku 网址时出现 Stripe webhook 错误 400 错误请求

问题描述 投票:0回答:2

我已将 Stripe 集成到我的 Ruby on Rails 网站中。如果我用 ngrok 测试它,一切正常,但是当我使用我的 heroku 网站地址作为 stripe webhook 时,它会抛出 400 bad request 错误。如果我查找文档,它会说缺少必需的参数。难道是因为我没有ssl证书?我在 Heroku 上使用免费套餐,但 Heroku 网址以 https 开头...这不安全吗?我已经在heroku网站上输入了可发布的、秘密的和签名的密钥。

路线.rb

Rails.application.routes.draw do

  mount StripeEvent::Engine, at: '/stripe-webhooks'

  devise_for :users, controllers: {
    sessions: 'users/sessions',
    passwords: 'users/passwords',
    registrations: 'users/registrations'
  }

  scope '(:locale)', locale: /en|de/ do
    root to: 'pages#home'
    get '/about', to: 'pages#about', as: 'about'
    get '/shipping', to: 'pages#shipping', as: 'shipping'
    get '/privacypolicy', to: 'pages#privacypolicy', as: 'privacypolicy'
    get '/termsandconditions', to: 'pages#termsandconditions', as: 'termsandconditions'
    get '/success', to: 'pages#success'

    get 'contact', to: 'contacts#new', as: 'contact'


    resources :contacts, only: [:new, :create]


    get 'cart', to: 'carts#show', as: 'cart'
    delete 'carts', to: 'carts#destroy'
    delete 'cart_items/:id', to: 'cart_items#destroy', as: 'cart_items'

    resources :cart_items, only: [:create, :destroy] do
      member do
        get :add_quantity
        get :reduce_quantity
      end
    end

    post 'without-login', to: 'orders#without_login'

    resources :users
    resources :products do
      resources :cart_items, only: [:create]
    end
    resources :categories
    resources :orders, only: [:new, :show, :create] do
      resources :payments, only: :new
    end
  end
end

架构:

create_table "orders", force: :cascade do |t|
    t.string "product_sku"
    t.integer "amount_cents", default: 0, null: false
    t.string "checkout_session_id"
    t.bigint "user_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "first_name"
    t.string "last_name"
    t.string "street_name"
    t.string "house_number"
    t.string "postal_code"
    t.string "city"
    t.string "country"
    t.string "email"
    t.text "comment"
    t.integer "price_cents", default: 0, null: false
    t.boolean "termsandconditions"
    t.string "status", default: "pending"
    t.index ["user_id"], name: "index_orders_on_user_id"
  end
class PaymentsController < ApplicationController
    skip_before_action :authenticate_user!

 def new
    if current_user
      @order = current_user.orders.where(status: 'pending').find(params[:order_id])
    else
      @order = Order.find(params[:order_id])
    end
    gon.order_amount = @order.amount_cents.to_f/100
    gon.order_id = @order.id
  end

end
class OrdersController < ApplicationController
  skip_before_action :authenticate_user!

  def new
    @order = Order.new
    @cart = current_cart
    # Storing the two constants in a gon variable to send data to the JS file
    gon.ceilings = Product::ORDER_CEILINGS
    gon.prices = Product::SHIPPING_PRICES
  end

  def create
    @order = Order.new(order_params)
    @cart = current_cart
    @cart.cart_items.each { |item| @order.cart_items << item }
    @order.amount = @cart.total_price

    shipping_costs = calculate_shipping_costs(params[:order][:country], @order.amount)
    @order.amount += Monetize.parse(shipping_costs)

    @order.user = current_user if current_user
    @order.email = current_user.email if current_user

    if @order.save
      save_user_address if params[:save_address].to_i == 1
      trigger_stripe(shipping_costs)
      cleanup_cart
      redirect_to new_order_payment_path(@order)
    else
      @cart = @current_cart
      render :new
    end
  end

  def show
    if current_user
      @order = current_user.orders.find(params[:id])
    else
      @order = Order.find(params[:id])
    end

    mail = OrderMailer.with(order: @order).confirmation
    mail.deliver_now
    # may need to change this for guest users- must check that their email address is saved to the database
  end

  def index
    @orders = current_user.orders
  end

  def without_login
    session[:without_login] = true
    redirect_to new_order_path
  end

  def submit
  end


  private

  def order_params
    params.require(:order).permit(:first_name, :last_name, :email, :street_name, :house_number, :postal_code, :city, :country, :comment)
  end

  def trigger_stripe(shipping_costs)
    stripe_session = Stripe::Checkout::Session.create(
      payment_method_types: ['card'],
      customer_email: customer_email,
      locale: I18n.locale.to_s,
      line_items: stripe_line_items(@order.cart_items, shipping_costs),
      success_url: order_url(@order),
      cancel_url: order_url(@order)
    )
    @order.update(checkout_session_id: stripe_session.id)
  end

  def cleanup_cart
    @cart.cart_items.each { |item| item.update(cart_id: nil) }
    Cart.destroy(session[:cart_id])
    session[:cart_id] = nil
  end

  def stripe_line_items(order_items, shipping_costs)
    all_items = []

    order_items.each do |item|
      item_hash = {
        name: item.product.title,
        amount: (item.total_price.amount * 100).to_i / item.quantity,
        quantity: item.quantity,
        currency: 'eur'
       }
       all_items.push(item_hash)
    end

    shipping_item_hash = {
      name: "Delivery",
      amount: (shipping_costs * 100).to_i,
      quantity: 1,
      currency: 'eur'
    }
    all_items.push(shipping_item_hash)

    return all_items
  end

  def customer_email
    current_user ? current_user.email : nil
  end

  def save_user_address
    if @order.user != nil
      current_user.attributes = @order.attributes.except("id", "email", "status", "comment", "amount_cents", "amount_currency", "checkout_session_id", "user_id", "updated_at", "created_at")
      current_user.save
    end
  end
class StripeCheckoutSessionService
  def call(event)
    order = Order.find_by(checkout_session_id: event.data.object.id)
    order.update(status: 'paid')
  end
end

付款 new.html.erb:

<script src="https://js.stripe.com/v3/"></script>
<script>
  const paymentButton = document.getElementById('pay-stripe');
  paymentButton.addEventListener('click', () => {
    const stripe = Stripe('<%= ENV['STRIPE_PUBLISHABLE_KEY'] %>');
    stripe.redirectToCheckout({
      sessionId: '<%= @order.checkout_session_id %>'
    });
  });
  </script>

初始化条纹

Rails.configuration.stripe = {
  publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'],
  secret_key:      ENV['STRIPE_SECRET_KEY'],
  signing_secret:  ENV['STRIPE_WEBHOOK_SECRET_KEY']
}

Stripe.api_key = Rails.configuration.stripe[:secret_key]

StripeEvent.signing_secret = Rails.configuration.stripe[:signing_secret]

StripeEvent.configure do |events|
  events.subscribe 'checkout.session.completed', StripeCheckoutSessionService.new
end

heroku stripe-payments
2个回答
2
投票

如果从 Stripe 返回签名验证错误,StripeEvent 会响应 400 错误(请参阅 https://github.com/integrallis/stripe_event/blob/31b948d6afd4a2f82c6ad3cd973211366b48a0d8/app/controllers/stripe_event/webhook_controller.rb#L12 )。

您应该仔细检查您的签名密钥,并确保它与您的 heroku webhook 的密钥相匹配,而不是您的 ngrok webhook 的密钥。


2
投票

我也有类似的问题。 我错误地放置了测试密钥 (当我在它旁边的代码示例中创建实时密钥时,它就在那里)

以条纹形式打开您的 webhooks,然后单击

reveal secret key
。将此密钥放入您的 webhook 处理程序中

© www.soinside.com 2019 - 2024. All rights reserved.