Новый процесс проверки 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

Другие вопросы по тегам