125 lines
4.5 KiB
PHP
125 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Lib\Axepta;
|
|
|
|
use App\Lib\Axepta\Objects\AxeptaOrder;
|
|
use GuzzleHttp\Exception\RequestException;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Str;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class AxeptaPaymentService
|
|
{
|
|
private array $axeptaCheckoutConfig;
|
|
|
|
public function __construct(
|
|
private readonly LoggerInterface $logger,
|
|
public readonly AxeptaConfig $axeptaConfig,
|
|
private readonly AxeptaApiGateway $gateway,
|
|
private readonly AxeptaTokenManager $tokenManager,
|
|
private readonly AxeptaSignatureVerifier $signatureVerifier
|
|
) {
|
|
$this->axeptaCheckoutConfig = [
|
|
...[
|
|
'language' => 'fr',
|
|
'amount' => ['currency' => 'EUR'],
|
|
'captureMethod' => ['type' => 'AUTOMATIC'],
|
|
'allowedPaymentMethods' => ['CARD'],
|
|
'paymentMethods' => [
|
|
'card' => ['subType' => ['VISA', 'MASTERCARD']],
|
|
],
|
|
],
|
|
...$axeptaConfig->checkoutConfig,
|
|
];
|
|
}
|
|
|
|
public function getCheckoutUrl(
|
|
#[\SensitiveParameter] AxeptaOrder $order,
|
|
#[\SensitiveParameter] string $returnUrl,
|
|
#[\SensitiveParameter] string $cancelUrl,
|
|
#[\SensitiveParameter] string $webhookUrl
|
|
): string {
|
|
$payload = [
|
|
'transId' => $this->generateTransId(),
|
|
'urls' => ['return' => $returnUrl, 'cancel' => $cancelUrl, 'webhook' => $webhookUrl],
|
|
'amount' => ['value' => $order->amount],
|
|
'order' => ['merchantReference' => $order->reference, 'creationDate' => gmdate('c')],
|
|
'customerInfo' => ['email' => $order->customerEmail],
|
|
];
|
|
|
|
try {
|
|
$token = $this->tokenManager->getToken();
|
|
$response = $this->gateway->post(
|
|
'/api/v2/payments/sessions',
|
|
array_merge_recursive($payload, $this->axeptaCheckoutConfig),
|
|
$token
|
|
);
|
|
|
|
if ($response->getStatusCode() !== 202) {
|
|
throw new \RuntimeException('Session creation failed');
|
|
}
|
|
|
|
$responseBody = json_decode($response->getBody()->getContents());
|
|
|
|
if (! isset($responseBody->_links->redirect->href)) {
|
|
throw new \RuntimeException('Missing redirect URL in response');
|
|
}
|
|
|
|
return $responseBody->_links->redirect->href;
|
|
} catch (RequestException $e) {
|
|
throw new \RuntimeException('Payment session error: ' . $e->getMessage(), 0, $e);
|
|
}
|
|
}
|
|
|
|
public function isValidWebhookAccess(Request $request): bool
|
|
{
|
|
$signatureVersion = $this->getHeaderValue($request, 'x-paygate-signature-version');
|
|
$signature = $this->getHeaderValue($request, 'x-paygate-signature');
|
|
$timestamp = $this->getHeaderValue($request, 'x-paygate-timestamp');
|
|
$rawPayload = $request->getContent(false);
|
|
|
|
$this->logger->debug('Received an IPN call', [
|
|
'request_signature_version' => $signatureVersion,
|
|
'request_signature' => $signature,
|
|
'request_timestamp' => $timestamp,
|
|
'request_payload' => base64_encode($rawPayload),
|
|
]);
|
|
|
|
return $this->signatureVerifier->verify($signatureVersion, $signature, $timestamp, $rawPayload);
|
|
}
|
|
|
|
public function getPaymentStatusById(string $paymentId): ?string
|
|
{
|
|
try {
|
|
$token = $this->tokenManager->getToken();
|
|
$response = $this->gateway->get(sprintf('/api/v2/payments/getByPayId/%s', $paymentId), $token);
|
|
$responseBody = json_decode($response->getBody()->getContents());
|
|
|
|
return $responseBody->responseCode ?? null;
|
|
} catch (RequestException $e) {
|
|
if ($e->hasResponse()) {
|
|
$responseBody = json_decode($e->getResponse()->getBody()->getContents());
|
|
|
|
return $responseBody->responseCode ?? null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private function generateTransId(): string
|
|
{
|
|
return Str::uuid7()->toString();
|
|
}
|
|
|
|
private function getHeaderValue(Request $request, string $headerName): string
|
|
{
|
|
$headerValue = Arr::wrap($request->header($headerName));
|
|
$headerValue = Arr::map($headerValue, fn ($v) => is_scalar($v) ? strval($v) : '');
|
|
|
|
return Str::trim(Arr::join($headerValue, ' '));
|
|
}
|
|
}
|