feat(payment): add payment detail endpoint and update payment model with order_id and patient_name
feat(appointment): enhance appointment detail page with time formatting and additional info fix(payment): update payment query to fetch from the correct endpoint and adjust response structure docs(api): add search parameter to payments API documentation and detail response structure test(payment): add unit test for MellatGateway to verify null credentials handling
This commit is contained in:
@@ -874,6 +874,54 @@ class AdminApiController extends BaseController
|
||||
return $this->paginated($items, (int) $total, $page, $limit);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/api/v1/admin/payments/{uuid}',
|
||||
summary: 'Payment detail by UUID',
|
||||
security: [['bearerAuth' => []]],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(response: 200, description: 'Payment detail'),
|
||||
new OA\Response(response: 404, description: 'Payment not found'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/admin/payments/{uuid}', methods: ['GET'])]
|
||||
public function paymentDetail(string $uuid): JsonResponse
|
||||
{
|
||||
$rows = $this->em->createQueryBuilder()
|
||||
->select(
|
||||
'p.uuid, p.orderId, p.amountRials, p.status, p.gateway, p.type, p.referenceId, p.createdAt, p.updatedAt',
|
||||
'u.mobileNumber as patient_mobile, u.realName as patient_name',
|
||||
'a.uuid as appointment_uuid',
|
||||
)
|
||||
->from(Payment::class, 'p')
|
||||
->join('p.user', 'u')
|
||||
->leftJoin('p.appointment', 'a')
|
||||
->where('p.uuid = :uuid')->setParameter('uuid', $uuid)
|
||||
->getQuery()->getArrayResult();
|
||||
|
||||
if (empty($rows)) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پرداخت یافت نشد', 404);
|
||||
}
|
||||
|
||||
$p = $rows[0];
|
||||
return $this->success([
|
||||
'uuid' => $p['uuid'],
|
||||
'order_id' => $p['orderId'],
|
||||
'amount' => (int) $p['amountRials'],
|
||||
'status' => $p['status'],
|
||||
'gateway' => $p['gateway'],
|
||||
'type' => $p['type'],
|
||||
'ref_id' => $p['referenceId'],
|
||||
'patient_mobile' => $p['patient_mobile'],
|
||||
'patient_name' => $p['patient_name'],
|
||||
'appointment_uuid' => $p['appointment_uuid'],
|
||||
'paid_at' => $p['status'] === 'success' ? date('c', (int) $p['updatedAt']) : null,
|
||||
'created_at' => date('c', (int) $p['createdAt']),
|
||||
]);
|
||||
}
|
||||
|
||||
// ── Representations ───────────────────────────────────────────────────────
|
||||
|
||||
#[OA\Get(
|
||||
|
||||
@@ -14,12 +14,15 @@ class MellatGateway implements PaymentGatewayInterface
|
||||
private readonly HttpClientInterface $httpClient,
|
||||
private readonly SiteConfigRepository $configRepo,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly string $terminalId = '',
|
||||
private readonly string $username = '',
|
||||
private readonly string $password = '',
|
||||
private readonly ?string $terminalId = '',
|
||||
private readonly ?string $username = '',
|
||||
private readonly ?string $password = '',
|
||||
) {}
|
||||
|
||||
public function getName(): string { return 'mellat'; }
|
||||
public function getName(): string
|
||||
{
|
||||
return 'mellat';
|
||||
}
|
||||
|
||||
public function isConfigured(): bool
|
||||
{
|
||||
@@ -28,16 +31,18 @@ class MellatGateway implements PaymentGatewayInterface
|
||||
&& $this->cfg('mellat_password', $this->password) !== '';
|
||||
}
|
||||
|
||||
private function cfg(string $key, string $envFallback): string
|
||||
private function cfg(string $key, ?string $envFallback): string
|
||||
{
|
||||
return $this->configRepo->get($key) ?: $envFallback;
|
||||
return (string) ($this->configRepo->get($key) ?: $envFallback ?? '');
|
||||
}
|
||||
|
||||
public function initiate(int $amountRials, string $orderId, string $callbackUrl): PaymentInitResult
|
||||
{
|
||||
try {
|
||||
$response = $this->httpClient->request('POST',
|
||||
'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl', [
|
||||
$response = $this->httpClient->request(
|
||||
'POST',
|
||||
'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl',
|
||||
[
|
||||
'body' => $this->buildRequestPayload($amountRials, $orderId, $callbackUrl),
|
||||
'headers' => ['Content-Type' => 'text/xml; charset=utf-8'],
|
||||
'timeout' => 10,
|
||||
@@ -82,8 +87,10 @@ class MellatGateway implements PaymentGatewayInterface
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->httpClient->request('POST',
|
||||
'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl', [
|
||||
$response = $this->httpClient->request(
|
||||
'POST',
|
||||
'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl',
|
||||
[
|
||||
'body' => $this->buildVerifyPayload($refId),
|
||||
'headers' => ['Content-Type' => 'text/xml; charset=utf-8'],
|
||||
'timeout' => 10,
|
||||
@@ -164,6 +171,12 @@ XML;
|
||||
return trim($parts[1] ?? '');
|
||||
}
|
||||
|
||||
private function date(): string { return date('Ymd'); }
|
||||
private function time(): string { return date('His'); }
|
||||
private function date(): string
|
||||
{
|
||||
return date('Ymd');
|
||||
}
|
||||
private function time(): string
|
||||
{
|
||||
return date('His');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,19 +15,22 @@ class SepGateway implements PaymentGatewayInterface
|
||||
private readonly HttpClientInterface $httpClient,
|
||||
private readonly SiteConfigRepository $configRepo,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly string $terminalId = '',
|
||||
private readonly ?string $terminalId = '',
|
||||
) {}
|
||||
|
||||
public function getName(): string { return 'sep'; }
|
||||
public function getName(): string
|
||||
{
|
||||
return 'sep';
|
||||
}
|
||||
|
||||
public function isConfigured(): bool
|
||||
{
|
||||
return $this->cfg('sep_terminal_id', $this->terminalId) !== '';
|
||||
}
|
||||
|
||||
private function cfg(string $key, string $envFallback): string
|
||||
private function cfg(string $key, ?string $envFallback): string
|
||||
{
|
||||
return $this->configRepo->get($key) ?: $envFallback;
|
||||
return (string) ($this->configRepo->get($key) ?: $envFallback ?? '');
|
||||
}
|
||||
|
||||
public function initiate(int $amountRials, string $orderId, string $callbackUrl): PaymentInitResult
|
||||
|
||||
Reference in New Issue
Block a user