ByteDance.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. $rList = [];
  79. foreach ($data as $k => $v) {
  80. if ($k == "other_settle_params" || $k == "app_id" || $k == "sign" || $k == "thirdparty_id")
  81. continue;
  82. $value = trim(strval($v));
  83. $len = strlen($value);
  84. if ($len > 1 && substr($value, 0, 1) == "\"" && substr($value, $len, $len - 1) == "\"")
  85. $value = substr($value, 1, $len - 1);
  86. $value = trim($value);
  87. if ($value == "" || $value == "null")
  88. continue;
  89. array_push($rList, $value);
  90. }
  91. array_push($rList, $this->slat);
  92. sort($rList, SORT_STRING);
  93. return md5(implode('&', $rList));
  94. }
  95. /**
  96. * @param array $data
  97. * @return string
  98. */
  99. public function getNotifySign(array $data)
  100. {
  101. $filtered = [];
  102. foreach ($data as $key => $value) {
  103. if (in_array($key, ['msg_signature', 'type'])) {
  104. continue;
  105. }
  106. $value = trim(strval($value));
  107. $len = strlen($value);
  108. if ($len > 1 && substr($value, 0, 1) == "\"" && substr($value, $len, $len - 1) == "\"")
  109. $value = substr($value, 1, $len - 1);
  110. $filtered[] =
  111. is_string($value)
  112. ? trim($value)
  113. : $value;
  114. }
  115. $filtered[] = trim($this->token);
  116. sort($filtered, SORT_STRING);
  117. return sha1(trim(implode('', $filtered)));
  118. }
  119. /**
  120. * @param string $code
  121. * @return array|mixed
  122. * @throws \Exception
  123. */
  124. public function login($code = ''): array
  125. {
  126. return $this->post($this->API::LOGIN, [
  127. 'appid' => $this->appId,
  128. 'secret' => $this->secret,
  129. 'code' => $code,
  130. ]);
  131. }
  132. /**
  133. * 接口请求
  134. *
  135. * @param string $uri
  136. * @param array $data
  137. * @return array|mixed
  138. * @throws \Exception
  139. */
  140. protected function post($uri = '', $data = []) : array
  141. {
  142. try {
  143. $client = new Client();
  144. $res = $client->post($uri, [
  145. 'verify' => false,
  146. 'headers' => ['Content-Type' => 'application/json'],
  147. 'body' => json_encode($data)
  148. ]);
  149. $stringBody = (string)$res->getBody();
  150. $res = json_decode($stringBody, true);
  151. if(!empty($res['err_no'])){
  152. throw new \Exception("请求字节跳动API接口错误,错误码:{$res['err_no']},错误信息:{$res['err_tips']}");
  153. }
  154. return $res['data'];
  155. } catch (GuzzleException $e) {
  156. \Log::error($e->getMessage());
  157. throw new \Exception($e->getMessage());
  158. }
  159. }
  160. protected function setAccessFileDir(): void
  161. {
  162. $this->accessTokenDir = storage_path('app/bytedance');
  163. }
  164. protected function setAccessFilePath(): void
  165. {
  166. $this->accessTokenFile = storage_path('app/bytedance/bytedance_access_token.json');
  167. }
  168. protected function setNoticeUrl(): void
  169. {
  170. $this->noticeUrl = env('APP_URL').'/api/pay/bytedance/notify';
  171. }
  172. }