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'));
|
||||
$totalAmount = $request->request->getString('totalAmount');
|
||||
$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.');
|
||||
|
||||
return $this->redirectToRoute('app_admin_clients_show', ['id' => $customerId, 'tab' => 'esyflex']);
|
||||
@@ -55,12 +53,12 @@ class EFlexController extends AbstractController
|
||||
$monthlyAmount = round($totalFloat / $nbEcheances, 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) {
|
||||
$scheduledAt = $start->modify('+'.($i - 1).' months');
|
||||
$scheduledAt = 1 === $i ? $firstDate : $firstDate->modify('+'.($i - 1) * 2 .' months');
|
||||
$amount = $i === $nbEcheances
|
||||
? number_format($totalFloat - ($monthlyAmount * ($nbEcheances - 1)), 2, '.', '')
|
||||
: number_format($monthlyAmount, 2, '.', '');
|
||||
|
||||
@@ -313,13 +313,28 @@ class EFlexProcessController extends AbstractController
|
||||
// @codeCoverageIgnoreStart
|
||||
\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);
|
||||
$cancelUrl = $successUrl;
|
||||
|
||||
$checkoutSession = \Stripe\Checkout\Session::create([
|
||||
'mode' => 'payment',
|
||||
'payment_method_types' => ['card'],
|
||||
'customer_email' => $eflex->getCustomer()->getEmail(),
|
||||
'customer' => $stripeCustomerId,
|
||||
'line_items' => [[
|
||||
'price_data' => [
|
||||
'currency' => 'eur',
|
||||
@@ -331,6 +346,7 @@ class EFlexProcessController extends AbstractController
|
||||
'quantity' => 1,
|
||||
]],
|
||||
'payment_intent_data' => [
|
||||
'setup_future_usage' => 'off_session',
|
||||
'metadata' => [
|
||||
'eflex_id' => (string) $eflex->getId(),
|
||||
'eflex_line_id' => (string) $line->getId(),
|
||||
|
||||
@@ -685,6 +685,16 @@ class WebhookStripeController extends AbstractController
|
||||
$line->setPaidAt(new \DateTimeImmutable());
|
||||
$line->setStripePaymentIntentId($paymentIntent->id);
|
||||
$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();
|
||||
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user