我正在努力将Rails应用切换到新的Stripe结帐流程,以适应新的SCA规定。
我想实现在此链接中找到的简单动态产品例程:https://stripe.com/docs/payments/checkout/migration#api-products-after
我不知道在哪里放置不同的代码。应该输入什么:
-控制器->其中的方法
-视图->例如,事件显示视图。用户将单击的表单/按钮
-JavaScript->如何传递正确的会话ID
-再次使用控制器->实现成功和错误用例
Stripe技术支持刚刚将我发送到上面的文档链接,因此,在此我非常感谢您的帮助。
答案 0 :(得分:2)
新的Stripe Checkout的Rails工作流程是:
创建Stripe Checkout会话并检索session.id(.rb)
将session.id传递给js初始化程序以重定向到Stripe Checkout
STRIPE CHECKOUT SESSION
这是我用于订阅服务的示例client/server Stripe Checkout implementation。除了要引用“条纹产品”而不是“计划”之外,您的步骤基本相同:
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会话所需的任何产品参数:
subscriptions/index.html.erb
<%= link_to 'Get more info...', new_subscription_path(plan: 'plan_xxx' %>
:new控制器操作将返回您的Stripe CHECKOUT_SESSION_ID,供您在模板中使用。 (还要注意,该控制器正在绕过login_in?和伪造保护,以允许Stripe Webhook POST对您的Checkout会话做出响应。您需要在此处解决您的特定授权方案)
现在,您需要调用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的调用成功,则控制器:new操作中的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>
上面的模板正在做一些重要的事情:
content_for
,则您的layout.html文件将具有相应的块: <% if content_for? :add_to_head %> <%= yield :add_to_head %> <% end %>
将@ stripe_session.id从controller:new操作传递到<div>
元素的data-stripe-id属性。
添加事件监听器以使subscribe-btn重定向到Stripe Checkout,并传入@ stripe_session.id
替代JS代码的方法
还有其他加载js脚本的方法。就个人而言,我喜欢将Stimulus用于此类操作。例如,与使用content_for
加载js并使用<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)
})
}
}
条纹网页
Stripe将POST到您的Webhook端点(如果将它们配置为)。如果要监听它们,则可以配置一些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 Webhook。如果成功,event
属性将包含您当前正在摄取的任何Webhook的JSON响应。
这使您可以基于event.type
调用各种方法,这些方法将成为Webhook的名称。 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
文件。如果愿意,cancel操作也可以重定向或为此创建html.erb文件。
因此,进行所有设置有点不容易。但是,随着管道的畅通,有很多很酷的可能性来处理各种生命周期事件/ webhooks。目前,我正在听大约15个消息,以使我的订阅系统保持平稳运行。
祝你好运!
答案 1 :(得分:0)
我不使用ruby,但是在创建会话时成功结帐完成时通过会话ID,只需在* _url之后添加“?session_id = {CHECKOUT_SESSION_ID}”, 不知道这是否是您的情况,但很乐意提供帮助
Add DROP TABLE / DROP VIEW
另外,我建议您观看此https://youtube.com/watch?v=8TNQL9x6Ntg