我有java+react应用程序。想要实施固定价格订阅。我确实遵循了这个示例 github 但看起来我错过了一些东西。
客户输入卡后,我可以在 Stripe 仪表板中看到付款已处理,但订阅仍处于“未完成”状态。这个问题与这个问题类似,但即使添加 ON_SUBSCRIPTION 后我仍然遇到问题。
Java部分:
@GetMapping("/stripe/prices")
public Response<PricesResponse> prices() {
RequestOptions options = RequestOptions.builder()
.setApiKey(apiKey)
.build();
// search customer
try {
CustomerSearchParams params =
CustomerSearchParams.builder()
.setQuery("name:'aaa bbb'")
.build();
CustomerSearchResult customers = Customer.search(params, options);
List<String> names = customers.getData()
.stream()
.map(Customer::getName)
.toList();
logger.info(String.valueOf(names));
} catch (StripeException e) {
throw new RuntimeException(e);
}
// create customer
Customer customer;
try {
String customerName = "Jenny Rosen " + System.currentTimeMillis();
System.out.println(customerName);
CustomerCreateParams params =
CustomerCreateParams.builder()
.setName(customerName)
.setEmail("[email protected]")
.build();
customer = Customer.create(params, options);
} catch (StripeException e) {
throw new RuntimeException(e);
}
// get prices
PriceCollection prices = new PriceCollection();
try {
PriceListParams params = PriceListParams
.builder()
.build();
prices = Price.list(params, options);
} catch (StripeException e) {
throw new RuntimeException(e);
}
// create subscriptions
Subscription subscription;
try {
SubscriptionCreateParams.PaymentSettings paymentSettings =
SubscriptionCreateParams.PaymentSettings
.builder()
.setSaveDefaultPaymentMethod(SubscriptionCreateParams.PaymentSettings.SaveDefaultPaymentMethod.ON_SUBSCRIPTION)
.build();
SubscriptionCreateParams subCreateParams = SubscriptionCreateParams
.builder()
.setCustomer(customer.getId())
.addItem(
SubscriptionCreateParams
.Item.builder()
.setPrice(prices.getData().get(0).getId())
.build()
)
.setPaymentSettings(paymentSettings)
.setPaymentBehavior(SubscriptionCreateParams.PaymentBehavior.DEFAULT_INCOMPLETE)
.addAllExpand(Arrays.asList("latest_invoice.payment_intent"))
.build();
subscription = Subscription.create(subCreateParams, options);
} catch (StripeException e) {
throw new RuntimeException(e);
}
// create payment intent
PaymentIntent paymentIntent;
try {
PaymentIntentCreateParams params =
PaymentIntentCreateParams.builder()
.setCustomer(customer.getId())
.setAmount(prices.getData().get(0).getUnitAmount())
.setCurrency("usd")
.build();
paymentIntent = PaymentIntent.create(params, options);
} catch (StripeException e) {
throw new RuntimeException(e);
}
return new Response<>(new PricesResponse(publishableKey, paymentIntent.getClientSecret(), prices.getData()
.stream()
.map(price -> new PriceResponse(price.getId(), price.getNickname(), price.getUnitAmount()))
.toList()));
}
反应部分:
价格页面
const Prices = () => {
const navigate = useNavigate();
const [prices, setPrices] = useState([]);
const [clientSecret, setClientSecret] = useState("");
useEffect(() => {
doRestCall('/stripe/prices', 'get', null, null,
(response) => {
setPrices(response.body.prices)
setClientSecret(response.body.clientSecret)
})
}, [])
function toCheckout() {
navigate('/checkout', {
state: {
clientSecret
}
})
}
return (
<div>
<h1>Select a plan</h1>
<div className="price-list">
{prices.map((price) => {
return (
<div key={price.id}>
<h3>{price.name}</h3>
<p>
${price.amount / 100} / month
</p>
<button onClick={() => toCheckout()}>
Select
</button>
</div>
)
})}
</div>
</div>
)}
结账页面
const Checkout = () => {
const {
state: {
clientSecret,
}
} = useLocation();
const stripe = useStripe();
const elements = useElements();
const [name, setName] = useState('Jenny Rosen');
const [messages, setMessages] = useState('');
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
const cardElement = elements.getElement(CardElement);
const { error } = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: name,
}
}
});
if(error) {
// show error and collect new card details.
setMessages(error.message);
return;
}
navigate('/complete', {
state: {
clientSecret
}
});
};
return (<>
<h1>Subscribe</h1>
<p>
Try the successful test card: <span>4242424242424242</span>.
</p>
<p>
Try the test card that requires SCA: <span>4000002500003155</span>.
</p>
<p>
Use any <i>future</i> expiry date, CVC,5 digit postal code
</p>
<hr/>
<form onSubmit={handleSubmit}>
<label>
Full name
<input type="text" id="name" value={name} onChange={(e) => setName(e.target.value)}/>
</label>
<CardElement/>
<button>
Subscribe
</button>
<div>{messages}</div>
</form>
</>);
还有关于订阅如何工作页面中的订阅流程的问题。其中提到了“发票”。我应该手动处理吗?我确实在 GitHub 示例中的 java 端看到了 /invoice-preview,但找不到从 React 部分调用它的位置。
您应该只返回与订阅的
latest_invoice
关联的 PaymentIntent 的 clientSecret,而不是创建新的 PaymentIntent 并返回其 clientSecret。
您可以在集成中找到示例代码指南