feat(payment): unify payment flow with new pure redirect entry and update related endpoints

This commit is contained in:
hamed
2026-07-02 17:08:50 +03:30
parent c247ac2c80
commit 7f5c65129c
17 changed files with 720 additions and 82 deletions
+13 -31
View File
@@ -4,12 +4,9 @@ namespace App\Sms\Controller;
use App\Auth\Entity\User;
use App\Clinic\Repository\ClinicRepository;
use App\Config\Repository\SiteConfigRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Payment\Entity\Payment;
use App\Payment\Gateway\MellatGateway;
use App\Payment\Gateway\MockGateway;
use App\Payment\Gateway\SepGateway;
use App\Payment\Gateway\GatewayFactory;
use App\Payment\Repository\PaymentRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
@@ -37,15 +34,19 @@ class SmsWalletController extends BaseController
private readonly SmsWalletTransactionRepository $txRepo,
private readonly SmsSettingsRepository $settingsRepo,
private readonly PaymentRepository $paymentRepo,
private readonly SiteConfigRepository $configRepo,
private readonly MellatGateway $mellat,
private readonly SepGateway $sep,
private readonly MockGateway $mock,
private readonly GatewayFactory $gateways,
private readonly DoctorRepository $doctorRepo,
private readonly ClinicRepository $clinicRepo,
private readonly \App\Config\Repository\SiteConfigRepository $configRepo,
private readonly string $appBaseUrl,
) {}
/** قیمت هر پیامک از تنظیمات سایت؛ با fallback به مقدار پیش‌فرض. */
private function smsPriceRials(): int
{
return max(1, (int) ($this->configRepo->get('sms_price_rials') ?: self::SMS_PRICE_RIALS));
}
#[Route('/api/v1/sms/wallet/balance', methods: ['GET'])]
public function balance(#[CurrentUser] User $user): JsonResponse
{
@@ -55,7 +56,7 @@ class SmsWalletController extends BaseController
}
$balanceRials = $this->walletService->getBalance($entityType, $entityId);
$smsPriceRials = self::SMS_PRICE_RIALS;
$smsPriceRials = $this->smsPriceRials();
$estimatedSms = (int) floor($balanceRials / $smsPriceRials);
return $this->success([
@@ -81,17 +82,8 @@ class SmsWalletController extends BaseController
return $this->error(ErrorCodes::ERR_PAYMENT_002, 'مبلغ شارژ نامعتبر است', 422);
}
if ($this->configRepo->get('payment_test_mode') === '1') {
$gateway = $this->mock;
} else {
$gateway = match ($gatewayName) {
'mellat' => $this->mellat,
'sep' => $this->sep,
default => null,
};
}
if ($gateway === null) {
// فقط اعتبارسنجی درگاه؛ ارتباط با بانک در flow واحد (GET /payment/pay) انجام می‌شود.
if ($this->gateways->resolve($gatewayName) === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'درگاه پرداخت نامعتبر است', 422);
}
@@ -100,19 +92,9 @@ class SmsWalletController extends BaseController
$payment->setMetadata(['entity_type' => $entityType, 'entity_id' => $entityId]);
$this->paymentRepo->save($payment);
$callbackUrl = $this->appBaseUrl . '/api/v1/payment/callback/' . $gatewayName . '?order_id=' . $payment->getOrderId();
$result = $gateway->initiate($amountRials, $payment->getOrderId(), $callbackUrl);
if (!$result->success) {
return $this->error(ErrorCodes::ERR_PAYMENT_001, $result->errorMessage ?? 'درگاه در دسترس نیست', 503);
}
$payment->setGatewayToken($result->token);
$this->paymentRepo->save($payment);
return $this->success([
'payment_uuid' => $payment->getUuid(),
'redirect_url' => $result->redirectUrl,
'pay_url' => $this->appBaseUrl . '/api/v1/payment/pay/' . $payment->getOrderId(),
'order_id' => $payment->getOrderId(),
]);
}