ByteDance.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. <?php
  2. namespace App\Helper;
  3. use App\Helper\UniPlatform\BaseAPI;
  4. use App\Helper\UniPlatform\BaseUniPlatform;
  5. use GuzzleHttp\Client;
  6. use GuzzleHttp\Exception\GuzzleException;
  7. /**
  8. * Class ByteDance
  9. *
  10. * @package App\Helper
  11. * @property-read string $appId
  12. * @property-read string $slat
  13. * @property-read string $secret
  14. * @property-read string $token
  15. * @property-read string $accessTokenFile
  16. * @property-read string $accessToken
  17. * @property-read string $noticeUrl
  18. * @property-read string $validTimestamp
  19. */
  20. class ByteDance extends BaseUniPlatform
  21. {
  22. public function __construct(BaseAPI $api)
  23. {
  24. $this->API = $api;
  25. }
  26. /**
  27. * 获取 access token
  28. * @throws \Exception
  29. */
  30. protected function getAccessToken() : string
  31. {
  32. $res = $this->post($this->API::ACCESS_TOKEN, [
  33. 'grant_type' => 'client_credential',
  34. 'appid' => $this->appId,
  35. 'secret' => $this->secret,
  36. ]);
  37. if (!empty($res['err_no'])) {
  38. throw new \Exception('获取access token 错误');
  39. }
  40. file_put_contents($this->accessTokenFile, json_encode([
  41. 'access_token' => $res['access_token'],
  42. 'expires_at' => $res['expiresAt']
  43. ]));
  44. return $res['access_token'];
  45. }
  46. /**
  47. * @param $outOrderNo
  48. * @param $totalAmount
  49. * @param $openId
  50. * @return array|mixed
  51. * @throws \Exception
  52. */
  53. public function createOrder($outOrderNo, $totalAmount, $openId): array
  54. {
  55. $data = [
  56. 'app_id' => $this->appId,
  57. 'out_order_no' => $outOrderNo,
  58. 'total_amount' => intval($totalAmount * 100),
  59. 'subject' => "订单号:".$outOrderNo,
  60. 'body' => '抖音担保支付',
  61. 'valid_time' => $this->validTimestamp,
  62. 'sign' => $this->secret
  63. //'notify_url' => $notifyUrl, // 可以不设置 使用小程序后台设置的回调
  64. ];
  65. $data = array_filter($data);
  66. $data['sign'] = $this->getSign($data);
  67. return $this->post(
  68. $this->API::CREATE_ORDER,
  69. $data
  70. );
  71. }
  72. /**
  73. * @param array $data
  74. * @return string
  75. */
  76. public function getSign(array $data)
  77. {
  78. foreach ($data as $k => $v) {
  79. if ($k == "other_settle_params" || $k == "app_id" || $k == "sign" || $k == "thirdparty_id")
  80. continue;
  81. $value = trim(strval($v));
  82. $len = strlen($value);
  83. if ($len > 1 && substr($value, 0, 1) == "\"" && substr($value, $len, $len - 1) == "\"")
  84. $value = substr($value, 1, $len - 1);
  85. $value = trim($value);
  86. if ($value == "" || $value == "null")
  87. continue;
  88. array_push($rList, $value);
  89. }
  90. array_push($rList, $this->slat);
  91. sort($rList, SORT_STRING);
  92. return md5(implode('&', $rList));
  93. }
  94. /**
  95. * @param array $data
  96. * @return string
  97. */
  98. public function getNotifySign(array $data)
  99. {
  100. $filtered = [];
  101. foreach ($data as $key => $value) {
  102. if (in_array($key, ['msg_signature', 'type'])) {
  103. continue;
  104. }
  105. $value = trim(strval($value));
  106. $len = strlen($value);
  107. if ($len > 1 && substr($value, 0, 1) == "\"" && substr($value, $len, $len - 1) == "\"")
  108. $value = substr($value, 1, $len - 1);
  109. $filtered[] =
  110. is_string($value)
  111. ? trim($value)
  112. : $value;
  113. }
  114. $filtered[] = trim($this->token);
  115. sort($filtered, SORT_STRING);
  116. return sha1(trim(implode('', $filtered)));
  117. }
  118. /**
  119. * @param string $code
  120. * @return array|mixed
  121. * @throws \Exception
  122. */
  123. public function login($code = ''): array
  124. {
  125. return $this->post($this->API::LOGIN, [
  126. 'appid' => $this->appId,
  127. 'secret' => $this->secret,
  128. 'code' => $code,
  129. ]);
  130. }
  131. /**
  132. * 接口请求
  133. *
  134. * @param string $uri
  135. * @param array $data
  136. * @return array|mixed
  137. * @throws \Exception
  138. */
  139. protected function post($uri = '', $data = []) : array
  140. {
  141. try {
  142. $client = new Client();
  143. $res = $client->post($uri, [
  144. 'verify' => false,
  145. 'headers' => ['Content-Type' => 'application/json'],
  146. 'body' => json_encode($data)
  147. ]);
  148. $stringBody = (string)$res->getBody();
  149. $res = json_decode($stringBody, true);
  150. if(!empty($res['err_no'])){
  151. throw new \Exception("请求字节跳动API接口错误,错误码:{$res['err_no']},错误信息:{$res['err_tips']}");
  152. }
  153. return $res['data'];
  154. } catch (GuzzleException $e) {
  155. \Log::error($e->getMessage());
  156. throw new \Exception($e->getMessage());
  157. }
  158. }
  159. protected function setAccessFileDir(): void
  160. {
  161. $this->accessTokenDir = storage_path('app/bytedance');
  162. }
  163. protected function setAccessFilePath(): void
  164. {
  165. $this->accessTokenFile = storage_path('app/bytedance/bytedance_access_token.json');
  166. }
  167. protected function setNoticeUrl(): void
  168. {
  169. $this->noticeUrl = env('APP_URL').'/api/pay/bytedance/notify';
  170. }
  171. }