首页 > 解决方案 > Stripe Connect form.dataset 未通过 API 密钥

问题描述

我正在将 Stripe Connect 集成到应用程序中,并且一直在关注本教程: https ://web-crunch.com/posts/ruby-on-rails-marketplace-stripe-connect

当我加载带有条带结帐表单以输入信用卡详细信息的页面时,没有显示使该表单可用以便用户可以输入其信用卡详细信息的预期行为。在控制台中,我看到以下消息:

(index):1 Uncaught IntegrationError: Invalid value for Stripe(): apiKey should be a string. You specified: undefined.
    at new t (https://js.stripe.com/v3/:1:10860)
    at St (https://js.stripe.com/v3/:1:19414)
    at Pt (https://js.stripe.com/v3/:1:19485)
    at new e (https://js.stripe.com/v3/:1:149125)
    at wu (https://js.stripe.com/v3/:1:162737)
    at new StripeCharges (http://localhost:5000/packs/js/application-9b4381ae6320422376fc.js:155:19)
    at HTMLDocument.<anonymous> (http://localhost:5000/packs/js/application-9b4381ae6320422376fc.js:265:18)
    at Object../node_modules/turbolinks/dist/turbolinks.js.e.dispatch (http://localhost:5000/packs/js/application-9b4381ae6320422376fc.js:40164:40)
    at r.notifyApplicationAfterPageLoad (http://localhost:5000/packs/js/application-9b4381ae6320422376fc.js:41083:43)
    at r.pageLoaded (http://localhost:5000/packs/js/application-9b4381ae6320422376fc.js:41037:66)
t @ (index):1
St @ (index):1
Pt @ (index):1
e @ (index):1
wu @ (index):1
StripeCharges @ stripe.js:5
(anonymous) @ stripe.js:82
./node_modules/turbolinks/dist/turbolinks.js.e.dispatch @ turbolinks.js:75
r.notifyApplicationAfterPageLoad @ turbolinks.js:994
r.pageLoaded @ turbolinks.js:948
(anonymous) @ turbolinks.js:872

我的代码如下所示:

应用程序/javascript/packs/application.js

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("local-time").start()
require("stylesheets/application.scss")
require("trix")
require("@rails/actiontext")

window.Rails = Rails

import 'bootstrap'
import 'data-confirm-modal'

import "controllers"
import "stylesheets/application"
import "components/stripe"


$(document).on("turbolinks:load", () => {
  $('[data-toggle="tooltip"]').tooltip()
  $('[data-toggle="popover"]').popover()
})

应用程序/javascript/components/stripe.js

class StripeCharges {
  constructor({ form, key }) {
    this.form = form;
    this.key = key;
    this.stripe = Stripe(this.key)
  }

  initialize() {
    this.mountCard()
  }

  mountCard() {
    const elements = this.stripe.elements();

    const style = {
      base: {
        color: "#32325D",
        fontWeight: 500,
        fontSize: "16px",
        fontSmoothing: "antialiased",

        "::placeholder": {
          color: "#CFD7DF"
        },
        invalid: {
          color: "#E25950"
        }
      }
    };

    const card = elements.create('card', { style })
    if (card) {
      card.mount('#card-element')
      this.generateToken(card)
    }
  }

  generateToken(card) {
    let self = this
    this.form.addEventListener('submit', async (event) => {
      event.preventDefault()

      const { token, error } = await self.stripe.createToken(card)

      if (error) {
        const errorElement = document.getElementById('card-errors')
        errorElement.textContent = error.message
      } else {
        this.tokenHandler(token)
      }
    });
  }

  tokenHandler(token) {
    let self = this;
    const hiddenInput = document.createElement('input');
    hiddenInput.setAttribute('type', 'hidden')
    hiddenInput.setAttribute('name', 'stripeToken')
    hiddenInput.setAttribute('value', token.id)
    this.form.appendChild(hiddenInput)

    ["brand", "last4", "exp_month", "exp_year"].forEach(field => {
      self.addCardField(token, field);
    })
  }

  addCardField(token, field) {
    let hiddenInput = document.createElement('input');
    hiddenInput.setAttribute('type', 'hidden')
    hiddenInput.setAttribute('name', `user[card_${field}]`);
    hiddenInput.setAttribute('value', token.card[field])
    this.form.appendChild(hiddenInput)
  }
}

// Kick it all off
document.addEventListener("turbolinks:load", () => {
  const form = document.querySelector('#payment-form')
  if (form) {
    const charge = new StripeCharges({
      form: form,
      key: form.dataset.stripeKey
    });
    charge.initialize()
  }
})

注意:如果我将线替换到底部

key: form.dataset.stripeKey

使用 Stripe 公共 API 字符串,表单将按应有的方式显示。像这样:在此处输入图像描述

表单页面

应用程序/视图/订阅/_form

<%= form_with model: current_user, url: subscription_url, method: :post, html: { id: "payment-form", class: "stripe-form" }, data: { stripe_key: project.user.publishable_key }  do %>

  <div>
    <label for="card-element" class="label">
      Credit or debit card
    </label>

    <div id="card-element">
      <!-- A Stripe Element will be inserted here. -->
    </div>

    <!-- Used to display Element errors. -->
    <div id="card-errors" role="alert" class="text-sm text-red-400"></div>

    <input type="hidden" name="plan" value="<%= params[:plan] %>">
    <input type="hidden" name="project" value="<%= params[:project] %>">

    <button>Back <%= number_to_currency(params[:amount]) %> /mo toward <em><%= project.title %></em></button>
  </div>
<% end %>

app/views/subscriptions/new.html.erb

<div class="w-1/2 mx-auto">
  <h3 class="mb-2 text-2xl font-bold text-center">You're about to back <em><%= @project.title %> </em></h3>

  <% if user_signed_in? %>
  <div class="p-6 border rounded">
    <%= render "form", project: @project %>
  </div>
  <% else %>
  <div class="p-6 text-center bg-white border rounded">
    <%= link_to "Sign in to back this idea", new_user_session_path, class: "btn btn-default" %>
  </div>
  <% end %>
</div>

我已经搞砸了2天了。如果有人可以提供帮助,我将不胜感激。谢谢你。

使用调试器编辑检查表单

form = form#payment-form.stripe-form {0: input, 1: input, 2: input, 3: button, acceptCharset: "UTF-8", action: "http://localhost:5000/subscription", autocomplete: "on", enctype: "application/x-www-form-urlencoded", encoding: "application/x-www-form-urlencoded", …}
Local
form: form#payment-form.stripe-form
acceptCharset: "UTF-8"
action: "http://localhost:5000/subscription"
autocomplete: "on"
enctype: "application/x-www-form-urlencoded"
encoding: "application/x-www-form-urlencoded"
method: "post"
name: ""
noValidate: false
target: ""
elements: HTMLFormControlsCollection(4)
length: 4
0: input
1: input
2: input
3: button
authenticity_token: input
plan: input
project: input
__proto__: HTMLFormControlsCollection
length: 4
title: ""
lang: ""
translate: true
dir: ""
hidden: false
accessKey: ""
draggable: false
spellcheck: true
autocapitalize: ""
contentEditable: "inherit"
isContentEditable: false
inputMode: ""
offsetParent: body.bg-blue-800.text-blue-100
offsetTop: 177
offsetLeft: 1107
offsetWidth: 575
offsetHeight: 128
style: CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}
innerText: "Credit or debit card↵Back $1,199.00 /mo toward Second Project: by Beshore"
outerText: "Credit or debit card↵Back $1,199.00 /mo toward Second Project: by Beshore"
oncopy: null
oncut: null
onpaste: null
onabort: null
onblur: null
oncancel: null
oncanplay: null
oncanplaythrough: null
onchange: null
onclick: null
onclose: null
oncontextmenu: null
oncuechange: null
ondblclick: null
ondrag: null
ondragend: null
ondragenter: null
ondragleave: null
ondragover: null
ondragstart: null
ondrop: null
ondurationchange: null
onemptied: null
onended: null
onerror: null
onfocus: null
onformdata: null
oninput: null
oninvalid: null
onkeydown: null
onkeypress: null
onkeyup: null
onload: null
onloadeddata: null
onloadedmetadata: null
onloadstart: null
onmousedown: null
onmouseenter: null
onmouseleave: null
onmousemove: null
onmouseout: null
onmouseover: null
onmouseup: null
onmousewheel: null
onpause: null
onplay: null
onplaying: null
onprogress: null
onratechange: null
onreset: null
onresize: null
onscroll: null
onseeked: null
onseeking: null
onselect: null
onstalled: null
onsubmit: null
onsuspend: null
ontimeupdate: null
ontoggle: null
onvolumechange: null
onwaiting: null
onwheel: null
onauxclick: null
ongotpointercapture: null
onlostpointercapture: null
onpointerdown: null
onpointermove: null
onpointerup: null
onpointercancel: null
onpointerover: null
onpointerout: null
onpointerenter: null
onpointerleave: null
onselectstart: null
onselectionchange: null
onanimationend: null
onanimationiteration: null
onanimationstart: null
ontransitionend: null
dataset: DOMStringMap {remote: "true"}
nonce: ""
autofocus: false
tabIndex: -1
enterKeyHint: ""
onpointerrawupdate: null
namespaceURI: "http://www.w3.org/1999/xhtml"
prefix: null
localName: "form"
tagName: "FORM"
id: "payment-form"
className: "stripe-form"
classList: DOMTokenList ["stripe-form", value: "stripe-form"]
slot: ""
attributes: NamedNodeMap {0: id, 1: class, 2: action, 3: accept-charset, 4: data-remote, 5: method, id: id, class: class, action: action, accept-charset: accept-charset, data-remote: data-remote, …}
shadowRoo

编辑 2 - HTML 表单数据属性

<form id="payment-form" class="stripe-form" action="http://localhost:5000/subscription" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="5vPxxBiI2nZRsLQlOi6rDIKI3Wp9ZI7v5w79dfQkgLykkfwPtFhEakD9D/iqrWlhvC3Mgrj6hqVJLY+2/IPrzg==">

  <div>
    <label for="card-element" class="label">
      Credit or debit card
    </label>

    <div id="card-element">
      <!-- A Stripe Element will be inserted here. -->
    </div>

    <!-- Used to display Element errors. -->
    <div id="card-errors" role="alert" class="text-sm text-red-400"></div>

    <input type="hidden" name="plan" value="perk-1-perk_42">
    <input type="hidden" name="project" value="29">

    <button>Back $1,199.00 /mo toward <em>Second Project: by Beshore</em></button>
  </div>
</form>

标签: javascriptruby-on-railsrubystripe-payments

解决方案


您是否尝试将其更改为form.data.stripe_key而不是form.dataset.stripeKey

您正在使用以下参数创建表单:

data: { stripe_key: project.user.publishable_key }

所以当试图访问这个值form.dataset.stripeKeyapp/javascript/components/stripe.jsform.dataset是一个无效的元素。最终,这会导致在实例化StripeCharges对象时传递未定义的 API 密钥。

在 JS 代码中设置断点可能会有所帮助,以便您可以检查表单对象,并了解如何正确访问 Stripe 键:

  const form = document.querySelector('#payment-form')

  // New line here. Use the JS console to inspect the `form` object,
  // and how to extract the API key.
  debugger;

  if (form) {
    const charge = new StripeCharges({
      form: form,
      key: form.dataset.stripeKey
    });
    charge.initialize()
  }

推荐阅读