我正在尝试在我的 Symfony 5 应用程序(后端:PHP,前端 JS)中实现 Stripe 以创建订阅(价格取决于选择的数量)
我想要这种情况:
用户为其订阅选择数量
它被重定向到结帐页面(我自己创建的,而不是他们自己托管的 Stripe Checkout 实用程序)
他输入他的付款信息。
如有必要,SCA 身份验证 (3D SECURE)。
如果一切正常,我创建订阅。
所以,从第 3 步开始,我尝试了这个:
我的后端端点:
/**
* @Route("/paiement/process", name="website_payment_process", options={"expose"=true})
*/
public function process(Request $request, $stripeSecretKey, ShoppingCart $cart, StripeClient $stripeClient, EntityManagerInterface $manager)
{
\Stripe\Stripe::setApiKey($stripeSecretKey);
$json_obj = json_decode($request->getContent());
$intent = null;
try {
if (isset($json_obj->payment_method_id)) {
// Create the PaymentIntent
$intent = \Stripe\PaymentIntent::create([
'payment_method' => $json_obj->payment_method_id,
'confirmation_method' => 'manual',
'confirm' => true,
'amount' => $cart->getTotalWithDiscount(false) * 100,
'currency' => 'eur',
'description' => 'Mon paiement',
'setup_future_usage' => 'off_session',
]);
}
if (isset($json_obj->payment_intent_id)) {
$intent = \Stripe\PaymentIntent::retrieve(
$json_obj->payment_intent_id
);
$intent->confirm();
}
if ('requires_action' == $intent->status &&
'use_stripe_sdk' == $intent->next_action->type) {
// Tell the client to handle the action
return new JsonResponse([
'requires_action' => true,
'payment_intent_client_secret' => $intent->client_secret,
]);
} elseif ('succeeded' == $intent->status) {
// Paiement Stripe accepté
$customer = \Stripe\Customer::create([
'description' => 'Abonnement à mon application',
'preferred_locales' => ['fr'],
'email' => $json_obj->email,
'payment_method' => $intent->payment_method,
]);
$stripeCustomer = new StripeCustomer();
$stripeCustomer->setStripeCustomerId($customer->id);
$this->getUser()->setStripeCustomer($stripeCustomer);
$manager->persist($this->getUser());
$manager->flush();
// Created the subscription in association with Customer Id
$stripeClient->createSubscription(
$this->getUser(),
$cart->getPlan(),
true,
$cart->getQuantity(),
$cart->getCouponId()
);
return new JsonResponse([
'success' => true,
]);
} else {
return new JsonResponse(['error' => 'Invalid PaymentIntent status'], 500);
}
} catch (\Exception $e) {
// Display error on client
return new JsonResponse([
'error' => $e->getMessage(),
]);
}
}
和 JS:
if($("#payment-form").length !== 0){
const STRIPE_PUBLIC_KEY = process.env.STRIPE_PUBLIC_KEY;
// Stripe.setPublishableKey(STRIPE_PUBLIC_KEY);
var stripe = Stripe(STRIPE_PUBLIC_KEY);
var processUrl = Routing.generate('website_payment_process');
var elements = stripe.elements();
var card = elements.create('card', {
style: {
base: {
iconColor: '#666EE8',
color: '#31325F',
lineHeight: '40px',
fontWeight: 300,
fontFamily: 'Helvetica Neue',
fontSize: '15px',
'::placeholder': {
color: '#CFD7E0',
},
},
}
});
card.mount('#card-element');
function setOutcome(result) {
var errorElement = document.querySelector('.error');
errorElement.classList.remove('visible');
if (result.token) {
$('#stripeToken').val(result.token.id);
$('#payment-form').submit();
} else if (result.error) {
errorElement.textContent = result.error.message;
errorElement.classList.add('visible');
}
}
card.on('change', function(event) {
setOutcome(event);
});
document.getElementById('payment-form').addEventListener('submit', function(e) {
e.preventDefault();
var form = document.getElementById('payment-form');
stripe.createPaymentMethod('card', card, {
billing_details: {name: form.querySelector('input[name=cardholder-name]').value}
}).then(function(result) {
if (result.error) {
var errorElement = document.querySelector('.error');
errorElement.textContent = result.error.message;
errorElement.classList.add('visible');
} else {
$('#buttonPayment').hide();
$('#spanWaitPayement').show();
// Otherwise send paymentMethod.id to your server (see Step 2)
fetch(processUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
},
body: JSON.stringify({ payment_method_id: result.paymentMethod.id })
}).then(function(result) {
result.json().then(function(json) {
handleServerResponse(json);
})
});
}
});
});
function handleServerResponse(response) {
if (response.error) {
$('#buttonPayment').show();
$('#spanWaitPayement').hide();
var errorElement = document.querySelector('.error');
errorElement.textContent = result.error.message;
errorElement.classList.add('visible');
} else if (response.requires_action) {
// Use Stripe.js to handle required card action
stripe.handleCardAction(
response.payment_intent_client_secret
).then(function(result) {
if (result.error) {
$('#buttonPayment').show();
$('#spanWaitPayement').hide();
var errorElement = document.querySelector('.error');
errorElement.textContent = result.error.message;
errorElement.classList.add('visible');
} else {
// The card action has been handled
// The PaymentIntent can be confirmed again on the server
fetch(processUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
},
body: JSON.stringify({
payment_intent_id: result.paymentIntent.id,
email: $('#email').val(),
})
}).then(function(confirmResult) {
return confirmResult.json();
}).then(handleServerResponse);
}
});
} else {
// window.location.replace('/paiement-ok');
}
}
}
我设法检索付款信息,使用 SCA 身份验证并进行付款。但是我肯定是错的,因为如果支付成功,就在我创建客户、订阅和关联之前使用的 PaymentIntent 之后。并且不可避免地它不起作用,订阅“不完整”,他再次尝试重新向客户收费