【发布时间】:2020-07-04 23:57:52
【问题描述】:
我正在将带有支付意图的 Stripe SCA 集成到我的 rails 5.2.3 (ruby 2.5.1) 应用程序中。我成功地使一次性付款正常工作,但是在我集成订阅(成功工作)后,一次性付款在 Stripe 上收到不完整的状态“客户尚未输入他们的付款方式”。查看 JSON,我可以看到成功创建 ID 的付款意图,但是我的收费数据显示为空。我无法弄清楚为什么没有将数据传递给条带。以下是对应的文件:
purchases.show.html.erb
<div class="container">
<h1>Purchasing <%= @recipe.title %> for <%= number_to_currency(@recipe.price) %></h1>
<%= form_with url: recipe_purchase_path(@recipe.id), local: true, id: "payment-form", data: { payment_intent_id: @payment_intent.client_secret } do |form| %>
<div class="form-group">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element" class="form-control">
</div>
<div id="card-errors" role="alert">
</div>
</div>
<div class="form-group">
<label>Name on Card</label>
<%= form.text_field :name_on_card, placeholder: "Full name", class: "form-control" %>
</div>
<div class="form-group">
<%= form.hidden_field :payment_intent_id, value: @payment_intent.id %>
<button class="btn btn-outline-primary buy-recipe">Submit Payment</button>
</div>
<% end %>
</div>
purchases_controller.rb
class PurchasesController < ApplicationController
before_action :authenticate_user!
before_action :set_recipe, only:[:show, :create]
def receipt
@purchase = Purchase.find_by_uuid(params[:id])
@recipe = Recipe.find(@purchase.recipe_id)
end
def show
@payment_intent = Stripe::PaymentIntent.create(
amount: @recipe.price_in_cents,
currency: 'usd',
payment_method_types: params['card'],
metadata: {integration_check: 'accept_a_payment'},
)
end
def create
@payment_intent = Stripe::PaymentIntent.retrieve(params[:payment_intent_id])
if @payment_intent.status == "succeeded"
charge = @payment_intent.charges.data.first
card = charge.payment_method_details.card
purchase = Purchase.create(
customer_id: charge.id,
user_id: current_user.id,
recipe_id: @recipe.id,
uuid: SecureRandom.uuid,
amount: @recipe.price
)
current_user.favorites << @recipe
redirect_to recipe_path(@recipe.slug), notice: "#{@recipe.title} has been added to your Cookbook, thanks for purchasing!"
else
flash[:alert] = "Your order was unsuccessful. Please try again."
redirect_to recipe_purchase_path(@recipe.id)
end
end
private
def set_recipe
@recipe = Recipe.find(params[:recipe_id])
end
end
purchases.index.js
document.addEventListener("turbolinks:load", () => {
const form = document.querySelector("#payment-form")
if (form == null) { return }
const public_key = document.querySelector("meta[name='stripe-key']").getAttribute("content")
const stripe = Stripe(public_key)
const elements = stripe.elements()
const card = elements.create('card')
card.mount('#card-element')
card.addEventListener("change", (event) => {
var displayError = document.getElementById('card-errors')
if (event.error) {
displayError.textContent = event.error.message
} else {
displayError.textContent = ''
}
})
form.addEventListener("submit", (event) => {
event.preventDefault()
let data = {
payment_method: {
card: card,
billing_details: {
name: form.querySelector("#name_on_card").value
}
}
}
stripe.confirmCardPayment(form.dataset.paymentIntentId, data).then((result) => {
if (result.error) {
var errorElement = document.getElementById('card-errors')
errorElement.textContent = result.error.message
} else {
//
//
form.submit()
}
})
})
})
这是我的 subscriptions.js 文件
document.addEventListener("turbolinks:load", () => {
let cardElement = document.querySelector("#card-element")
if (cardElement !== null) { setupStripe() }
})
function setupStripe() {
const stripe_key = document.querySelector("meta[name='stripe-key']").getAttribute("content")
const stripe = Stripe(stripe_key)
const elements = stripe.elements()
const card = elements.create('card')
card.mount('#card-element')
var displayError = document.getElementById('card-errors')
card.addEventListener('change', (event) => {
if (event.error) {
displayError.textContent = event.error.message
} else {
displayError.textContent = ''
}
})
const form = document.querySelector("#payment-form")
let paymentIntentId = form.dataset.paymentIntent
let setupIntentId = form.dataset.setupIntent
if (paymentIntentId) {
if (form.dataset.status == "requires_action") {
stripe.confirmCardPayment(paymentIntentId, { setup_future_usage: 'off_session' }).then((result) => {
if (result.error) {
displayError.textContent = result.error.message
form.querySelector("#card-details").classList.remove("d-none")
} else {
form.submit()
}
})
}
}
form.addEventListener('submit', (event) => {
event.preventDefault()
let name = form.querySelector("#name_on_card").value
let data = {
payment_method_data: {
card: card,
billing_details: {
name: name,
}
}
}
// Complete a payment intent
if (paymentIntentId) {
stripe.confirmCardPayment(paymentIntentId, {
payment_method: data.payment_method_data,
setup_future_usage: 'off_session',
save_payment_method: true,
}).then((result) => {
if (result.error) {
displayError.textContent = result.error.message
form.querySelector("#card-details").classList.remove("d-none")
} else {
form.submit()
}
})
// Updating a card or subscribing with a trial (using a SetupIntent)
} else if (setupIntentId) {
stripe.confirmCardSetup(setupIntentId, {
payment_method: data.payment_method_data
}).then((result) => {
if (result.error) {
displayError.textContent = result.error.message
} else {
addHiddenField(form, "payment_method_id", result.setupIntent.payment_method)
form.submit()
}
})
} else {
//subscribing w no trial
data.payment_method_data.type = 'card'
stripe.createPaymentMethod(data.payment_method_data).then((result) => {
if (result.error) {
displayError.textContent = result.error.message
} else {
addHiddenField(form, "payment_method_id", result.paymentMethod.id)
form.submit()
}
})
}
})
}
function addHiddenField(form, name, value) {
let input = document.createElement("input")
input.setAttribute("type", "hidden")
input.setAttribute("name", name)
input.setAttribute("value", value)
form.appendChild(input)
}
【问题讨论】:
-
您能否更准确地隔离您的问题?这里发生了很多事情,也有很多潜在的故障点。您确定
confirmCardPayment正在工作,还是遇到错误情况?我注意到您传入了form.dataset.paymentIntentId,但之前您将client_secret 设置为payment_intent_id。不匹配的外壳会导致问题吗?建议使用更精确的名称以减少混淆。 -
我认为这是问题所在,confirmCardPayment 不起作用,因为在创建 payment_intent 时卡参数未发送到 Stripe。但我不确定它在哪里失败。
-
您是否在
confirmCardPayment的回调中点击if (result.error) { ... }?如果是这样,错误信息是什么?您提到这在添加订阅支持之前有效,但我在这里看不到任何与订阅相关的内容。添加订阅后,这段代码发生了什么变化? -
我没有在我的日志中看到它的命中 (result.error)
-
对不起,你能澄清一下
purchases和subscriptionsjs 文件背后的意图吗?它们似乎有很大的重叠,目前尚不清楚我们应该关注哪一个。我想再次请您检查您的表单数据。你确定confirmCardPayment还在运行吗?你可以在它之前和之后登录吗?看起来您在 erb 中设置了data: { payment_intent_id: 'someValue'},然后尝试执行let paymentIntentId = form.dataset.paymentIntent; if (paymentIntentId) { ... },所以我怀疑这些代码都没有运行过。请添加日志记录以找出您的流程失败的地方。
标签: ruby-on-rails stripe-payments