Tapd.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <?php
  2. namespace Overtrue\Socialite\Providers;
  3. use GuzzleHttp\Psr7\Stream;
  4. use Overtrue\Socialite\Exceptions\AuthorizeFailedException;
  5. use Overtrue\Socialite\Exceptions\BadRequestException;
  6. use Overtrue\Socialite\User;
  7. /**
  8. * @see https://www.tapd.cn/help/show#1120003271001000708
  9. */
  10. class Tapd extends Base
  11. {
  12. public const NAME = 'tapd';
  13. protected string $baseUrl = 'https://api.tapd.cn';
  14. /**
  15. * @return string
  16. */
  17. protected function getAuthUrl(): string
  18. {
  19. return $this->buildAuthUrlFromBase($this->baseUrl . '/quickstart/testauth');
  20. }
  21. /**
  22. * @return string
  23. */
  24. protected function getTokenUrl(): string
  25. {
  26. return $this->baseUrl . '/tokens/request_token';
  27. }
  28. /**
  29. * @return string
  30. */
  31. protected function getRefreshTokenUrl(): string
  32. {
  33. return $this->baseUrl . '/tokens/refresh_token';
  34. }
  35. /**
  36. * @param string $code
  37. *
  38. * @return array
  39. * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
  40. * @throws \GuzzleHttp\Exception\GuzzleException
  41. */
  42. public function tokenFromCode($code): array
  43. {
  44. $response = $this->getHttpClient()->post($this->getTokenUrl(), [
  45. 'headers' => [
  46. 'Accept' => 'application/json',
  47. 'Authorization' => 'Basic ' . \base64_encode(\sprintf('%s:%s', $this->getClientId(), $this->getClientSecret()))
  48. ],
  49. 'form_params' => $this->getTokenFields($code),
  50. ]);
  51. return $this->normalizeAccessTokenResponse($response->getBody()->getContents());
  52. }
  53. /**
  54. * @param string $code
  55. *
  56. * @return array
  57. */
  58. protected function getTokenFields(string $code): array
  59. {
  60. return [
  61. 'grant_type' => 'authorization_code',
  62. 'redirect_uri' => $this->redirectUrl,
  63. 'code' => $code,
  64. ];
  65. }
  66. /**
  67. * @param $refreshToken
  68. *
  69. * @return array
  70. */
  71. protected function getRefreshTokenFields($refreshToken): array
  72. {
  73. return [
  74. 'grant_type' => 'refresh_token',
  75. 'redirect_uri' => $this->redirectUrl,
  76. 'refresh_token' => $refreshToken,
  77. ];
  78. }
  79. /**
  80. * @param string $refreshToken
  81. *
  82. * @return array
  83. *
  84. * @throws \GuzzleHttp\Exception\GuzzleException
  85. * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
  86. */
  87. public function tokenFromRefreshToken(string $refreshToken): array
  88. {
  89. $response = $this->getHttpClient()->post($this->getRefreshTokenUrl(), [
  90. 'headers' => [
  91. 'Accept' => 'application/json',
  92. 'Authorization' => 'Basic ' . \base64_encode(\sprintf('%s:%s', $this->getClientId(), $this->getClientSecret()))
  93. ],
  94. 'form_params' => $this->getRefreshTokenFields($refreshToken),
  95. ]);
  96. return $this->normalizeAccessTokenResponse($response->getBody()->getContents());
  97. }
  98. /**
  99. * @param string $token
  100. *
  101. * @return array
  102. *
  103. * @throws \GuzzleHttp\Exception\GuzzleException
  104. */
  105. protected function getUserByToken(string $token): array
  106. {
  107. $response = $this->getHttpClient()->get($this->baseUrl . '/users/info', [
  108. 'headers' => [
  109. 'Accept' => 'application/json',
  110. 'Authorization' => 'Bearer ' . $token,
  111. ],
  112. ]);
  113. return \json_decode($response->getBody(), true) ?? [];
  114. }
  115. /**
  116. * @param array $user
  117. *
  118. * @return \Overtrue\Socialite\User
  119. *
  120. * @throws \Overtrue\Socialite\Exceptions\BadRequestException
  121. */
  122. protected function mapUserToObject(array $user): User
  123. {
  124. if (!isset($user['status']) && $user['status'] != 1) {
  125. throw new BadRequestException("用户信息获取失败");
  126. }
  127. return new User([
  128. 'id' => $user['data']['id'] ?? null,
  129. 'nickname' => $user['data']['nick'] ?? null,
  130. 'name' => $user['data']['name'] ?? null,
  131. 'email' => $user['data']['email'] ?? null,
  132. 'avatar' => $user['data']['avatar'] ?? null,
  133. ]);
  134. }
  135. /**
  136. * @param array|string $response
  137. *
  138. * @return mixed
  139. * @return array
  140. * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
  141. *
  142. */
  143. protected function normalizeAccessTokenResponse($response): array
  144. {
  145. if ($response instanceof Stream) {
  146. $response->rewind();
  147. $response = $response->getContents();
  148. }
  149. if (\is_string($response)) {
  150. $response = json_decode($response, true) ?? [];
  151. }
  152. if (!\is_array($response)) {
  153. throw new AuthorizeFailedException('Invalid token response', [$response]);
  154. }
  155. if (empty($response['data'][$this->accessTokenKey])) {
  156. throw new AuthorizeFailedException('Authorize Failed: ' . json_encode($response, JSON_UNESCAPED_UNICODE), $response);
  157. }
  158. return $response + [
  159. 'access_token' => $response['data'][$this->accessTokenKey],
  160. 'refresh_token' => $response['data'][$this->refreshTokenKey] ?? null,
  161. 'expires_in' => \intval($response['data'][$this->expiresInKey] ?? 0),
  162. ];
  163. }
  164. }