当前位置: 首页 > 知识库问答 >
问题:

Stripe SCA一次性支付Rails

荀靖
2023-03-14

我正在将带有支付意图的Stripe SCA集成到我的Rails5.2.3(Ruby2.5.1)应用程序中。我成功地使一次性付款正常工作,但是在我集成订阅(成功地工作)之后,一次性付款在Stripe上接收到一个不完整的状态“客户尚未输入他们的付款方法”(the customer has not interned their payment method)。查看JSON,我可以看到我的支付意图,ID被成功创建,但是我的收费,数据显示为空。我不明白为什么数据没有传递给Stripe。下面是相应的文件:

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)
}

共有1个答案

岳玉书
2023-03-14

所以特别感谢Chris Oliver在这方面。。。但是需要做的是在show.html.erb上我必须将表单数据中的payment_intent_id更改为:payment_intent。

<%= form_with url: recipe_purchase_path(@recipe.id), local: true, id: "payment-form", data: { payment_intent: @payment_intent.client_secret } do |form| %>

然后在purchases_controller.rb中的show操作中,我需要添加customer

def show
    @payment_intent = Stripe::PaymentIntent.create(
            amount: @recipe.price_in_cents,
            currency: 'usd',
            payment_method_types: ['card'],
            customer: current_user.stripe_id || current_user.stripe_customer.id
            )
end

然后我完全删除了我的purchases.js,因为一次性付款是在subscription.js中处理的。

 类似资料:
  • 我试图在stripe中收取一次性的第一个月订阅费,但我不知道怎么做,因为他们更改了界面。 在第一个月订阅费的初始费用之后,它应该按月滚动。 理想的情况下,我期待着这样做与拉威尔收银员。 欢迎提供任何想法和示例。

  • 问题内容: 我有一个要添加“立即付款”按钮的产品列表,这样我就可以允许我的客户通过Paypal付款。 我已经阅读了文档,找不到如何执行此操作。我可以添加多个项目,但这不会很方便,因为我已经有要处理的项目列表。我还需要结帐流程来逐项列出订单,因此以1个价格“立即购买”也不是一件好事。 任何帮助表示赞赏的人,我都尝试过(没有运气): 问题答案: 请参阅此示例,并相应地进行更改。基本上将下划线添加到项目

  • wx.BaaS.pay(OBJECT) OBJECT 参数说明 参数 类型 必填 参数描述 totalCost Number Y 支付总额 merchandiseDescription String Y 微信支付凭证-商品详情的内容 merchandiseSchemaID Integer N 商品表 ID,可用于定位用户购买的物品 merchandiseRecordID String N 商品记录

  • 1、新版支付宝支付配置 配置支付宝支付之前,需要到支付宝商家中心开通手机网站应用和电脑网站应用两个产品。 产品开通链接:快捷手机wap支付 电脑网站支付 一个工作日即可通过审核,完成产品签约。 接下来,介绍支付宝支付配置教程。 第一步 登录商城后台,设置->交易设置->支付配置 ,选择支付宝支付,点击配置,进入到支付宝支付参数配置界面,选择新版支付宝。 需要我们配置应用APPID、应用私钥、应用公

  • 本文向大家介绍支付宝支付开发——当面付条码支付和扫码支付实例,包括了支付宝支付开发——当面付条码支付和扫码支付实例的使用技巧和注意事项,需要的朋友参考一下 本文介绍支付宝中当面付下属的条码支付、扫码支付、订单查询、退款申请的集成开发过程。  本文分为以下五个部分: 条码支付和扫码支付介绍 申请应用 密钥生成及配置 API及SDK集成 条码支付、扫码支付、订单查询、退款申请  一、条码支付及二维码支

  • 本文向大家介绍SpringBoot集成支付宝沙箱支付(支付、退款),包括了SpringBoot集成支付宝沙箱支付(支付、退款)的使用技巧和注意事项,需要的朋友参考一下 前言 支付宝推出一个沙箱环境,能够很好的模拟支付宝支付,并且还提供了demo,但demo是一个普通web项目,怎么整合到Spring Boot项目呢,其实很简单 简单配置请参照支付宝沙箱支付开发文档 一、支付部分 AlipayCon

  • 说明 微信扫码支付(模式一)SDK。 不通过接口,直接生成以weixin://开头的url,并生成二维码让用户扫描。 用户支付后,公众平台后台设置的支付回调URL会接收到消息。 官方文档: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4 https://pay.weixin.qq.com/wiki/doc/api/nati

  • 更新问题-添加更多详细信息。当我尝试支付订单时,我从PayPal收到一个500(内部服务错误)错误。 我从创建订单中获得ok,并获得创建的orderID订单状态。订单被买方成功批准,订单状态成为批准。 由创建的订单https://api.sandbox.paypal.com/v1/checkout/orders 批准使用贝宝https://www.paypalobjects.com/api/che