createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn(new Event()); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], [ 'HTTP_STRIPE_SIGNATURE' => 'valid', ], '{}'); self::assertResponseIsSuccessful(); self::assertSame('OK', $client->getResponse()->getContent()); } public function testWebhookWithInvalidSignature(): void { $client = static::createClient(); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn(null); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], [ 'HTTP_STRIPE_SIGNATURE' => 'invalid', ], '{}'); self::assertResponseStatusCodeSame(400); } public function testAccountCreatedSetsStartedStatus(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_created'); $event = new Event(); $event->type = 'v2.core.account.created'; $this->mockStripe($client, $event); $payload = json_encode(['related_object' => ['id' => 'acct_created']]); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_created']); self::assertSame('started', $updated->getStripeStatus()); } public function testAccountClosedDisablesFlags(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_closed'); $user->setStripeChargesEnabled(true); $user->setStripePayoutsEnabled(true); $em->flush(); $event = new Event(); $event->type = 'v2.core.account.closed'; $this->mockStripe($client, $event); $payload = json_encode(['related_object' => ['id' => 'acct_closed']]); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_closed']); self::assertSame('closed', $updated->getStripeStatus()); self::assertFalse($updated->isStripeChargesEnabled()); self::assertFalse($updated->isStripePayoutsEnabled()); } public function testAccountUpdatedSetsStatus(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $this->createOrgaUser($em, 'acct_upd'); $event = new Event(); $event->type = 'v2.core.account.updated'; $this->mockStripe($client, $event); $payload = json_encode(['related_object' => ['id' => 'acct_upd']]); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_upd']); self::assertSame('updated', $updated->getStripeStatus()); } public function testAccountStatusUnknownUser(): void { $client = static::createClient(); $event = new Event(); $event->type = 'v2.core.account.created'; $this->mockStripe($client, $event); $payload = json_encode(['related_object' => ['id' => 'acct_nonexistent']]); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload); self::assertResponseIsSuccessful(); } public function testAccountStatusNoRelatedObject(): void { $client = static::createClient(); $event = new Event(); $event->type = 'v2.core.account.created'; $this->mockStripe($client, $event); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } public function testMerchantCapabilityCardPaymentsActive(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $this->createOrgaUser($em, 'acct_merchant'); $event = new Event(); $event->type = 'v2.core.account[configuration.merchant].capability_status_updated'; $this->mockStripe($client, $event); $payload = json_encode([ 'related_object' => ['id' => 'acct_merchant'], 'changes' => ['after' => ['configuration' => [ 'merchant' => ['capabilities' => [ 'card_payments' => ['status' => 'active'], ]], ]]], ]); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_merchant']); self::assertTrue($updated->isStripeChargesEnabled()); } public function testRecipientCapabilityPayoutsActive(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $this->createOrgaUser($em, 'acct_recipient'); $event = new Event(); $event->type = 'v2.core.account[configuration.recipient].capability_status_updated'; $this->mockStripe($client, $event); $payload = json_encode([ 'related_object' => ['id' => 'acct_recipient'], 'changes' => ['after' => ['configuration' => [ 'recipient' => ['capabilities' => [ 'stripe_balance' => ['payouts' => ['status' => 'active']], ]], ]]], ]); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_recipient']); self::assertTrue($updated->isStripePayoutsEnabled()); } public function testCapabilityBothActiveSetStatusActive(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_both'); $user->setStripeChargesEnabled(true); $em->flush(); $event = new Event(); $event->type = 'v2.core.account[configuration.merchant].capability_status_updated'; $this->mockStripe($client, $event); $payload = json_encode([ 'related_object' => ['id' => 'acct_both'], 'changes' => ['after' => ['configuration' => [ 'merchant' => ['capabilities' => [ 'stripe_balance' => ['payouts' => ['status' => 'active']], ]], ]]], ]); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(User::class)->findOneBy(['stripeAccountId' => 'acct_both']); self::assertSame('active', $updated->getStripeStatus()); } public function testCapabilityUnknownUser(): void { $client = static::createClient(); $event = new Event(); $event->type = 'v2.core.account[configuration.merchant].capability_status_updated'; $this->mockStripe($client, $event); $payload = json_encode([ 'related_object' => ['id' => 'acct_ghost'], 'changes' => ['after' => ['configuration' => []]], ]); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], $payload); self::assertResponseIsSuccessful(); } public function testPayoutCreatedSavesAndSendsEmail(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_payout'); $event = Event::constructFrom([ 'type' => 'payout.created', 'account' => 'acct_payout', 'data' => ['object' => [ 'id' => 'po_test_wh_'.uniqid(), 'status' => 'pending', 'amount' => 5000, 'currency' => 'eur', 'destination' => 'ba_xxx', ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $mailer = $this->createMock(MailerService::class); $mailer->expects(self::once())->method('sendEmail'); static::getContainer()->set(MailerService::class, $mailer); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } public function testPayoutPaidSendsEmailWithPdf(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_paid'); $payoutId = 'po_paid_'.uniqid(); $event = Event::constructFrom([ 'type' => 'payout.paid', 'account' => 'acct_paid', 'data' => ['object' => [ 'id' => $payoutId, 'status' => 'paid', 'amount' => 10000, 'currency' => 'eur', 'destination' => 'ba_yyy', 'arrival_date' => time() + 86400, ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $mailer = $this->createMock(MailerService::class); $mailer->expects(self::once())->method('sendEmail'); static::getContainer()->set(MailerService::class, $mailer); $pdfService = $this->createMock(PayoutPdfService::class); $pdfService->expects(self::once())->method('generateToFile')->willReturn('/tmp/fake.pdf'); static::getContainer()->set(PayoutPdfService::class, $pdfService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $payout = $freshEm->getRepository(Payout::class)->findOneBy(['stripePayoutId' => $payoutId]); self::assertNotNull($payout); self::assertSame('paid', $payout->getStatus()); self::assertSame(10000, $payout->getAmount()); } public function testPayoutUpdatedExistingPayout(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_upd_po'); $payout = new Payout(); $payout->setOrganizer($user); $payout->setStripePayoutId('po_existing_'.uniqid()); $payout->setStatus('pending'); $payout->setAmount(3000); $payout->setCurrency('eur'); $payout->setStripeAccountId('acct_upd_po'); $em->persist($payout); $em->flush(); $event = Event::constructFrom([ 'type' => 'payout.updated', 'account' => 'acct_upd_po', 'data' => ['object' => [ 'id' => $payout->getStripePayoutId(), 'status' => 'in_transit', 'amount' => 3000, 'currency' => 'eur', 'destination' => 'ba_zzz', ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $mailer = $this->createMock(MailerService::class); $mailer->expects(self::once())->method('sendEmail'); static::getContainer()->set(MailerService::class, $mailer); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(Payout::class)->findOneBy(['stripePayoutId' => $payout->getStripePayoutId()]); self::assertSame('in_transit', $updated->getStatus()); } public function testPayoutNoPayoutId(): void { $client = static::createClient(); $event = Event::constructFrom([ 'type' => 'payout.created', 'data' => ['object' => []], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } public function testPayoutNoUser(): void { $client = static::createClient(); $event = Event::constructFrom([ 'type' => 'payout.created', 'account' => 'acct_nouser', 'data' => ['object' => [ 'id' => 'po_nouser_'.uniqid(), 'status' => 'pending', 'amount' => 1000, 'currency' => 'eur', ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } // === payment_intent.payment_failed === public function testPaymentFailedCancelsOrder(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_pf_'.uniqid()); $order = $this->createTestOrder($em, $user); $event = Event::constructFrom([ 'type' => 'payment_intent.payment_failed', 'data' => ['object' => [ 'metadata' => ['order_id' => (string) $order->getId()], 'last_payment_error' => ['message' => 'Your card was declined.'], ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $mailer = $this->createMock(MailerService::class); $mailer->expects(self::once())->method('sendEmail'); static::getContainer()->set(MailerService::class, $mailer); $audit = $this->createMock(AuditService::class); $audit->expects(self::once())->method('log')->with('payment_failed', 'BilletBuyer', $order->getId(), $this->anything()); static::getContainer()->set(AuditService::class, $audit); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(BilletBuyer::class)->find($order->getId()); self::assertSame(BilletBuyer::STATUS_CANCELLED, $updated->getStatus()); } public function testPaymentFailedNoOrderId(): void { $client = static::createClient(); $event = Event::constructFrom([ 'type' => 'payment_intent.payment_failed', 'data' => ['object' => ['metadata' => []]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } public function testPaymentFailedOrderNotFound(): void { $client = static::createClient(); $event = Event::constructFrom([ 'type' => 'payment_intent.payment_failed', 'data' => ['object' => [ 'metadata' => ['order_id' => '999999'], ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } public function testPaymentFailedOrderAlreadyPaid(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_pf_paid_'.uniqid()); $order = $this->createTestOrder($em, $user); $order->setStatus(BilletBuyer::STATUS_PAID); $em->flush(); $event = Event::constructFrom([ 'type' => 'payment_intent.payment_failed', 'data' => ['object' => [ 'metadata' => ['order_id' => (string) $order->getId()], ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(BilletBuyer::class)->find($order->getId()); self::assertSame(BilletBuyer::STATUS_PAID, $updated->getStatus()); } public function testPaymentFailedDefaultErrorMessage(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_pf_def_'.uniqid()); $order = $this->createTestOrder($em, $user); $event = Event::constructFrom([ 'type' => 'payment_intent.payment_failed', 'data' => ['object' => [ 'metadata' => ['order_id' => (string) $order->getId()], ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $mailer = $this->createMock(MailerService::class); $mailer->expects(self::once())->method('sendEmail'); static::getContainer()->set(MailerService::class, $mailer); $audit = $this->createMock(AuditService::class); $audit->expects(self::once())->method('log')->with( 'payment_failed', 'BilletBuyer', $order->getId(), $this->callback(fn (array $d) => 'Paiement refuse' === $d['error']) ); static::getContainer()->set(AuditService::class, $audit); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } public function testPaymentFailedNoEmail(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_pf_nomail_'.uniqid()); $order = $this->createTestOrder($em, $user); $order->setEmail(null); $em->flush(); $event = Event::constructFrom([ 'type' => 'payment_intent.payment_failed', 'data' => ['object' => [ 'metadata' => ['order_id' => (string) $order->getId()], ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $mailer = $this->createMock(MailerService::class); $mailer->expects(self::never())->method('sendEmail'); static::getContainer()->set(MailerService::class, $mailer); $audit = $this->createMock(AuditService::class); static::getContainer()->set(AuditService::class, $audit); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } // === charge.refunded === public function testChargeRefundedUpdatesOrder(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_cr_'.uniqid()); $order = $this->createTestOrder($em, $user); $order->setStatus(BilletBuyer::STATUS_PAID); $order->setStripeSessionId('pi_refund_test_'.uniqid()); $em->flush(); $event = Event::constructFrom([ 'type' => 'charge.refunded', 'data' => ['object' => [ 'payment_intent' => $order->getStripeSessionId(), ]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $mailer = $this->createMock(MailerService::class); $mailer->expects(self::atLeastOnce())->method('sendEmail'); static::getContainer()->set(MailerService::class, $mailer); $audit = $this->createMock(AuditService::class); $audit->expects(self::once())->method('log')->with('order_refunded_webhook', 'BilletBuyer', $order->getId(), $this->anything()); static::getContainer()->set(AuditService::class, $audit); $billetOrderService = $this->createMock(BilletOrderService::class); $billetOrderService->expects(self::once())->method('notifyOrganizerCancelled')->with($this->anything(), 'remboursee'); static::getContainer()->set(BilletOrderService::class, $billetOrderService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(BilletBuyer::class)->find($order->getId()); self::assertSame(BilletBuyer::STATUS_REFUNDED, $updated->getStatus()); } public function testChargeRefundedInvalidatesTickets(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_cr_tk_'.uniqid()); $order = $this->createTestOrder($em, $user); $order->setStatus(BilletBuyer::STATUS_PAID); $piId = 'pi_ticket_refund_'.uniqid(); $order->setStripeSessionId($piId); $ticket = new BilletOrder(); $ticket->setBilletBuyer($order); $ticket->setBilletName('Entree'); $ticket->setUnitPriceHT(1500); $ticket->setState(BilletOrder::STATE_VALID); $em->persist($ticket); $em->flush(); $event = Event::constructFrom([ 'type' => 'charge.refunded', 'data' => ['object' => ['payment_intent' => $piId]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $mailer = $this->createMock(MailerService::class); static::getContainer()->set(MailerService::class, $mailer); $audit = $this->createMock(AuditService::class); static::getContainer()->set(AuditService::class, $audit); $billetOrderService = $this->createMock(BilletOrderService::class); static::getContainer()->set(BilletOrderService::class, $billetOrderService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updatedTicket = $freshEm->getRepository(BilletOrder::class)->find($ticket->getId()); self::assertSame(BilletOrder::STATE_INVALID, $updatedTicket->getState()); } public function testChargeRefundedNoPaymentIntent(): void { $client = static::createClient(); $event = Event::constructFrom([ 'type' => 'charge.refunded', 'data' => ['object' => []], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } public function testChargeRefundedOrderNotFound(): void { $client = static::createClient(); $event = Event::constructFrom([ 'type' => 'charge.refunded', 'data' => ['object' => ['payment_intent' => 'pi_nonexistent_'.uniqid()]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } public function testChargeRefundedAlreadyRefunded(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_cr_dup_'.uniqid()); $order = $this->createTestOrder($em, $user); $piId = 'pi_already_refunded_'.uniqid(); $order->setStatus(BilletBuyer::STATUS_REFUNDED); $order->setStripeSessionId($piId); $em->flush(); $event = Event::constructFrom([ 'type' => 'charge.refunded', 'data' => ['object' => ['payment_intent' => $piId]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); $freshEm = static::getContainer()->get(EntityManagerInterface::class); $updated = $freshEm->getRepository(BilletBuyer::class)->find($order->getId()); self::assertSame(BilletBuyer::STATUS_REFUNDED, $updated->getStatus()); } public function testChargeRefundedNoEmail(): void { $client = static::createClient(); $em = static::getContainer()->get(EntityManagerInterface::class); $user = $this->createOrgaUser($em, 'acct_cr_nomail_'.uniqid()); $order = $this->createTestOrder($em, $user); $order->setStatus(BilletBuyer::STATUS_PAID); $piId = 'pi_nomail_'.uniqid(); $order->setStripeSessionId($piId); $order->setEmail(null); $em->flush(); $event = Event::constructFrom([ 'type' => 'charge.refunded', 'data' => ['object' => ['payment_intent' => $piId]], ]); $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); $mailer = $this->createMock(MailerService::class); $mailer->expects(self::never())->method('sendEmail'); static::getContainer()->set(MailerService::class, $mailer); $audit = $this->createMock(AuditService::class); static::getContainer()->set(AuditService::class, $audit); $billetOrderService = $this->createMock(BilletOrderService::class); static::getContainer()->set(BilletOrderService::class, $billetOrderService); $client->request('POST', '/stripe/webhook', [], [], ['HTTP_STRIPE_SIGNATURE' => 'v'], '{}'); self::assertResponseIsSuccessful(); } private function createTestOrder(EntityManagerInterface $em, User $user): BilletBuyer { $event = new \App\Entity\Event(); $event->setAccount($user); $event->setTitle('WH Event '.uniqid()); $event->setStartAt(new \DateTimeImmutable('2026-08-01 10:00')); $event->setEndAt(new \DateTimeImmutable('2026-08-01 18:00')); $event->setAddress('1 rue'); $event->setZipcode('75001'); $event->setCity('Paris'); $event->setIsOnline(true); $em->persist($event); $category = new Category(); $category->setName('Cat'); $category->setEvent($event); $em->persist($category); $billet = new Billet(); $billet->setName('Entree'); $billet->setCategory($category); $billet->setPriceHT(1500); $em->persist($billet); $count = $em->getRepository(BilletBuyer::class)->count([]) + 1; $order = new BilletBuyer(); $order->setEvent($event); $order->setFirstName('Jean'); $order->setLastName('Dupont'); $order->setEmail('jean-wh@test.fr'); $order->setOrderNumber('2026-03-23-'.$count); $order->setTotalHT(1500); $item = new BilletBuyerItem(); $item->setBillet($billet); $item->setBilletName('Entree'); $item->setQuantity(1); $item->setUnitPriceHT(1500); $order->addItem($item); $em->persist($order); $em->flush(); return $order; } private function createOrgaUser(EntityManagerInterface $em, string $stripeAccountId): User { $user = new User(); $user->setEmail('test-wh-'.uniqid().'@example.com'); $user->setFirstName('WH'); $user->setLastName('Test'); $user->setPassword('$2y$13$hashed'); $user->setRoles(['ROLE_ORGANIZER']); $user->setStripeAccountId($stripeAccountId); $em->persist($user); $em->flush(); return $user; } private function mockStripe(\Symfony\Bundle\FrameworkBundle\KernelBrowser $client, Event $event): void { $stripeService = $this->createMock(StripeService::class); $stripeService->method('verifyWebhookSignature')->willReturn($event); static::getContainer()->set(StripeService::class, $stripeService); } }