feat: E-Flex - sauvegarde PaymentMethod CB + echeances auto (1ere +2j, suivantes tous les 2 mois)
- Checkout Session avec setup_future_usage=off_session + customer Stripe pour sauvegarder la carte et permettre les prelevements futurs - Webhook payment_intent.succeeded stocke le PaymentMethod sur EFlex si pas deja configure (permet cron auto ensuite) - 1ere echeance = creation +2 jours (pas de champ startDate) - Echeances suivantes = tous les 2 mois apres la 1ere - Retrait du champ date 1ere echeance du formulaire (automatique) - Info dans le formulaire: "La 1ere echeance sera due 2 jours apres la signature. Les suivantes tous les 2 mois." Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,10 +42,8 @@ class EFlexController extends AbstractController
|
|||||||
$description = trim($request->request->getString('description'));
|
$description = trim($request->request->getString('description'));
|
||||||
$totalAmount = $request->request->getString('totalAmount');
|
$totalAmount = $request->request->getString('totalAmount');
|
||||||
$nbEcheances = $request->request->getInt('nbEcheances');
|
$nbEcheances = $request->request->getInt('nbEcheances');
|
||||||
$startDate = $request->request->getString('startDate');
|
|
||||||
$paymentMethod = $request->request->getString('paymentMethod', EFlex::METHOD_SEPA);
|
|
||||||
|
|
||||||
if ('' === $description || $nbEcheances < 2 || $nbEcheances > 36 || '' === $startDate) {
|
if ('' === $description || $nbEcheances < 2 || $nbEcheances > 36) {
|
||||||
$this->addFlash('error', 'Donnees invalides. Minimum 2 echeances, maximum 36.');
|
$this->addFlash('error', 'Donnees invalides. Minimum 2 echeances, maximum 36.');
|
||||||
|
|
||||||
return $this->redirectToRoute('app_admin_clients_show', ['id' => $customerId, 'tab' => 'esyflex']);
|
return $this->redirectToRoute('app_admin_clients_show', ['id' => $customerId, 'tab' => 'esyflex']);
|
||||||
@@ -55,12 +53,12 @@ class EFlexController extends AbstractController
|
|||||||
$monthlyAmount = round($totalFloat / $nbEcheances, 2);
|
$monthlyAmount = round($totalFloat / $nbEcheances, 2);
|
||||||
|
|
||||||
$eflex = new EFlex($customer, $description, number_format($totalFloat, 2, '.', ''));
|
$eflex = new EFlex($customer, $description, number_format($totalFloat, 2, '.', ''));
|
||||||
$eflex->setPaymentMethod($paymentMethod);
|
|
||||||
|
|
||||||
$start = new \DateTimeImmutable($startDate);
|
// 1ere echeance = maintenant +2 jours, les suivantes tous les 2 mois
|
||||||
|
$firstDate = (new \DateTimeImmutable())->modify('+2 days');
|
||||||
|
|
||||||
for ($i = 1; $i <= $nbEcheances; ++$i) {
|
for ($i = 1; $i <= $nbEcheances; ++$i) {
|
||||||
$scheduledAt = $start->modify('+'.($i - 1).' months');
|
$scheduledAt = 1 === $i ? $firstDate : $firstDate->modify('+'.($i - 1) * 2 .' months');
|
||||||
$amount = $i === $nbEcheances
|
$amount = $i === $nbEcheances
|
||||||
? number_format($totalFloat - ($monthlyAmount * ($nbEcheances - 1)), 2, '.', '')
|
? number_format($totalFloat - ($monthlyAmount * ($nbEcheances - 1)), 2, '.', '')
|
||||||
: number_format($monthlyAmount, 2, '.', '');
|
: number_format($monthlyAmount, 2, '.', '');
|
||||||
|
|||||||
@@ -313,13 +313,28 @@ class EFlexProcessController extends AbstractController
|
|||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
\Stripe\Stripe::setApiKey($stripeSk);
|
\Stripe\Stripe::setApiKey($stripeSk);
|
||||||
|
|
||||||
|
$customer = $eflex->getCustomer();
|
||||||
|
|
||||||
|
// Creer le customer Stripe si besoin
|
||||||
|
$stripeCustomerId = $eflex->getStripeCustomerId() ?? $customer->getStripeCustomerId();
|
||||||
|
if (null === $stripeCustomerId) {
|
||||||
|
$stripeCustomer = \Stripe\Customer::create([
|
||||||
|
'email' => $customer->getEmail(),
|
||||||
|
'name' => $customer->getFullName(),
|
||||||
|
]);
|
||||||
|
$stripeCustomerId = $stripeCustomer->id;
|
||||||
|
$customer->setStripeCustomerId($stripeCustomerId);
|
||||||
|
$eflex->setStripeCustomerId($stripeCustomerId);
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
$successUrl = $this->generateUrl('app_eflex_process', ['id' => $id], UrlGeneratorInterface::ABSOLUTE_URL);
|
$successUrl = $this->generateUrl('app_eflex_process', ['id' => $id], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||||
$cancelUrl = $successUrl;
|
$cancelUrl = $successUrl;
|
||||||
|
|
||||||
$checkoutSession = \Stripe\Checkout\Session::create([
|
$checkoutSession = \Stripe\Checkout\Session::create([
|
||||||
'mode' => 'payment',
|
'mode' => 'payment',
|
||||||
'payment_method_types' => ['card'],
|
'payment_method_types' => ['card'],
|
||||||
'customer_email' => $eflex->getCustomer()->getEmail(),
|
'customer' => $stripeCustomerId,
|
||||||
'line_items' => [[
|
'line_items' => [[
|
||||||
'price_data' => [
|
'price_data' => [
|
||||||
'currency' => 'eur',
|
'currency' => 'eur',
|
||||||
@@ -331,6 +346,7 @@ class EFlexProcessController extends AbstractController
|
|||||||
'quantity' => 1,
|
'quantity' => 1,
|
||||||
]],
|
]],
|
||||||
'payment_intent_data' => [
|
'payment_intent_data' => [
|
||||||
|
'setup_future_usage' => 'off_session',
|
||||||
'metadata' => [
|
'metadata' => [
|
||||||
'eflex_id' => (string) $eflex->getId(),
|
'eflex_id' => (string) $eflex->getId(),
|
||||||
'eflex_line_id' => (string) $line->getId(),
|
'eflex_line_id' => (string) $line->getId(),
|
||||||
|
|||||||
@@ -685,6 +685,16 @@ class WebhookStripeController extends AbstractController
|
|||||||
$line->setPaidAt(new \DateTimeImmutable());
|
$line->setPaidAt(new \DateTimeImmutable());
|
||||||
$line->setStripePaymentIntentId($paymentIntent->id);
|
$line->setStripePaymentIntentId($paymentIntent->id);
|
||||||
$line->setPaidMethod('stripe');
|
$line->setPaidMethod('stripe');
|
||||||
|
|
||||||
|
// Sauvegarder le PaymentMethod pour les prelevements futurs (si pas deja fait)
|
||||||
|
if (null === $eflex->getStripePaymentMethodId()) {
|
||||||
|
$pmId = $paymentIntent->payment_method ?? null;
|
||||||
|
if (null !== $pmId) {
|
||||||
|
$eflex->setStripePaymentMethodId((string) $pmId);
|
||||||
|
$eflex->setState(\App\Entity\EFlex::STATE_ACTIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
|
|
||||||
if ($eflex->getNbPaid() >= $eflex->getNbLines()) {
|
if ($eflex->getNbPaid() >= $eflex->getNbLines()) {
|
||||||
|
|||||||
@@ -1284,11 +1284,8 @@
|
|||||||
<label for="eflex-nbEcheances" class="block text-[9px] font-bold uppercase tracking-wider text-gray-400 mb-1">Nombre d'echeances *</label>
|
<label for="eflex-nbEcheances" class="block text-[9px] font-bold uppercase tracking-wider text-gray-400 mb-1">Nombre d'echeances *</label>
|
||||||
<input type="number" id="eflex-nbEcheances" name="nbEcheances" min="2" max="36" value="3" required class="input-glass w-full px-3 py-2 text-xs font-bold">
|
<input type="number" id="eflex-nbEcheances" name="nbEcheances" min="2" max="36" value="3" required class="input-glass w-full px-3 py-2 text-xs font-bold">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<label for="eflex-startDate" class="block text-[9px] font-bold uppercase tracking-wider text-gray-400 mb-1">Date 1ere echeance *</label>
|
|
||||||
<input type="date" id="eflex-startDate" name="startDate" required class="input-glass w-full px-3 py-2 text-xs font-bold">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="text-[10px] text-gray-400 mb-4">La 1ere echeance sera due 2 jours apres la signature. Les suivantes tous les 2 mois.</p>
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
<button type="button" data-modal-close="modal-eflex" class="px-4 py-2 glass font-bold uppercase text-[10px] tracking-widest">Annuler</button>
|
<button type="button" data-modal-close="modal-eflex" class="px-4 py-2 glass font-bold uppercase text-[10px] tracking-widest">Annuler</button>
|
||||||
<button type="submit" class="btn-gold px-4 py-2 font-bold uppercase text-[10px] tracking-wider text-gray-900">Creer</button>
|
<button type="submit" class="btn-gold px-4 py-2 font-bold uppercase text-[10px] tracking-wider text-gray-900">Creer</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user