我正在尝试为我的初创公司创建一个基于订阅的网页。我们拥有实时模式的 Stripe,并且基本上存储了卡片以备将来使用,这使我们能够在创建新订阅时使用不同的付款方式。然后,订阅将分配给设备(类似于 Office 365 向用户分配许可证的方式)。
当用户注册时,我们的代码会自动使用 Stripe 创建 Stripe 客户 ID 并将其存储在后端。然后,我们有一个设置页面,其中包含“付款方式”部分,该部分会在服务器端动态加载它们,并允许通过单击同一区域中的“+”按钮来添加新的付款方式。“+”按钮设置为onclick="addPaymentOption()"
,它调用了这段漂亮的代码(为相关性而剪辑):
function addPaymentOption(){
//initiated by front end, loads stripe payment form
const attachto = document.getElementById("payment-dash");
const ppw_t = document.createElement("li");
ppw_t.setAttribute("id", "payment_add_form_cover");
ppw_t.setAttribute("style", "border-top: 1px solid #262626; margin-top: 6px;");
ppw_t.innerHTML = '<h3>Add a Payment method</h3>';
const ppw = document.createElement("div");
ppw.setAttribute("id", "payment_add_form");
ppw.setAttribute("style", "padding: 6px; max-width: 400px; margin: 0px auto;");
ppw.innerHTML = '<form id="payment-form" action=""><div id="payment-element"></div><button id="submit" type="submit" style="width:100%;">Save card details</button><div id="error-message"></div></form><form id="address-element"><div id="address-element"></div></form>';
attachto.appendChild(ppw_t);
ppw_t.appendChild(ppw);
(async () => {
const response = await fetch('/settings/payments/secret'); //<-- Renders client secret in backend, in JSON format.
const { client_secret: clientSecret } = await response.json();
const options = {
clientSecret: clientSecret
};
const stripe = Stripe('pk_live_51Mm3X7HIvvDoRmVUJay6Ib7MFBZgXvRtwwSosMh34h5GqCHZ0EKtnehc05DxFMfkggp2tzi6nu7ekNQnYyknMeC300T2iYxOHU');
console.log(clientSecret);
const elements = stripe.elements(options);
const paymentElement = elements.create('payment'); // <-- create payment element
paymentElement.mount('#payment-element');
loadStripe();
})();
}
function loadStripe(){
const form = document.getElementById('payment-form');
form.addEventListener('submit', async (event) => {
event.preventDefault();
const {error} = await stripe.confirmSetup({
//`Elements` instance that was used to create the Payment Element
elements,
confirmParams: {
return_url: 'https://**********.com/settings/payments',
}
});
if (error) {
// This point will only be reached if there is an immediate error when
// confirming the payment. Show error to your customer (for example, payment
// details incomplete)
const messageContainer = document.querySelector('#error-message');
messageContainer.textContent = error.message;
} else {
// Your customer will be redirected to your `return_url`. For some payment
// methods like iDEAL, your customer will be redirected to an intermediate
// site first to authorize the payment, then redirected to the `return_url`.
}
});
}
代码引用的 HTML(为保证相关性已剪辑)是:
<ul class="Dashboard" id="payment-dash">
<li>
<contextmenu>
<ol>
<li onclick="addSubscription()">+</li>
<li onclick="doHelp()">?</li>
</ol>
</contextmenu>
<img src="/assets/img/ui/pay/subscriptions.png" alt=""/>
<h3>Subscriptions</h3>
<ul class="DashboardSub">
<li>
<header>
<img src="/assets/img/static/lock_logo.png"/>
<h4>Subscription #1</h4>
</header>
<span>Monthly cost: $12.99<br/>Linked to: <i>Device name</i></span>
<opts><a href="#">Remove</a> | <a href="#">Assign</a></opts>
</li>
<li>
<header>
<img src="/assets/img/static/lock_logo.png"/>
<h4>Device #1</h4>
</header>
<span>Monthly cost: $12.99<br/>Linked to: <i>Device name</i></span>
<opts><a href="#">Remove</a> | <a href="#">Assign</a></opts>
</li>
<li>
<header>
<img src="/assets/img/static/lock_logo.png"/>
<h4>Device #1</h4>
</header>
<span>Monthly cost: $12.99<br/>Linked to: <i>Device name</i></span>
<opts><a href="#">Remove</a> | <a href="#">Assign</a></opts>
</li>
<li>
<header>
<img src="/assets/img/icons/static/guardian-gpt.png"/>
<h4>AI Family</h4>
</header>
<span>Monthly cost: $5.99<br/></span>
<opts><a href="#">Remove</a></opts>
</li>
<div>
<b>Monthly total</b>: $42.99
</div>
</ul>
</li>
<li>
<contextmenu>
<ol>
<li onclick="addPaymentOption()">+</li>
<li>?</li>
</ol>
</contextmenu>
<img src="/assets/img/ui/pay/payment_methods.png" alt=""/>
<h3>Payment Methods</h3>
<ul class="DashboardSub" id="payments">
<li>
<header>
<img src="/assets/img/ui/pay/mc.png"/>
<h4>Mastercard *1234</h4>
</header>
<span>Expires in 2025<br/>Last use: </span>
<opts><a href="#">Remove</a> | <a href="#">Set Default</a></opts>
</li>
<li>
<header>
<img src="/assets/img/ui/pay/visa.png"/>
<h4>Mastercard *1234</h4>
</header>
<span>Expires in 2025<br/>Last use: </span>
<opts><a href="#">Remove</a> | <a href="#">Set Default</a></opts>
</li>
<li>
<header>
<img src="/assets/img/ui/pay/amex.png"/>
<h4>Mastercard *1234</h4>
</header>
<span>Expires in 2025<br/>Last use: </span>
<opts><a href="#">Remove</a> | <a href="#">Set Default</a></opts>
</li>
<li>
<header>
<img src="/assets/img/ui/pay/discover.png"/>
<h4>Mastercard *1234</h4>
</header>
<span>Expires in 2025<br/>Last use: </span>
<opts><a href="#">Remove</a> | <a href="#">Set Default</a></opts>
</li>
<li>
<header>
<img src="/assets/img/ui/pay/ach_pay.png"/>
<h4>Bank Acct. *1234</h4>
</header>
<span>Expires in 2025<br/>Pre-auth: </span>
<opts><a href="#">Remove</a> | <a href="#">Set Default</a></opts>
</li>
</ul>
</li>
<li>
<img src="/assets/img/ui/pay/history.png" alt=""/>
<h3>Payment History</h3>
<table>
<tr>
<td><b>Subscriptions</b></td>
<td>4</td>
</tr>
<tr>
<td><b>Last Payment</b></td>
<td><b>$45.<sup>00</sup>.</td>
</tr>
</table>
</li>
<li>
<img src="/assets/img/ui/pay/cancel.png" alt=""/>
<h3>Cancel Service</h3>
<table>
<tr>
<td><b>Subscriptions</b></td>
<td>4</td>
</tr>
<tr>
<td><b>Last Payment</b></td>
<td><b>$45.<sup>00</sup>.</td>
</tr>
</table>
</li>
</ul>
此外,的代码/secrets
是:
if(isLoggedIn()){
$customer_id = fetchstripeid();
$intent = $stripe->setupIntents->create(
[
'customer' => $customer_id,
'payment_method_types' => ['card_present'],
]);
echo json_encode(array('client_secret' => $intent->client_secret));
ob_flush();flush();
die();
}
几次修订之前,它可以正确创建信用卡信息和账单邮政编码的表单元素,但“保存信用卡详细信息”按钮会消失。我重新安排了代码,因为看起来该元素正在加载和卸载。然后,几次修订之后,“保存信用卡详细信息”按钮加载到表单中,但信用卡输入表单元素拒绝加载。一直以来,它在控制台中几乎没有显示任何错误消息。
有人发现我遗漏了什么吗?加载的时间是在应该加载之前还是之后?我的 Stripe 经验有限,也许这里有一个我没理解的概念?