Я интегрирую 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
Мой код выглядит следующим образом:
app / 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()
})
app / 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
При использовании строки API Stripe publi c форма отображается так, как должна. Как это:
Страница формы
приложение / просмотры / подписки / _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 / subscription / 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>