通过Payola的StripeEvent方法块,我能够从Stripe接收Web挂钩响应。当我单击Stripe仪表板上的测试webhooks按钮时,每个响应都会为我提供200 OK HTTP标头。我遇到的问题是Payola :: StripeWebhook的存储在数据库中,但是订阅和销售表没有更新。结果,用户一直处于试用状态,并且由于他们的记录已陷入僵局,因此我无法记录付款。有人告诉我,由于服务器关闭/硬件故障而丢失这些事件,可能会导致记录无法正确更新。我还看到有一种名为 Payola :: Subscription#sync_with!的方法,该方法应该同步和更新记录。如何通过Stripe Web Hook Responses修复StripeEvents以更改Subscription / Sales模型数据?
我为我认为必要的每个Web挂钩响应创建了事件。例如,我为charge.refunded,invoice.payment_failed,invoice.payment_succeeded,customer.subscription.deleted和trial_will_end创建了一个网络挂钩事件。我在Stripe的服务器上看到了响应,但是没有看到表的更新。
服务器日志
Started POST "/payola/events" for 54.187.205.235 at 2019-01-16 19:54:07 -0500
Processing by StripeEvent::WebhookController#event as XML
Parameters: {"id"=>"evt_1DtP922xuZZdQdXfrVD0Iel0", "object"=>"event", "api_version"=>"2017-06-05", "created"=>1547686448, "data"=>{"object"=>{"id"=>"biz_basic_account", "object"=>"plan", "active"=>true, "aggregate_usage"=>nil, "amount"=>4600, "billing_scheme"=>"per_unit", "created"=>1543343985, "currency"=>"usd", "interval"=>"month", "interval_count"=>1, "livemode"=>false, "metadata"=>{}, "name"=>"Business Basics | $4.00", "nickname"=>"Business Basic", "product"=>"prod_E3I35OUSByBfci", "statement_descriptor"=>nil, "tiers"=>nil, "tiers_mode"=>nil, "transform_usage"=>nil, "trial_period_days"=>3, "usage_type"=>"licensed"}, "previous_attributes"=>{"nickname"=>nil, "trial_period_days"=>nil}}, "livemode"=>false, "pending_webhooks"=>1, "request"=>{"id"=>"req_E9ztRRhsBJ1PyU", "idempotency_key"=>nil}, "type"=>"plan.updated", "webhook"=>{"id"=>"evt_1DtP922xuZZdQdXfrVD0Iel0", "object"=>"event", "api_version"=>"2017-06-05", "created"=>1547686448, "data"=>{"object"=>{"id"=>"biz_basic_account", "object"=>"plan", "active"=>true, "aggregate_usage"=>nil, "amount"=>4600, "billing_scheme"=>"per_unit", "created"=>1543343985, "currency"=>"usd", "interval"=>"month", "interval_count"=>1, "livemode"=>false, "metadata"=>{}, "name"=>"Business Basics | $4.00", "nickname"=>"Business Basic", "product"=>"prod_E3I35OUSByBfci", "statement_descriptor"=>nil, "tiers"=>nil, "tiers_mode"=>nil, "transform_usage"=>nil, "trial_period_days"=>3, "usage_type"=>"licensed"}, "previous_attributes"=>{"nickname"=>nil, "trial_period_days"=>nil}}, "livemode"=>false, "pending_webhooks"=>1, "request"=>{"id"=>"req_E9ztRRhsBJ1PyU", "idempotency_key"=>nil}, "type"=>"plan.updated"}}
Payola::StripeWebhook Exists (1.0ms) SELECT 1 AS one FROM "payola_stripe_webhooks" WHERE "payola_stripe_webhooks"."stripe_id" = $1 LIMIT $2 [["stripe_id", "evt_1DtPegrgr922xuZZdQdXfrrgegeD0rgegl0"], ["LIMIT", 1]]
(0.0ms) BEGIN
CACHE Payola::StripeWebhook Exists (0.0ms) SELECT 1 AS one FROM "payola_stripe_webhooks" WHERE "payola_stripe_webhooks"."stripe_id" = $1 LIMIT $2 [["stripe_id", "evt_1DtPegrgr922xuZZdQdXfrrgegeD0rgegl0"], ["LIMIT", 1]]
Payola::StripeWebhook Create (0.0ms) INSERT INTO "payola_stripe_webhooks" ("stripe_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["stripe_id", "evt_1DtPegrgr922xuZZdQdXfrrgegeD0rgegl0"], ["created_at", "2019-01-17 00:54:08.269998"], ["updated_at", "2019-01-17 00:54:08.269998"]]
(0.0ms) COMMIT
Completed 200 OK in 469ms (ActiveRecord: 1.0ms)
payola.rb / StripeEvent管理
Payola.configure do |config|
# Example subscription:
#
# config.subscribe 'payola.package.sale.finished' do |sale|
# EmailSender.send_an_email(sale.email)
# end
#
# In addition to any event that Stripe sends, you can subscribe
# to the following special payola events:
#
# - payola.<sellable class>.sale.finished
# - payola.<sellable class>.sale.refunded
# - payola.<sellable class>.sale.failed
#
# These events consume a Payola::Sale, not a Stripe::Event
#
# Example charge verifier:
#
# config.charge_verifier = lambda do |sale|
# raise "Nope!" if sale.email.includes?('yahoo.com')
# end
config.secret_key = ENV['STRIPE_SECRET_KEY']
config.publishable_key = ENV['STRIPE_PUBLISHABLE_KEY']
config.charge_verifier = lambda do |event|
user = User.find_by(email: event.email)
if event.is_a?(Payola::Subscription) && user.subscriptions.active.any?
raise 'Error: This user already has a subscription'
end
event.owner = user
event.save!
end
# Keep this subscription unless you want to disable refund handling
config.subscribe 'charge.refunded' do |event|
subscription = Payola::Subscription.find_by(stripe_id: event.data.object.id)
subscription.refund! unless sale.refunded?
end
Payola.subscribe 'invoice.payment_failed' do |event|
subscription = Payola::Subscription.find_by(stripe_id: event.data.object.id)
user = User.find_by(email: subscription.email)
ChargeFailedMailer.charge_failure_user(user)
user.update_attribute(:account_delinquent, true)
end
Payola.subscribe 'invoice.payment_succeeded' do |event|
subscription = Payola::Subscription.find_by(stripe_id: event.data.object.id)
user = User.find_by(email: subscription.email)
sale = Sale.find_by(stripe_id: event.data.object.id)
ChargeSucceededMailer.charge_succeeded_user(user)
user.update_attribute(:account_delinquent, false)
subscription.update_attribute(:status, event.data.object.status)
subscription.save
end
Payola.subscribe 'customer.subscription.deleted' do |event|
subscription = Payola::Subscription.find_by(stripe_id: event.data.object.id)
user = User.find_by(email: subscription.email)
sale = Sale.find_by(stripe_id: event.data.object.id)
CanceledSubscriptionMailer.subscription_canceled_for_user(user).deliver
user.destroy
end
Payola.subscribe 'customer.subscription.trial_will_end' do |event|
subscription = Payola::Subscription.find_by(stripe_id: event.data.object.id)
user = User.find_by(email: subscription.email)
sale = Sale.find_by(stripe_id: event.data.object.id)
TrialEndingMailer.trial_ending_for_user(user)
end
Payola.subscribe 'customer.subscription.updated' do |event|
puts 'Subscription updated!'
end
end
路线
Rails.application.routes.draw do
match '/subscription_expired', to: 'service_disruption#index', :via => :get
#mount Payola::Engine => '/payola', as: :payola
mount ActionCable.server => '/cable'
mount CountryStateSelect::Rails::Engine => '/'
get 'my_review/index'
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
post '/rate' => 'rater#create', :as => 'rate'
get 'update_account/index'
get 'u_help', to: 'user_help#index', as: :user_help
get 'username_validator/:username', to: 'usernames#username_validator'
devise_for :users, path: '', path_names: {sign_in: 'login', sign_out: 'logout', sign_up: 'signup'}, controllers: {registrations: 'users/registrations'}
namespace :api do
scope :v1 do
end
end
devise_scope :user do
put 'user_change_plan', to: 'users/registrations#user_change_plan'
put 'user_change_credit_card', to: 'users/registrations#user_change_credit_card'
authenticated do
root to: 'user_dashboard#index', as: 'authenticated_user_root'
end
unauthenticated do
root to: 'home#index', as: 'unauthenticated_user_root'
end
end
controller :home do
get :index, to: 'home#index', as: 'home', path: 'home'
get :pricing, to: 'home#pricing', as: 'pricing', path: 'pricing'
get :about, to: 'home#about', as: 'about', path: 'about'
get :contact, to: 'home#contact', as: 'contact', path: 'contact'
get :login_portal, to: 'home#login_portal', as: 'login_portal', path: 'login_portal'
get :signup_portal, to: 'home#signup_portal', as: 'signup_portal', path: 'signup_portal'
end
scope module: 'payola' do
mount StripeEvent::Engine => 'payola/events', as: :payola
post 'payola/buy/:product_class/:permalink' => 'transactions#create', as: :buy
get 'payola/confirm/:guid' => 'transactions#show', as: :confirm
get 'payola/status/:guid' => 'transactions#status', as: :status
post 'payola/subscribe/:plan_class/:plan_id' => 'subscriptions#create', as: :subscribe
get 'payola/confirm_subscription/:guid' => 'subscriptions#show', as: :confirm_subscription
get 'payola/subscription_status/:guid' => 'subscriptions#status', as: :subscription_status
delete 'payola/cancel_subscription/:guid' => 'subscriptions#destroy', as: :cancel_subscription
post 'payola/change_plan/:guid' => 'subscriptions#change_plan', as: :change_subscription_plan
post 'payola/change_quantity/:guid' => 'subscriptions#change_quantity', as: :change_subscription_quantity
post 'payola/update_card/:guid' => 'subscriptions#update_card', as: :update_card
post 'payola/update_customer/:id' => 'customers#update', as: :update_customer
post 'payola/create_card/:customer_id' => 'cards#create', as: :create_card
delete 'payola/destroy_card/:id/:customer_id' => 'cards#destroy', as: :destroy_card
end
root 'home#index'
end
application.js
//= require jquery3
......
//= require payola/subscription_form_onestep
//= require payola/subscription_form_twostep
//= require payola/checkout_button
//= require payola/form
//= require payola/subscription_checkout_button
//= require payola/subscription_form_register
stripe.rb
Rails.configuration.stripe = {
:publishable_key => ENV['STRIPE_PUBLISHABLE_KEY'],
:secret_key => ENV['STRIPE_SECRET_KEY']
}
Stripe.api_key = Rails.application.secrets.stripe_api_key
StripeEvent.signing_secret = ENV['STRIPE_SIGNING_SECRET']
更新 将条纹事件路线更改为宝石显示的内容并将其放置在底部之后。我收到类似下面的webhook响应。
Completed 200 OK in 260332ms (Views: 259165.4ms | ActiveRecord: 81.9ms)
Started POST "/events" for 54.187.174.169 at 2019-01-18 21:11:30 -0500
Processing by StripeEvent::WebhookController#event as XML
Parameters: {"created"=>1326853478, "livemode"=>false, "id"=>"invoice.payment_00000000000000", "type"=>"invoice.payment_succeeded", "object"=>"event", "request"=>nil, "pending_webhooks"=>1, "api_version"=>"2017-06-05", "data"=>{"object"=>{"id"=>"in_00000000000000", "object"=>"invoice", "amount_due"=>900, "amount_paid"=>900, "amount_remaining"=>0, "application_fee"=>nil, "attempt_count"=>1, "attempted"=>true, "auto_advance"=>false, "billing"=>"charge_automatically", "billing_reason"=>"subscription", "charge"=>"_00000000000000", "closed"=>true, "currency"=>"usd", "custom_fields"=>nil, "customer"=>"cus_00000000000000", "date"=>1455648779, "default_source"=>nil, "description"=>nil, "discount"=>nil, "due_date"=>nil, "ending_balance"=>0, "finalized_at"=>1455648779, "footer"=>nil, "forgiven"=>false, "hosted_invoice_url"=>"https://pay.stripe.com/invoice/invst_1cWFwm2egegegegege", "invoice_pdf"=>"https://pay.stripe.com/invoice/invst_1cWFwm20xegegegegege/pdf", "lines"=>{"data"=>[{"id"=>"sub_00000000000000", "object"=>"line_item", "amount"=>900, "currency"=>"usd", "description"=>nil, "discountable"=>true, "livemode"=>false, "metadata"=>{}, "period"=>{"end"=>1502909579, "start"=>1500231179}, "plan"=>{"id"=>"gbsubscriptionlevel1_00000000000000", "object"=>"plan", "active"=>true, "aggregate_usage"=>nil, "amount"=>900, "billing_scheme"=>"per_unit", "created"=>1455057017, "currency"=>"usd", "interval"=>"month", "interval_count"=>1, "livemode"=>false, "metadata"=>{}, "name"=>"Going Big Subscription Basic", "nickname"=>nil, "product"=>"prod_00000000000000", "statement_descriptor"=>"Going Big SUBSCRIPTION", "tiers"=>nil, "tiers_mode"=>nil, "transform_usage"=>nil, "trial_period_days"=>nil, "usage_type"=>"licensed"}, "proration"=>false, "quantity"=>1, "subscription"=>nil, "subscription_item"=>"si_00000000000000", "type"=>"subscription"}], "has_more"=>false, "object"=>"list", "url"=>"/v1/invoices/in_17fDwV2xuZZdQdXgegegegeNB8uL/lines"}, "livemode"=>false, "metadata"=>{}, "next_payment_attempt"=>nil, "number"=>nil, "paid"=>true, "period_end"=>1455648779, "period_start"=>1455648779, "receipt_number"=>nil, "starting_balance"=>0, "statement_descriptor"=>nil, "status"=>"paid", "subscription"=>"sub_00000000000000", "subtotal"=>900, "tax"=>nil, "tax_percent"=>nil, "total"=>900, "webhooks_delivered_at"=>1455648780}}, "webhook"=>{"created"=>1326853478, "livemode"=>false, "id"=>"invoice.payment_00000000000000", "type"=>"invoice.payment_succeeded", "object"=>"event", "request"=>nil, "pending_webhooks"=>1, "api_version"=>"2017-06-05", "data"=>{"object"=>{"id"=>"in_00000000000000", "object"=>"invoice", "amount_due"=>900, "amount_paid"=>900, "amount_remaining"=>0, "application_fee"=>nil, "attempt_count"=>1, "attempted"=>true, "auto_advance"=>false, "billing"=>"charge_automatically", "billing_reason"=>"subscription", "charge"=>"_00000000000000", "closed"=>true, "currency"=>"usd", "custom_fields"=>nil, "customer"=>"cus_00000000000000", "date"=>1455648779, "default_source"=>nil, "description"=>nil, "discount"=>nil, "due_date"=>nil, "ending_balance"=>0, "finalized_at"=>1455648779, "footer"=>nil, "forgiven"=>false, "hosted_invoice_url"=>"https://pay.stripe.com/invoice/invst_1cWFwm2rggegegegeg", "invoice_pdf"=>"https://pay.stripe.com/invoice/invst_1cWrgegegegegege/pdf", "lines"=>{"data"=>[{"id"=>"sub_00000000000000", "object"=>"line_item", "amount"=>900, "currency"=>"usd", "description"=>nil, "discountable"=>true, "livemode"=>false, "metadata"=>{}, "period"=>{"end"=>1502909579, "start"=>1500231179}, "plan"=>{"id"=>"gbsubscriptionlevel1_00000000000000", "object"=>"plan", "active"=>true, "aggregate_usage"=>nil, "amount"=>900, "billing_scheme"=>"per_unit", "created"=>1455057017, "currency"=>"usd", "interval"=>"month", "interval_count"=>1, "livemode"=>false, "metadata"=>{}, "name"=>"Going Big Subscription Basic", "nickname"=>nil, "product"=>"prod_00000000000000", "statement_descriptor"=>"Going Big SUBSCRIPTION", "tiers"=>nil, "tiers_mode"=>nil, "transform_usage"=>nil, "trial_period_days"=>nil, "usage_type"=>"licensed"}, "proration"=>false, "quantity"=>1, "subscription"=>nil, "subscription_item"=>"si_00000000000000", "type"=>"subscription"}], "has_more"=>false, "object"=>"list", "url"=>"/v1/invoices/in_17fDwV2xuZZdQdegergegeg8uL/lines"}, "livemode"=>false, "metadata"=>{}, "next_payment_attempt"=>nil, "number"=>nil, "paid"=>true, "period_end"=>1455648779, "period_start"=>1455648779, "receipt_number"=>nil, "starting_balance"=>0, "statement_descriptor"=>nil, "status"=>"paid", "subscription"=>"sub_00000000000000", "subtotal"=>900, "tax"=>nil, "tax_percent"=>nil, "total"=>900, "webhooks_delivered_at"=>1455648780}}}}
Payola::StripeWebhook Exists (109.9ms) SELECT 1 AS one FROM "payola_stripe_webhooks" WHERE "payola_stripe_webhooks"."stripe_id" = $1 LIMIT $2 [["stripe_id", "invoice.payment_00000000000000"], ["LIMIT", 1]]
Completed 200 OK in 379ms (ActiveRecord: 114.9ms)
更新2
我相信它现在可以正常工作。我收到了以下有关一项订阅计划的活动。
Event Details
ID
evt_1Dv7l02xuZZdQdXfaSXmAOSz
Date
2019/01/21 18:44:26
Type
customer.subscription.updated
Source
Automatic
答案 0 :(得分:0)
目前尚不清楚问题出在哪里。因此,如果您还没有尝试过,可以考虑以下几种选择:
检查Payola在mount
中config/routes.rb
。它应该看起来像这样:
mount Payola::Engine => '/payments', as: :payments
检查正在运行的后台作业,并查看作业队列以查看是否有与Payola相关的作业正在排队。
确保payola.rb
位于config/initializers/
通过运行config/initializers/payola.rb
bin/spring stop; bin/rails server
时完全重启应用程序和Spring。
在整个应用程序中检查任何StripeEvent
配置,以防意外。同样,对于任何Payola
配置。
将上述Payola.subscribe
文件中的config.subscribe
调用更新为payola.rb
。
仅在您的开发环境中,通过在任何关注点将自己的代码添加到payola-payments gem的源中来调试payola gem。通过运行bundle show payola-payments
找到其位置,然后打开在代码编辑器中打印的目录。您可以更改gem中的任何文件以帮助调试。每次更改此gem时,都需要完全重新启动以获取最新更改:bin/spring stop; bin/rails server
。完成调试后,通过运行bundle exec gem pristine payola-payments
将gem还原为原始状态。