在收到Payola的条纹webhook回复后,我如何更新订阅和销售表



我能够通过Payola的StripeEvent方法块从Stripe接收Web挂钩响应。当我单击Stripe面板上的测试webhook按钮时,每个响应都会为我呈现一个200 OK的HTTP标头。我遇到的问题是,Payola::StripeWebhook存储在数据库中,但订阅和销售表没有更新。因此,用户仍在试用,我无法记录付款,因为他们的记录陷入僵局。我被告知,由于服务器关闭/硬件故障而错过这些事件,可能会导致记录无法正确更新。我还看到有一个名为Payola的方法:Subscription#sync_with,用于同步和更新记录。如何修复我的StripeEvents以使用Stripe Web挂钩响应更改订阅/销售模型数据?

我为每一个我认为必要的网络挂钩响应创建了事件。例如,我为charge.refunded、invoice.payment_failed、invoiice.payment_secessed、customer.subscription.deleted和trial_will_end创建了一个webhook事件。我在我的服务器上看到了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

条纹.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']

UPDATE将stripe事件路由更改为gem显示的内容并将其放在底部之后。我收到一个类似下面的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

问题究竟是什么还不清楚。因此,如果您还没有尝试过,可以考虑尝试以下几个选项:

  • 检查Payola是mount还是config/routes.rb。它应该是这样的:

    mount Payola::Engine => '/payments', as: :payments

  • 检查后台作业正在运行,并查看作业队列,以查看是否有任何与payola相关的作业正在排队。

  • 确保payola.rbconfig/initializers/

  • 通过运行bin/spring stop; bin/rails server,在任何时候更改config/initializers/payola.rb,都可以完全重新启动应用程序和Spring

  • 检查整个应用程序中的任何StripeEvent配置是否有任何意外情况。对于任何Payola配置也是如此。

  • 在上面给出的payola.rb文件中更新对config.subscribePayola.subscribe调用。

  • 仅在您的开发环境中,通过在任何兴趣点将您自己的代码添加到payola payments gem的源代码中来调试payola gem。通过运行bundle show payola-payments找到它的位置,然后打开代码编辑器中打印的目录。您可以对gem中的任何文件进行更改以帮助调试。每次对这个gem进行更改时,都需要重新启动以获取最新的更改:bin/spring stop; bin/rails server。完成调试后,通过运行bundle exec gem pristine payola-payments将gem返回到其原始状态而不进行更改。

最新更新