- Implemented `refund` and `reverse` methods in `PaymentGatewayInterface`. - Added `PaymentRefundResult` class to handle refund operation results. - Enhanced `MockGateway` and `SepGateway` to support refund and reversal operations. - Updated `PaymentManager` to include `refundPayment` and `reversePayment` methods for handling refunds and reversals in transactions. - Modified `ClinicSubscriptionRepository` and `SubscriptionService` to manage subscriptions during refunds. - Added admin API endpoints for processing refunds and reversals. - Updated security headers to allow form actions to the sandbox environment. - Documented the new refund and reversal features in the API documentation.
98 lines
3.4 KiB
PHP
98 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Payment\Gateway;
|
|
|
|
use App\Config\Repository\SiteConfigRepository;
|
|
|
|
/**
|
|
* انتخاب/ساخت درگاه پرداخت (Factory + Strategy).
|
|
*
|
|
* تمام منطقِ «کدام درگاه، فعال است یا نه، حالت تست» اینجا متمرکز است تا کنترلر
|
|
* فقط Orchestration کند. افزودن درگاه جدید = ثبت آن بهعنوان سرویس و اضافهکردن
|
|
* یک case در map + یک برچسب، بدون تغییر کنترلر.
|
|
*/
|
|
class GatewayFactory
|
|
{
|
|
/** @var array<string, PaymentGatewayInterface> */
|
|
private array $gateways;
|
|
|
|
/** @var array<string, string> برچسب فارسی هر درگاه */
|
|
private const LABELS = [
|
|
'mellat' => 'بانک ملت',
|
|
'sep' => 'سپ (سامان کیش)',
|
|
];
|
|
|
|
public function __construct(
|
|
MellatGateway $mellat,
|
|
SepGateway $sep,
|
|
private readonly MockGateway $mock,
|
|
private readonly SiteConfigRepository $configRepo,
|
|
) {
|
|
$this->gateways = [
|
|
$mellat->getName() => $mellat,
|
|
$sep->getName() => $sep,
|
|
];
|
|
}
|
|
|
|
public function isTestMode(): bool
|
|
{
|
|
return $this->configRepo->get('payment_test_mode') === '1';
|
|
}
|
|
|
|
/** حالت sandbox ملت (banktest.ir) — اتصال واقعی، جدا از test_mode/Mock. موقت. */
|
|
public function isMellatSandbox(): bool
|
|
{
|
|
return $this->configRepo->get('mellat_sandbox') === '1';
|
|
}
|
|
|
|
/**
|
|
* درگاهِ قابلاستفاده برای این نام؛ در حالت تست همیشه Mock، در غیر اینصورت
|
|
* درگاه واقعی در صورت فعال بودن. null یعنی نامعتبر/غیرفعال.
|
|
*/
|
|
public function resolve(string $name): ?PaymentGatewayInterface
|
|
{
|
|
// sandbox ملت: اتصال واقعی به banktest، نه Mock.
|
|
if ($name === 'mellat' && $this->isMellatSandbox()) {
|
|
return $this->gateways['mellat'] ?? null;
|
|
}
|
|
if ($this->isTestMode()) {
|
|
return $this->mock;
|
|
}
|
|
if (!$this->isEnabled($name)) {
|
|
return null;
|
|
}
|
|
return $this->gateways[$name] ?? null;
|
|
}
|
|
|
|
/** آیا درگاه در تنظیمات فعال است؟ کلید تنظیمنشده = فعال (سازگاری با نصبهای قبلی). */
|
|
public function isEnabled(string $name): bool
|
|
{
|
|
$v = $this->configRepo->get($name . '_enabled');
|
|
return $v === null || $v === '1';
|
|
}
|
|
|
|
/**
|
|
* درگاههای قابلانتخاب برای نمایش به کاربر: در حالت تست فقط درگاه آزمایشی،
|
|
* در غیر اینصورت هر درگاهی که اعتبارنامهاش ست شده و فعال است.
|
|
*
|
|
* @return array<int, array{name: string, label: string}>
|
|
*/
|
|
public function activeGateways(): array
|
|
{
|
|
if ($this->isMellatSandbox()) {
|
|
return [['name' => 'mellat', 'label' => 'بانک ملت (Sandbox)']];
|
|
}
|
|
if ($this->isTestMode()) {
|
|
return [['name' => 'mellat', 'label' => 'بانک ملت (آزمایشی)']];
|
|
}
|
|
|
|
$out = [];
|
|
foreach ($this->gateways as $name => $gateway) {
|
|
if ($gateway->isConfigured() && $this->isEnabled($name)) {
|
|
$out[] = ['name' => $name, 'label' => self::LABELS[$name] ?? $name];
|
|
}
|
|
}
|
|
return $out;
|
|
}
|
|
}
|