Add Paynet (Moldova) payment gateway

PaymentSettings:
- New "🇲🇩 Paynet" section: enabled toggle, mode (test/live), merchant_code,
  service_id, user, password, secret (HMAC), webhook URL hint
- Webhook URL: https://service.mir.md/payments/paynet/webhook

PaymentController:
- startPaynet() — builds Paynet redirect (stub mode prints flow)
- paynetWebhook() — verifies HMAC-SHA256 signature canonical
  Merchant_Code|Order_ID|Amount|Status, marks subscription paid on Status=OK,
  matches by invoice_number = Order_ID
- availableMethods() includes paynet

Tenant /billing:
- 4th payment button "🇲🇩 Paynet" — visible only when configured.
  Description: Card MAIB / MICB / Victoriabank, MD Cash, e-money

Routes:
- POST /payments/paynet/webhook (CSRF excluded)
This commit is contained in:
2026-05-08 06:20:11 +00:00
parent 827bf12d89
commit 93a69dd826
4 changed files with 141 additions and 3 deletions
+57 -1
View File
@@ -27,13 +27,14 @@ class PaymentSettings extends Page
public function mount(): void
{
$s = PlatformSetting::many([
'payments.stripe', 'payments.paypal', 'payments.bank',
'payments.stripe', 'payments.paypal', 'payments.paynet', 'payments.bank',
'payments.platform_currency', 'payments.invoice_prefix',
'payments.terms', 'payments.company_legal',
]);
$stripe = $s['payments.stripe'] ?? [];
$paypal = $s['payments.paypal'] ?? [];
$paynet = $s['payments.paynet'] ?? [];
$bank = $s['payments.bank'] ?? [];
$legal = $s['payments.company_legal'] ?? [];
@@ -59,6 +60,14 @@ class PaymentSettings extends Page
'paypal_client_id' => $paypal['client_id'] ?? null,
'paypal_secret' => $paypal['secret'] ?? null,
'paynet_enabled' => $paynet['enabled'] ?? false,
'paynet_mode' => $paynet['mode'] ?? 'test',
'paynet_merchant_code' => $paynet['merchant_code'] ?? null,
'paynet_service_id' => $paynet['service_id'] ?? null,
'paynet_user' => $paynet['user'] ?? null,
'paynet_password' => $paynet['password'] ?? null,
'paynet_secret' => $paynet['secret'] ?? null,
'bank_enabled' => $bank['enabled'] ?? true,
'bank_iban' => $bank['iban'] ?? null,
'bank_bic' => $bank['bic'] ?? null,
@@ -141,6 +150,43 @@ class PaymentSettings extends Page
->label('Secret')->password()->revealable()->placeholder('EJk...')
->visible(fn (Get $get) => $get('paypal_enabled')),
]),
Schemas\Components\Section::make('🇲🇩 Paynet (Moldova)')
->description('Procesator de plăți Moldova. Acceptă carduri MAIB, MICB, Victoriabank, OTP, MD Cash, Bitcoin, e-money. Comision negociabil ~1.8-2.5%.')
->columns(3)
->collapsible()
->collapsed(fn (Get $get) => ! $get('paynet_enabled'))
->schema([
Forms\Components\Toggle::make('paynet_enabled')->label('Activează Paynet')->default(false)->live(),
Forms\Components\Select::make('paynet_mode')
->label('Mod')
->options(['test' => '🧪 Test', 'live' => '🟢 Live'])
->default('test')
->visible(fn (Get $get) => $get('paynet_enabled')),
Forms\Components\Placeholder::make('paynet_webhook_info')
->label('Webhook (Notify URL)')
->content('https://service.mir.md/payments/paynet/webhook')
->visible(fn (Get $get) => $get('paynet_enabled')),
Forms\Components\TextInput::make('paynet_merchant_code')
->label('Merchant Code')
->placeholder('AUTOCRM_001')
->visible(fn (Get $get) => $get('paynet_enabled')),
Forms\Components\TextInput::make('paynet_service_id')
->label('Service ID')
->numeric()
->placeholder('12345')
->visible(fn (Get $get) => $get('paynet_enabled')),
Forms\Components\TextInput::make('paynet_user')
->label('User API')
->visible(fn (Get $get) => $get('paynet_enabled')),
Forms\Components\TextInput::make('paynet_password')
->label('Parolă API')->password()->revealable()
->visible(fn (Get $get) => $get('paynet_enabled')),
Forms\Components\TextInput::make('paynet_secret')
->label('Secret semnătură')->password()->revealable()
->columnSpanFull()
->helperText('Cheia HMAC pentru semnarea cererilor + verificarea webhook-urilor de la Paynet.')
->visible(fn (Get $get) => $get('paynet_enabled')),
]),
Schemas\Components\Section::make('🏦 Transfer bancar (manual)')
->description('Datele apar pe facturi și pe pagina de plată a tenant-ului. Confirmarea o faci manual.')
->columns(2)
@@ -198,6 +244,16 @@ class PaymentSettings extends Page
'secret' => $data['paypal_secret'] ?? null,
]);
PlatformSetting::put('payments.paynet', [
'enabled' => (bool) ($data['paynet_enabled'] ?? false),
'mode' => $data['paynet_mode'] ?? 'test',
'merchant_code' => $data['paynet_merchant_code'] ?? null,
'service_id' => $data['paynet_service_id'] ?? null,
'user' => $data['paynet_user'] ?? null,
'password' => $data['paynet_password'] ?? null,
'secret' => $data['paynet_secret'] ?? null,
]);
PlatformSetting::put('payments.bank', [
'enabled' => (bool) ($data['bank_enabled'] ?? false),
'beneficiary' => $data['bank_beneficiary'] ?? null,