HttpClient.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. <?php
  2. declare(strict_types=1);
  3. namespace SixShop\Wangdian\Http;
  4. use GuzzleHttp\Client;
  5. use GuzzleHttp\Exception\GuzzleException;
  6. use GuzzleHttp\Exception\RequestException;
  7. use Psr\Http\Client\ClientInterface;
  8. use Psr\Log\LoggerInterface;
  9. use Psr\Log\NullLogger;
  10. use SixShop\Wangdian\Config\Config;
  11. use SixShop\Wangdian\Exception\HttpException;
  12. /**
  13. * HTTP client wrapper for making API requests
  14. */
  15. class HttpClient
  16. {
  17. private readonly ClientInterface $client;
  18. private readonly LoggerInterface $logger;
  19. public function __construct(
  20. private readonly Config $config,
  21. ?ClientInterface $client = null,
  22. ?LoggerInterface $logger = null
  23. ) {
  24. $this->client = $client ?? new Client([
  25. 'timeout' => $this->config->timeout,
  26. 'verify' => !$this->config->isSandbox(), // Disable SSL verification for sandbox
  27. 'headers' => [
  28. 'User-Agent' => 'SixShop-Wangdian-SDK/1.0',
  29. 'Accept' => 'application/json',
  30. ],
  31. ]);
  32. $this->logger = $logger ?? new NullLogger();
  33. }
  34. /**
  35. * Make a POST request to the API
  36. */
  37. public function post(string $endpoint, array $data = []): array
  38. {
  39. $url = $this->config->getEndpoint($endpoint);
  40. try {
  41. $this->logger->info('Making API request', [
  42. 'url' => $url,
  43. 'data_keys' => array_keys($data),
  44. ]);
  45. $response = $this->client->request('POST', $url, [
  46. 'form_params' => $data,
  47. 'headers' => [
  48. 'Content-Type' => 'application/x-www-form-urlencoded',
  49. ],
  50. ]);
  51. $statusCode = $response->getStatusCode();
  52. $body = $response->getBody()->getContents();
  53. $this->logger->info('API response received', [
  54. 'status_code' => $statusCode,
  55. 'response_length' => strlen($body),
  56. ]);
  57. if ($statusCode !== 200) {
  58. throw new HttpException(
  59. message: "HTTP request failed with status code: {$statusCode}",
  60. httpStatusCode: $statusCode,
  61. context: ['response_body' => $body]
  62. );
  63. }
  64. return $this->parseResponse($body);
  65. } catch (RequestException $e) {
  66. $this->logger->error('HTTP request failed', [
  67. 'url' => $url,
  68. 'error' => $e->getMessage(),
  69. ]);
  70. throw new HttpException(
  71. message: 'HTTP request failed: ' . $e->getMessage(),
  72. previous: $e,
  73. httpStatusCode: $e->getResponse()?->getStatusCode(),
  74. context: ['url' => $url, 'data' => $data]
  75. );
  76. } catch (GuzzleException $e) {
  77. $this->logger->error('Guzzle exception', [
  78. 'url' => $url,
  79. 'error' => $e->getMessage(),
  80. ]);
  81. throw new HttpException(
  82. message: 'HTTP client error: ' . $e->getMessage(),
  83. previous: $e,
  84. context: ['url' => $url, 'data' => $data]
  85. );
  86. }
  87. }
  88. /**
  89. * Parse JSON response
  90. */
  91. private function parseResponse(string $body): array
  92. {
  93. if (empty($body)) {
  94. throw new HttpException('Empty response body received');
  95. }
  96. $decoded = json_decode($body, true);
  97. if (json_last_error() !== JSON_ERROR_NONE) {
  98. $this->logger->error('Failed to parse JSON response', [
  99. 'json_error' => json_last_error_msg(),
  100. 'response_body' => $this->config->debug ? $body : '[hidden]',
  101. ]);
  102. throw new HttpException(
  103. message: 'Failed to parse JSON response: ' . json_last_error_msg(),
  104. context: ['response_body' => $body]
  105. );
  106. }
  107. return $decoded;
  108. }
  109. }