123 lines
4.8 KiB
PHP
123 lines
4.8 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\Oceanpayment\Model\Gateway;
|
|
|
|
use Esun\Oceanpayment\Model\Config;
|
|
use Esun\Oceanpayment\Model\Signature;
|
|
use Magento\Framework\Exception\LocalizedException;
|
|
use Magento\Framework\HTTP\Client\CurlFactory;
|
|
use Magento\Sales\Api\OrderRepositoryInterface;
|
|
use Magento\Sales\Model\Order;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class Client
|
|
{
|
|
public function __construct(
|
|
private readonly Config $config,
|
|
private readonly Signature $signature,
|
|
private readonly RequestBuilder $requestBuilder,
|
|
private readonly ResponseParser $responseParser,
|
|
private readonly CurlFactory $curlFactory,
|
|
private readonly OrderRepositoryInterface $orderRepository,
|
|
private readonly LoggerInterface $logger
|
|
) {
|
|
}
|
|
|
|
public function createPaymentUrl(Order $order): string
|
|
{
|
|
$storeId = (int)$order->getStoreId();
|
|
$method = (string)$order->getPayment()->getMethod();
|
|
if (!$this->config->isConfigured($storeId, $method)) {
|
|
throw new LocalizedException(__('Oceanpayment is not fully configured.'));
|
|
}
|
|
|
|
$request = $this->requestBuilder->build($order);
|
|
$curl = $this->curlFactory->create();
|
|
$curl->setTimeout(30);
|
|
$curl->addHeader('Accept', 'application/xml');
|
|
$curl->addHeader('Content-Type', 'application/x-www-form-urlencoded');
|
|
$curl->setOption(CURLOPT_FOLLOWLOCATION, false);
|
|
|
|
if ($this->config->isDebug($storeId)) {
|
|
$this->logger->debug('Oceanpayment create request', [
|
|
'order_number' => $request['order_number'],
|
|
'endpoint' => $this->config->getEndpoint($storeId, $method),
|
|
'sandbox' => $this->config->isSandbox($storeId, $method),
|
|
]);
|
|
}
|
|
|
|
try {
|
|
$curl->post($this->config->getEndpoint($storeId, $method), $request);
|
|
} catch (\Throwable $exception) {
|
|
$this->logger->error('Oceanpayment gateway request failed', [
|
|
'order_number' => $order->getIncrementId(),
|
|
'exception' => $exception,
|
|
]);
|
|
throw new LocalizedException(__('Unable to connect to Oceanpayment.'), $exception);
|
|
}
|
|
|
|
$status = $curl->getStatus();
|
|
if ($status < 200 || $status >= 300) {
|
|
throw new LocalizedException(__('Oceanpayment returned HTTP status %1.', $status));
|
|
}
|
|
|
|
$response = $this->responseParser->parse((string)$curl->getBody());
|
|
$this->validateResponse($order, $response);
|
|
|
|
$payment = $order->getPayment();
|
|
$payment->setAdditionalInformation('qianhai_payment_id', $response['payment_id']);
|
|
$payment->setAdditionalInformation('qianhai_pay_url', $response['pay_url']);
|
|
$payment->setAdditionalInformation('qianhai_create_result', $response['pay_details']);
|
|
$this->orderRepository->save($order);
|
|
|
|
return $response['pay_url'];
|
|
}
|
|
|
|
private function validateResponse(Order $order, array $response): void
|
|
{
|
|
$storeId = (int)$order->getStoreId();
|
|
$method = (string)$order->getPayment()->getMethod();
|
|
$account = $this->config->getSecret('account', $storeId, $method);
|
|
$terminal = $this->config->getSecret('terminal', $storeId, $method);
|
|
|
|
if (!hash_equals($account, $response['account'])
|
|
|| !hash_equals($terminal, $response['terminal'])
|
|
|| !hash_equals((string)$order->getIncrementId(), $response['order_number'])
|
|
|| !hash_equals(strtoupper((string)$order->getOrderCurrencyCode()), strtoupper($response['order_currency']))
|
|
|| !$this->amountsEqual((float)$order->getGrandTotal(), $response['order_amount'])
|
|
) {
|
|
throw new LocalizedException(__('Oceanpayment response did not match the Magento order.'));
|
|
}
|
|
|
|
if (!$this->signature->verifyCreateResponse(
|
|
$response,
|
|
$this->config->getSecret('secure_code', $storeId, $method)
|
|
)) {
|
|
throw new LocalizedException(__('Oceanpayment response signature verification failed.'));
|
|
}
|
|
|
|
if ($response['pay_results'] !== '1') {
|
|
throw new LocalizedException(__('Oceanpayment rejected the order: %1', $response['pay_details']));
|
|
}
|
|
|
|
if (!$this->isAllowedPaymentUrl($response['pay_url'])) {
|
|
throw new LocalizedException(__('Oceanpayment returned an invalid payment URL.'));
|
|
}
|
|
}
|
|
|
|
private function amountsEqual(float $expected, string $actual): bool
|
|
{
|
|
return abs($expected - (float)$actual) < 0.005;
|
|
}
|
|
|
|
public function isAllowedPaymentUrl(string $url): bool
|
|
{
|
|
$parts = parse_url($url);
|
|
$host = strtolower((string)($parts['host'] ?? ''));
|
|
|
|
return ($parts['scheme'] ?? '') === 'https'
|
|
&& ($host === 'oceanpayment.com' || str_ends_with($host, '.oceanpayment.com'));
|
|
}
|
|
}
|