diff --git a/docs/api/payment.md b/docs/api/payment.md index 02b6e5ee..5b80ad0d 100644 --- a/docs/api/payment.md +++ b/docs/api/payment.md @@ -28,7 +28,7 @@ [درگاه بانک / شاپرک] پرداخت کاربر │ (۳) بازگشت به callbackUrl بک‌اند ▼ -[PaymentController::callback] (عمومی، محدود به IP شاپرک مگر تست) +[PaymentController::callback] (عمومی؛ ملت/سپ ریدایرکت مرورگر → بدون IP-check، امنیت با tamper+verify) │ gateway->verify()؛ بررسی مبلغ؛ جلوگیری از replay (reference_id یکتا)؛ ست وضعیت │ post-action: confirm نوبت / فعال‌سازی اشتراک / شارژ کیف‌پول + کمیسیون + پیامک │ (۴) RedirectResponse → frontend_address?payment_uuid=..&status=.. (همان دامنهٔ مبدأ) @@ -271,7 +271,7 @@ Payment gateway callback. Called by the bank after user completes (or cancels) p **نکته IPG ملت:** طبق راهنمای درگاه ملت، `callBackUrl` باید روی **دامنهٔ ثبت‌شدهٔ پذیرنده** باشد و **IP مجاز نیست** (در غیر این صورت کد پاسخ `62` — «مسیر back call در دامنهٔ ثبت‌شده نیست»). بنابراین `APP_BASE_URL` در پروداکشن باید دقیقاً `https://clinic-pro.ir` (دامنهٔ ثبت‌شده نزد ملت/شاپرک) باشد. -**Permission:** `PUBLIC` — called by the gateway, not the user (محدود به IP‌های شبکهٔ شاپرک `isAllowedCallbackIp`؛ در `test_mode` بدون محدودیت IP) +**Permission:** `PUBLIC`. **نکتهٔ مهم:** درگاه‌های **ملت و سپ** نتیجه را با **ریدایرکتِ مرورگرِ کاربر** (POST/GET) برمی‌گردانند، نه server-to-server؛ پس IP دریافتی، IPِ کاربر است و **allowlist شاپرک اعمال نمی‌شود** (برای `gateway ∈ {mellat, sep}` و نیز `test_mode`). در غیر این صورت هر callback واقعی — از جمله «لغو» توسط کاربر — با «دسترسی غیرمجاز» رد می‌شد. امنیت از طریق **چک ضد-دستکاری** (`RefId==gateway_token`، `SaleOrderId==payment.id`) و **verify سمت بانک** در `PaymentManager` تأمین می‌شود. `isAllowedCallbackIp` فقط برای درگاه‌های آیندهٔ server-to-server معنی دارد. ### Path Parameters | Param | Type | Description | diff --git a/src/Payment/Controller/PaymentController.php b/src/Payment/Controller/PaymentController.php index 0bd6370f..9e588833 100644 --- a/src/Payment/Controller/PaymentController.php +++ b/src/Payment/Controller/PaymentController.php @@ -285,9 +285,12 @@ class PaymentController extends BaseController public function callback(string $gateway, Request $request): \Symfony\Component\HttpFoundation\Response { $clientIp = $request->getClientIp() ?? ''; - // در حالت تست یا sandbox ملت، callback از IPی خارج از رنج شاپرک می‌آید؛ IP-check رد می‌شود. - $bypassIp = $this->gateways->isTestMode() - || ($gateway === 'mellat' && $this->gateways->isMellatSandbox()); + // درگاه‌های ملت و سپ نتیجه را با ریدایرکتِ مرورگرِ کاربر (POST/GET) برمی‌گردانند، + // نه server-to-server؛ پس IP دریافتی، IPِ کاربر است و allowlist شاپرک اعمال نمی‌شود + // (در غیر این صورت هر callback واقعی — از جمله «لغو» — رد می‌شد). امنیت از طریق چک + // ضد-دستکاری (RefId/SaleOrderId) و verify سمت بانک در PaymentManager تأمین می‌شود. + $browserRedirectGateways = ['mellat', 'sep']; + $bypassIp = $this->gateways->isTestMode() || in_array($gateway, $browserRedirectGateways, true); if (!$bypassIp && !$this->isAllowedCallbackIp($clientIp)) { return $this->renderPaymentResult('forbidden'); }