我遵循了基本的 React stripe 文档: https://stripe.com/docs/stripe-js/react
所以这是我有问题的代码:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Elements} from '@stripe/react-stripe-js';
import {loadStripe} from '@stripe/stripe-js';
// Make sure to call `loadStripe` outside of a component’s render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe('process.env.REACT_APP_STRIPE_PUBLIC_KEY');
function Index() {
/* const options = {
// passing the client secret obtained from the server
clientSecret: '{{CLIENT_SECRET}}',
}; */
return (
<Elements stripe={stripePromise} /* options={options} */>
<App/>
</Elements>
);
};
ReactDOM.render(<Index />, document.getElementById('root'));
结账.tsx
import {PaymentElement} from '@stripe/react-stripe-js';
import React from 'react';
export default function CheckoutForm () {
return (
<form>
<PaymentElement />
<button>Submit</button>
</form>
);
};
注释掉客户端机密选项的错误:
Uncaught (in promise) IntegrationError: In order to create a payment element, you must pass a valid PaymentIntent or SetupIntent client secret when creating the Elements group.
e.g. stripe.elements({clientSecret: "{{CLIENT_SECRET}}"})
未注释选项的错误基本上是 CLIENT_SECRET 未定义或格式不正确。
我在 Stripe 的视频教程中注意到,这个家伙没有这个选项就很好,但我无法重现它。 https://www.youtube.com/watch?v=5y5WwF9s-ZI&t=356s
一般来说,文档并没有说明如何以及何时获取 CLIENT_SECRET 变量。 😦
不确定如何从服务器获取客户端密钥,但您必须确保
<Elements>
在其初始渲染时提供了正确的客户端密钥(即不能为空)。
尝试在
clientSecret &&
之前添加<Elements>
,就像官方文档中的这个示例(选择“Web”和“React”查看React示例代码):
export default function App() {
const [clientSecret, setClientSecret] = useState("");
useEffect(() => {
// Create PaymentIntent as soon as the page loads
fetch("/create-payment-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items: [{ id: "xl-tshirt" }] }),
})
.then((res) => res.json())
.then((data) => setClientSecret(data.clientSecret));
}, []);
const appearance = {
theme: 'stripe',
};
const options = {
clientSecret,
appearance,
};
return (
<div className="App">
{clientSecret && (
<Elements options={options} stripe={stripePromise}>
<CheckoutForm />
</Elements>
)}
</div>
);
}
这确保如果 clientSecret 为假(即 false、未定义或 null),则不会安装
<Elements>
。
您需要去掉 env 变量周围的引号,因为现在您只是传递一个字符串。它应该是 loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY);假设你有这个环境变量可用于终端,它应该可以工作。
感谢@blessanm86
如果您尝试遵循 stripe 文档,从 CardElement 迁移到 PaymentElement 可能会很痛苦。您的情况可能有所不同,但如果有人尝试从 CardElement 迁移到 PaymentElement,则有一些要点需要注意。
大多数时候的问题是您想要收取的价格不是恒定的。因此,您必须为每个付款请求发出一个 post 请求来获取 clientSecret。由于您通常不使用 app.js 来发出此类请求,因此您可以将支付组件分开(最好使用不同的 URL,例如 yourapp/ payment 等)。它与 CardElement 配合得很好,您可以创建一个 payment.js 组件并通过传递 stripe={stripePromise} 来用 Elements 包装它。但是,如果您想在 Payment.js 中使用 PaymentElement。然后你还必须传递 options={options}。选项常量内部有 clientSecret 值,您也必须事先澄清该值。如果你不这样做,那就行不通。如果您尝试按照顶部评论的建议用“{ clientSecret && ”将其包装,那么您将无法在客户端看到 payment.js,因为您没有 clientSecret。 Stripe 要求 clientSecret 有一个适当的值。
Stripe 在这里造成了先有鸡还是先有蛋的问题,如果我无法进入 Payment 组件,我将如何获得 payment.js 工作所需的 clientSecret?因为,如果 payment.js 不起作用,那么我就无法发出 post 请求,也无法获取 clientSecret。
问题的解决方案是您必须再创建 1 个组件。 app.js、 payment.js 和 checkout.js。 stripe 文档建议您在 app.js 中进行的任何操作都将在 payment.js 中完成。所有逻辑、价格计算等都将在 payment.js 中。您将保持 checkout.js 与文档中的相同。然后,“鲍勃是你叔叔”
他们的设计太糟糕了,很难相信一家拥有 Stripe 工程能力的公司创造了它。正如 oğuz gürkan bilir 所描述的,这是一个“先有鸡还是先有蛋”的问题。如果您在同一页面上输入用户地址信息和付款信息,但该地址随后由于税费而更改了总价,那么您最终会遇到一个问题,即 Elements 组件现在的价格不正确且无法更新因为您使用的是客户端密钥而不是“模式”。如果您使用 options.mode,则只能在事后更改金额。但如果您想避免这种情况并且仅在计算最终价格后创建意图,那么您基本上无法在最终价格确定之前渲染 PaymentElement。但是,如果用户在安装元素后犯了错误并返回并更改了其地址,从而触发了总金额更改,该怎么办?我猜您现在必须添加一堆额外的逻辑来卸载和重新安装 PaymentElement。
太糟糕了。