em->getRepository(SiteConfig::class)->findOneBy(['configKey' => 'payment_test_mode']); if ($cfg === null) { $cfg = new SiteConfig('payment_test_mode', '1'); $this->em->persist($cfg); } else { $cfg->setValue('1'); } $this->em->flush(); } private function makePayment(int $amountRials): Payment { $user = $this->createUser(); $payment = new Payment($user, $amountRials, 'mock', Payment::TYPE_SMS_WALLET); $this->em->persist($payment); $this->em->flush(); return $payment; } private function fireCallback(Payment $payment, int $reportedAmount): void { $this->client->request('POST', '/api/v1/payment/callback/mock?' . http_build_query([ 'order_id' => $payment->getOrderId(), 'mock' => '1', 'ResCode' => '0', 'mock_amount' => (string) $reportedAmount, ])); } private function reload(Payment $payment): Payment { $this->em->clear(); return $this->em->getRepository(Payment::class)->find($payment->getId()); } public function testUnderpaymentIsRejected(): void { $this->enableTestMode(); $payment = $this->makePayment(50000); $this->fireCallback($payment, 10000); $this->assertSame(Payment::STATUS_FAILED, $this->reload($payment)->getStatus()); } public function testMatchingAmountSucceeds(): void { $this->enableTestMode(); $payment = $this->makePayment(50000); $this->fireCallback($payment, 50000); $this->assertSame(Payment::STATUS_SUCCESS, $this->reload($payment)->getStatus()); } public function testReplayedGatewayReferenceIsRejected(): void { $this->enableTestMode(); // Unique per run — db_test is shared and not reset between runs. $ref = 'REF-' . bin2hex(random_bytes(8)); $first = $this->makePayment(50000); $this->fireCallbackWithRef($first, $ref, 50000); $this->assertSame(Payment::STATUS_SUCCESS, $this->reload($first)->getStatus()); // Same gateway reference replayed onto a different (same-amount) order. $second = $this->makePayment(50000); $this->fireCallbackWithRef($second, $ref, 50000); $this->assertSame(Payment::STATUS_FAILED, $this->reload($second)->getStatus()); } private function fireCallbackWithRef(Payment $payment, string $refId, int $reportedAmount): void { $this->client->request('POST', '/api/v1/payment/callback/mock?' . http_build_query([ 'order_id' => $payment->getOrderId(), 'mock' => '1', 'ResCode' => '0', 'RefId' => $refId, 'mock_amount' => (string) $reportedAmount, ])); } }