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')); } }