Stripe SCA одноразовые платежи Rails - PullRequest
0 голосов
/ 24 марта 2020

Я интегрирую Stripe SCA с намерениями оплаты в мое приложение rails 5.2.3 (ruby 2.5.1). У меня успешно работали одноразовые платежи, однако после интеграции подписок (успешно работающих) одноразовые платежи получают в Stripe статус неполного: «Клиент не указал свой способ оплаты». Глядя на JSON, я вижу свое платежное намерение с успешно созданным идентификатором, однако мои платежи, данные отображаются как нулевые. Я не могу понять, почему данные не передаются в полоску. Вот соответствующие файлы:

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

и скриншот JSON enter image description here

вот мои подписки. 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 Ответ

0 голосов
/ 25 марта 2020

Особая благодарность Крису Оливеру за это ... но то, что нужно было сделать, было на шоу. 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| %>

затем в моем действии show в purchaseases_controller.rb мне нужно было добавить клиента

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

Затем я полностью удалил свои покупки. js, поскольку в подписке обрабатываются разовые платежи. js

...