Rails中的新Stripe SCA检出流程 [英] New Stripe SCA checkout flow in Rails
问题描述
我正在努力将Rails应用切换到新的Stripe结帐流程,以适应新的SCA规定.
I'm struggling with switching my Rails app to the new Stripe checkout flow to accommodate the new SCA regulation.
我想实现在此链接中找到的简单动态产品例程: https://stripe.com/docs/payments/checkout/migration#api-products-after
I want to implement the simple dynamic product routine found in this link: https://stripe.com/docs/payments/checkout/migration#api-products-after
我不知道在哪里放置不同的代码.应该输入什么:
-控制器->其中的方法
-视图->例如,事件显示视图.用户将单击的表单/按钮
-JavaScript->如何传递正确的会话ID
-再次执行控制器->实现成功和错误用例
I can't figure out where to put the different pieces of code. What should go in:
- controller -> in which methods
- views -> the event show view for example. The form/button the user will click
- javascript -> how to pass the right session id
- controller again -> implementing the success and error use cases
Stripe技术支持刚刚将我发送到上面的文档链接,因此,在此我将非常感谢您的帮助.
The Stripe tech support just sent me to the documentation link above so I would really appreciate some help here.
推荐答案
新的Stripe Checkout的Rails工作流程是:
The Rails workflow for the new Stripe Checkout is:
-
创建Stripe Checkout会话并检索session.id(.rb)
Create a Stripe Checkout Session and retrieve the session.id (.rb)
将session.id传递给js初始化程序以重定向到Stripe Checkout
Pass the session.id to a js initializer to redirect to Stripe Checkout
条纹结帐环节
这是一个示例客户端/服务器条纹签出实现我正在用于订阅服务.除了要引用条纹产品"而不是计划"之外,您的步骤基本相同:
This is a sample client/server Stripe Checkout implementation that I'm using for a Subscription service. Your steps would essentially be the same except you would be referencing a Stripe Product rather than a Plan:
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 Plan(或Product)信息作为参数传递.就您而言,您可以传递Stripe Checkout会话所需的任何Product参数:
In my case, my index.html.erb
template has a link to "Get more info..." about a particular subscription. That link goes to the controller's :new action, passing the relevant Stripe Plan (or Product) info as params. In your case, you might pass whatever Product params necessary for your Stripe Checkout session:
subscriptions/index.html.erb
<%= link_to 'Get more info...', new_subscription_path(plan: 'plan_xxx' %>
:new控制器操作将返回您的Stripe CHECKOUT_SESSION_ID,供您在模板中使用. (还要注意,该控制器正在绕过login_in?和伪造保护,以允许对您的Checkout会话使用Stripe Webhook POST响应.您需要在此处解决您的特定授权方案)
The :new controller action will return your Stripe CHECKOUT_SESSION_ID for use in your template. (Also, note that this controller is bypassing logged_in? and forgery protection to allow for the Stripe Webhook POST response to your Checkout Session. You'll need to address your particular authorization scheme here)
现在,您需要调用Stripe API.我正在像这样的Stripe服务中做到这一点:
Now, you need to call the Stripe API. I'm doing this in a Stripe service like so:
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的调用成功,则控制器:new操作中的session
对象现在将包含您的会话数据:
If your call to Stripe was successful the session
object in your controller :new action will now contain your session data:
def new
session = StripeSession.new_session(STRIPE_API_KEY, current_user.email, params[:plan])
@stripe_session = session
end
JS脚本加载
您将在链接中使用session.id重定向到Stripe Checkout页面:
You'll be using the session.id in your link to redirect to the Stripe Checkout page:
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>
上面的模板正在做一些重要的事情:
The above template is doing several important things:
- 加载条纹v3 js脚本(取决于您如何加载此脚本/在何处加载该脚本.如果使用
content_for
,则您的layout.html文件将具有相应的块:
- Load the stripe v3 js script (it's up to you how/where you load this script. If using
content_for
then your layout.html file would have a corresponding block:
<% if content_for? :add_to_head %> <%= yield :add_to_head %> <% end %>
-
将@ stripe_session.id从controller:new动作传递到
<div>
元素的data-stripe-id属性.
Pass the @stripe_session.id from the controller :new action to the data-stripe-id attribute of your
<div>
element.
添加事件监听器以使subscribe-btn重定向到Stripe Checkout,并传入@ stripe_session.id
Add the EventListener for the subscribe-btn to redirect to Stripe Checkout, passing in the @stripe_session.id
替代JS代码的方法
还有其他加载js脚本的方法.就个人而言,我喜欢使用刺激进行此类操作.例如,不是使用content_for
并使用<script>
标签加载js,而是使用subscription_controller.js
刺激控制器来完成工作:
There are other ways to load the js scripts. Personally, I love using Stimulus for this sort of thing. For example, rather than loading js with content_for
and using <script>
tags I have a subscription_controller.js
Stimulus Controller doing the work:
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)
})
}
}
- 您需要将Rails应用程序添加/初始化刺激才能使以上功能起作用...
条纹网页
Stripe将POST到您的Webhook端点(如果将它们配置为).如果要监听它们,则可以配置一些routes
(请参见下文)来处理它们.您也可以在自己选择的服务中执行此操作.例如,在您的app/services文件夹中创建另一个文件:
Stripe will POST to your webhook endpoints (if you configure them to). If listening for them, you configure some routes
(see below) to handle them. You can also do this in a service of your choosing. For example, create another file in your app/services folder:
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 Webhook.如果成功,event
属性将包含您当前正在摄取的任何Webhook的JSON响应.
This file will receive and authorize the incoming Stripe webhook that you configured in your Stripe Dashboard. If successful, event
attribute will contain the JSON response of whichever webhook you're ingesting at the moment.
这允许您基于event.type
调用各种方法,这将是Webhook的名称. event.data.object
将带您进入特定的响应数据.
That allows you to call various methods based on the event.type
which will be the name of the webhook. event.data.object
will get you into specific response data.
铁路路线
如果没有正确的路线,以上任何一项都不起作用!
None of the above will work without the proper routes!
routes.rb
get 'success', to: 'subscriptions#success'
get 'cancel', to: 'subscriptions#cancel'
resources :subscriptions
post '/stripe-webhooks', to: 'subscriptions#stripe_webhook'
我必须放置获取成功"&在订阅资源上方取消"路由,以使其正常解决.
I had to place the get 'success' & 'cancel' routes above the subscription resources for them to resolve properly.
最后,将success
和cancel
回调添加到您的控制器,并使用它们进行所需的操作.例如:
And, finally, add the success
and cancel
callbacks to your controller and do whatever you need with them. For example:
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
文件.如果您愿意,cancel操作也可以重定向或为此创建一个html.erb文件.
Note: you'll need a corresponding success.html.erb
file. The cancel action can redirect or create an html.erb file for that too if you'd like.
因此,进行所有设置有点不容易.但是,随着管道的畅通,有很多很酷的可能性来处理各种生命周期事件/webhooks.目前,我正在听大约15个消息,以使我的订阅系统保持平稳运行.
So, it was kind of a bear to get it all setup. However, with the plumbing out of the way there are lots of cool possibilities to handle all sorts of lifecycle events/webhooks. Currently, I'm listening for about 15 of them to keep my subscription system running smoothly.
祝你好运!
这篇关于Rails中的新Stripe SCA检出流程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!