Новый процесс проверки Stripe SCA в Rails
Мне трудно переключить свое приложение Rails на новый поток проверки Stripe, чтобы учесть новые правила SCA.
Я хочу реализовать простую динамическую подпрограмму продукта, найденную по этой ссылке: https://stripe.com/docs/payments/checkout/migration
Я не могу понять, где разместить разные фрагменты кода. Что должно входить:
- контроллер -> в какие методы
- представления -> например, представление показа событий. Форма / кнопка, которую пользователь нажмет
- javascript -> как снова передать правильный идентификатор сеанса - контроллер -> реализация успешных и ошибочных вариантов использования
Служба технической поддержки Stripe только что отправила мне ссылку на документацию выше, поэтому я был бы очень признателен за помощь здесь.
2 ответа
Рабочий процесс Rails для новой Stripe Checkout:
Создайте сеанс Stripe Checkout и получите session.id (.rb)
Передайте session.id инициализатору js для перенаправления на Stripe Checkout
СТРИПСКАЯ КОНТРОЛЬНАЯ СЕССИЯ
Это пример реализации Stripe Checkout клиент / сервер, которую я использую для службы подписки. Ваши шаги по сути будут такими же, за исключением того, что вы будете ссылаться на продукт Stripe, а не на план:
subscriptions_controller.rb
STRIPE_API_KEY = Rails.application.credential.stripe[:secret_key]
skip_before_action :user_logged_in?, only: :stripe_webhook
protect_from_forgery except: :stripe_webhook
def stripe_webhook
stripe_response = StripeWebhooks.subscription_events(request)
end
def index
end
def new
session = StripeSession.new_session(STRIPE_API_KEY, current_user.email, params[:plan])
@stripe_session = session
end
В моем случае мой index.html.erb
В шаблоне есть ссылка "Подробнее..." о конкретной подписке. Эта ссылка ведет к действию контроллера:new, передавая информацию о соответствующем плане Stripe (или продукте) в качестве параметров. В вашем случае вы можете передать любые параметры продукта, необходимые для сеанса Stripe Checkout:
subscriptions/index.html.erb
<%= link_to 'Get more info...', new_subscription_path(plan: 'plan_xxx' %>
Действие контроллера:new вернет ваш Stripe CHECKOUT_SESSION_ID для использования в вашем шаблоне. (Также обратите внимание, что этот контроллер обходит logged_in? И защиту от подделки, чтобы разрешить ответ Stripe Webhook POST на ваш сеанс проверки. Здесь вам необходимо указать свою конкретную схему авторизации)
Теперь вам нужно вызвать Stripe API. Я делаю это в сервисе Stripe вот так:
app/services/stripe_session.rb
class StripeSession
require 'stripe' ### make sure gem 'stripe' is in your Gemfile ###
def self.new_session(key, user_email, plan)
new(key, customer_email: user_email, plan: plan).new_checkout_session
end
def initialize(key, options={})
@key = key
@customer_email = options[:customer_email]
@plan = options[:plan]
end
def new_checkout_session
Stripe.api_key = key
session = Stripe::Checkout::Session.create(
customer_email: customer_email,
payment_method_types: ['card'],
subscription_data: {
items: [{
plan: plan,
}],
},
success_url: 'https://yourapp.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://yourapp.com/cancel'
)
end
private
attr_reader :key, :customer_email, :plan
end
Если ваш звонок в Stripe был успешным, session
объект в вашем контроллере: новое действие теперь будет содержать данные вашей сессии:
def new
session = StripeSession.new_session(STRIPE_API_KEY, current_user.email, params[:plan])
@stripe_session = session
end
ЗАГРУЗКА СКРИПТА JS
Вы будете использовать session.id в своей ссылке для перенаправления на страницу Stripe Checkout:
subscriptions/new.html.erb
<%= content_for :header do %>
<script src="https://js.stripe.com/v3/" data-turbolinks-eval="false"></script>
<% end %>
<div data-stripe="<%= @stripe_session.id %>">
<%= link_to 'Subscribe', '', class: 'subscribe-btn', remote: true %>
</div>
<script>
const subscribeBtn = document.querySelector('.subscribe-btn')
subscribeBtn.addEventListener('click', e => {
e.preventDefault()
const CHECKOUT_SESSION_ID = subscribeBtn.parentElement.dataset.stripe
stripe.redirectToCheckout({
sessionId: CHECKOUT_SESSION_ID
}).then((result) => {
// handle any result data you might need
console.log(result.error.message)
})
}
</script>
Приведенный выше шаблон выполняет несколько важных задач:
- Загрузите скрипт stripe v3 js (как / где вы загружаете этот скрипт, решать вам. Если вы используете
content_for
тогда ваш файл layout.html будет иметь соответствующий блок:
<% if content_for? :add_to_head %> <%= yield :add_to_head %> <% end %>
Передайте @stripe_session.id от контроллера: новое действие атрибуту data-stripe-id вашего
<div>
элемент.Добавьте EventListener для подписки-btn для перенаправления в Stripe Checkout, передавая @stripe_session.id
АЛЬТЕРНАТИВНЫЙ ПОДХОД ДЛЯ JS-СКРИПТОВ
Есть и другие способы загрузки js-скриптов. Лично мне нравится использовать Stimulus для подобных вещей. Например, вместо загрузки js с помощьюcontent_for
и используя <script>
Теги у меня есть subscription_controller.js
Контроллер стимула выполняет работу:
subscriptions/new.html.erb (now becomes)
<div data-controller="subscription" data-session="<%= @stripe_session.id %>">
<%= link_to 'Subscribe', '', class: 'btn', remote: true,
data: {action: 'subscription#redirectToCheckout', target: 'subscription.sessionID'}
%>
</div>
---
(The Stimulus controller)
app/javascript/controllers/subscription_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ 'sessionID' ]
get sessionID() {
return this.sessionIDTarget.parentElement.dataset.session
}
initialize() {
const script = document.createElement('script')
script.src = "https://js.stripe.com/v3/"
document.head.appendChild(script)
}
redirectToCheckout(e) {
e.preventDefault()
// grab your key securely in whichever way works for you
const stripe = Stripe('pk_test_xxx')
const CHECKOUT_SESSION_ID = this.sessionID
stripe.redirectToCheckout({
sessionId: CHECKOUT_SESSION_ID
}).then((result) => {
console.log(result.error.message)
})
}
}
- Вам нужно будет добавить / инициализировать Stimulus в ваше приложение Rails, чтобы все вышеперечисленное работало...
ПОЛОСНЫЕ ВЕБ-КНИГИ
Stripe отправит POST на конечные точки вашего веб-перехватчика (если вы настроите их для этого). Если вы их слушаете, вы настраиваете некоторыеroutes
(см. ниже), чтобы справиться с ними. Вы также можете сделать это в сервисе по вашему выбору. Например, создайте еще один файл в папке app / services:
app/services/stripe_webhooks.rb
class StripeWebhooks
require 'stripe'
STRIPE_API_KEY = Rails.application.credentials.stripe[:secret_key]
def self.subscription_events(request)
new(request).subscription_lifecycle_events
end
def initialize(request)
@webhook_request = request
end
def subscription_lifecycle_events
authorize_webhook
case event.type
when 'customer.created'
handle_customer_created
when 'checkout.session.completed'
handle_checkout_session_completed
when # etc.
end
end
private
attr_reader :webhook_request, :event
def handle_customer_created(event)
## your work here
end
def handle_checkout_session_completed(event)
## your work here
end
def authorize_webhook
Stripe.api_key = STRIPE_API_KEY
endpoint_secret = Rails.application.credentials.stripe[:webhooks][:subscription]
payload = webhook_request.body.read
sig_header = webhook_request.env['HTTP_STRIPE_SIGNATURE']
@event = nil
begin
@event = Stripe::Webhook.construct_event(
payload, sig_header, endpoint_secret
)
rescue JSON::ParserError => e
puts e.message
rescue Stripe::SignatureVerificationError => e
puts e.message
end
end
end
Этот файл будет получать и авторизовывать входящий веб-перехватчик Stripe, который вы настроили на панели инструментов Stripe. В случае успехаevent
Атрибут будет содержать ответ JSON того веб-перехватчика, который вы загружаете в данный момент.
Это позволяет вызывать различные методы на основе event.type
которое будет именем веб-перехватчика. event.data.object
предоставит вам конкретные данные ответа.
ЖЕЛЕЗНЫЕ МАРШРУТЫ
Ничего из вышеперечисленного не будет работать без правильных маршрутов!
routes.rb
get 'success', to: 'subscriptions#success'
get 'cancel', to: 'subscriptions#cancel'
resources :subscriptions
post '/stripe-webhooks', to: 'subscriptions#stripe_webhook'
Мне пришлось разместить маршруты получения "успех" и "отмена" над ресурсами подписки, чтобы они разрешились должным образом.
И, наконец, добавьте success
а также cancel
обратные вызовы к вашему контроллеру и делайте с ними все, что вам нужно. Например:
subscriptions_controller.rb
...
def success
### the Stripe {CHECKOUT_SESSION_ID} will be available in params[:session_id]
if params[:session_id]
flash.now[:success] = "Thanks for your Subscribing/Purchasing/Whatever..."
else
flash[:error] = "Session expired error...your implementation will vary"
redirect_to subscriptions_path
end
end
def cancel
redirect_to subscriptions_path
end
...
Примечание: вам понадобится соответствующий success.html.erb
файл. Действие отмены может перенаправить или создать для этого файл html.erb, если хотите.
Итак, это было своего рода медведем, чтобы все настроить. Однако, если убрать сантехнику, есть много интересных возможностей для обработки всех видов событий жизненного цикла / веб-перехватчиков. В настоящее время я слушаю около 15 из них, чтобы моя система подписки работала бесперебойно.
Удачи!
Я не использую рубин, но в случае передачи идентификатора сеанса, когда проверка успешности выполнена, при создании сеанса просто добавьте "? Session_id={CHECKOUT_SESSION_ID}" после * _url, не знаю, относится ли это к вашему случаю, но рад помочь
mode : "subscription",
customer : customerid,
success_url: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://example.com/cancel?session_id={CHECKOUT_SESSION_ID}',
также я предлагаю посмотреть это https://youtube.com/watch?v=8TNQL9x6Ntg