Я могу получать ответы веб-хуков от Stripe через блоки методов Payola StripeEvent. Каждый ответ отображает заголовок HTTP «200 OK», когда я нажимаю кнопку «Проверить веб-зацепки» на панели инструментов Stripe. Проблема, с которой я столкнулся, заключается в том, что Payola :: StripeWebhook хранятся в базе данных, но таблица подписки и продаж не обновляется. В результате пользователи остаются на пробной версии, и я не могу регистрировать платежи, потому что их записи находятся в тупике. Мне сказали, что пропуск этих событий из-за отключений сервера / аппаратного сбоя может привести к неправильному обновлению записей. Я также вижу, что существует метод с именем Payola :: Subscription # sync_with! , который должен синхронизировать и обновлять записи. Как я могу исправить свои StripeEvents, чтобы изменить данные модели подписки / продажи с помощью Stripe Web Hook Responses?
Я создавал события для каждого ответа веб-ловушки, который, по моему мнению, был необходим. Например, я сделал событие webhook для 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']
UPDATE
изменив полосу события, пройдите по направлению к тому, что показывает драгоценный камень, и поместите его внизу. Я получаю ответ от 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