我已将 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
如果从 Stripe 返回签名验证错误,StripeEvent 会响应 400 错误(请参阅 https://github.com/integrallis/stripe_event/blob/31b948d6afd4a2f82c6ad3cd973211366b48a0d8/app/controllers/stripe_event/webhook_controller.rb#L12 )。
您应该仔细检查您的签名密钥,并确保它与您的 heroku webhook 的密钥相匹配,而不是您的 ngrok webhook 的密钥。
我也有类似的问题。 我错误地放置了测试密钥 (当我在它旁边的代码示例中创建实时密钥时,它就在那里)。
以条纹形式打开您的 webhooks,然后单击
reveal secret key
。将此密钥放入您的 webhook 处理程序中