feat(payment): canceled status, manageable origin allowlist, CORS subdomains
Unify and harden the payment flow (same API for the main site and all consumer sites; per-client difference is only frontend_address). - Payment gains STATUS_CANCELED. Gateways distinguish user-cancel from failure (Mellat ResCode=17, SEP CanceledByUser, mock cancel=1) via a new PaymentVerifyResult::canceled flag; callback sets canceled vs failed and skips the circuit-breaker on cancel. - Expiry job now cancels the pending payment when a booking lapses (AppointmentExpiryService + PaymentRepository::findPendingByAppointment). - frontend_address allowlist is read from the payment_allowed_frontend_hosts site setting (manageable via PATCH /api/v1/admin/settings), falling back to the ALLOWED_FRONTEND_HOSTS env var — so a new consumer site needs no code change. - .env: broaden CORS_ALLOW_ORIGIN to city subdomains (*.localhost / *.clinic-pro.ddev.site) and add yazd-nobat.localhost to ALLOWED_FRONTEND_HOSTS. - Update docs/api/payment.md and docs/api/admin.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -259,9 +259,12 @@ class PaymentController extends BaseController
|
||||
$result = $gw?->verify($callbackData) ?? null;
|
||||
|
||||
if ($result === null || !$result->success) {
|
||||
$payment->setStatus(Payment::STATUS_FAILED);
|
||||
$canceled = $result?->canceled ?? false;
|
||||
$payment->setStatus($canceled ? Payment::STATUS_CANCELED : Payment::STATUS_FAILED);
|
||||
$this->paymentRepo->save($payment);
|
||||
$this->circuitBreaker->recordFailure($gateway);
|
||||
if (!$canceled) {
|
||||
$this->circuitBreaker->recordFailure($gateway);
|
||||
}
|
||||
|
||||
return $this->redirectToFrontend($payment, false);
|
||||
}
|
||||
@@ -566,9 +569,17 @@ class PaymentController extends BaseController
|
||||
};
|
||||
}
|
||||
|
||||
/** @return string[] allowed frontend hosts — from SiteConfig, falling back to env. */
|
||||
private function allowedHosts(): array
|
||||
{
|
||||
$fromConfig = (string) ($this->configRepo->get('payment_allowed_frontend_hosts') ?? '');
|
||||
$raw = $fromConfig !== '' ? $fromConfig : $this->allowedFrontendHosts;
|
||||
return array_filter(array_map('trim', explode(',', $raw)));
|
||||
}
|
||||
|
||||
private function isAllowedFrontend(string $url): bool
|
||||
{
|
||||
$hosts = array_filter(array_map('trim', explode(',', $this->allowedFrontendHosts)));
|
||||
$hosts = $this->allowedHosts();
|
||||
if (empty($hosts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user