BetbyTestService.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. <?php
  2. namespace App\Game\Services;
  3. use App\Game\BetBy\PlayerDataItem;
  4. use App\Game\GlobalUserInfo;
  5. use App\Notification\TelegramBot;
  6. use GuzzleHttp\Client;
  7. use GuzzleHttp\Exception\RequestException;
  8. use App\Game\BetBy\PlayerDetails;
  9. use App\Game\BetBy\PlayerSegment;
  10. use App\Game\BetBy\BonusTemplate;
  11. use App\Game\BetBy\TemplateItem;
  12. use App\Game\BetBy\BonusItem;
  13. use App\Game\BetBy\Bonus;
  14. use App\Game\BetBy\ErrorItem;
  15. use Firebase\JWT\JWT;
  16. use Firebase\JWT\Key;
  17. use Exception;
  18. use Illuminate\Support\Facades\Log;
  19. class BetbyTestService
  20. {
  21. protected $client;
  22. protected $baseUrl;
  23. public $operatorId;
  24. public $brandId;
  25. protected $privateKey;
  26. protected $publicKey;
  27. //-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENPcid+kKnpbbW3HuY+m1uRbishkC
  28. //nhD1k1apZyZUqLy3kmCtBTdKfNaOrk9hjOmJjD84hRBHjwLCgPraOATGAw==-----END PUBLIC KEY-----
  29. public function __construct()
  30. {
  31. $this->client = new Client();
  32. $this->baseUrl = "https://external-api.invisiblesport.com/api/v1/external_api";
  33. $this->operatorId = '2424946715804176387';
  34. $this->brandId = '2424948766365847552';
  35. $this->privateKey = '-----BEGIN EC PRIVATE KEY-----
  36. MHcCAQEEIE+5vrcbCZ+7UyavHgEmaNDMVc4V/OmQcK8hMnoaBI0toAoGCCqGSM49
  37. AwEHoUQDQgAENPcid+kKnpbbW3HuY+m1uRbishkCnhD1k1apZyZUqLy3kmCtBTdKfNaOrk9hjOmJjD84hRBHjwLCgPraOATGAw==
  38. -----END EC PRIVATE KEY-----';
  39. $this->publicKey = '-----BEGIN PUBLIC KEY-----
  40. MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjmWwsejCzJHljSDpYtwP
  41. uA5HYIP49dNaIuY4ambFiYSTLCPEMqq6kJ9Xo6nphW0mfV+VsDOqcynkzgNAG2y0
  42. kuEe7TF0Tpc5OgTrj9PEm6kxCryxnHSRB56Wv22RQlpqYEO/wQiTXxecgceVL/Dm
  43. vQ91peCcuK6GtSr46RzT/lzsx6DrcGTdUb0aJz7sszmhN6YG8RAb/vHGuekfajZi
  44. UilPU51NOFqRLtSYTxBG3uakPhNtwnxjTFoQZmQ3jPFXCXwn+FnocLIPK5UPJe35
  45. sbnqGhWyIPnTPf75HE6WqdP4GnMrvO1oIaIluuyM1WjCxTVVqHUnUJpMOAPK4QtK
  46. CkU8kWV3URJrTyuQ9A8dODcCvRjZZp96Dvd6taJHN2/brZ78zX3gBP4C8MrtP1na
  47. lzs/Dr7Uo5yQoe/CW5cHczRG/XtbOhloqCJXu56vbMC9DQIDVMEythprAe6haVPE
  48. GBJ2Ci3VgIMY91HILsE42Aoj5uN2LiHLWyh5UN/ESJeY/BL8Vxkv6VDaPjRZD7ee
  49. JeepqOs7zNo2srXBfJIQ3n0ZZULIO/hdZInTLXnAGaP+5eUjkSqsQWt3OttRXmA7
  50. 7Ps7k45ghFB1ZFa+t00omOpBzP7HF6f8sdnFNSugvc2vW8c3+Noj74PimX9AcJfd
  51. +rFDv70YSd67t+E/9DSo4l8CAwEAAQ==
  52. -----END PUBLIC KEY-----';
  53. }
  54. /**
  55. * 验证并解码JWT payload
  56. *
  57. * @param string $token JWT token
  58. * @return array 解码后的数据
  59. */
  60. public function decodePayload(string $token): array
  61. {
  62. return (array) JWT::decode($token, new Key($this->publicKey, 'RS256'));
  63. }
  64. /**
  65. * 生成JWT payload
  66. *
  67. * @param array $data 数据
  68. * @return string 生成的JWT token
  69. */
  70. public function encodePayload(array $data): string
  71. {
  72. $now = time();
  73. $payload = [
  74. 'iat' => $now,
  75. 'exp' => $now + 3600,
  76. 'jti' => strval(rand()),
  77. 'iss' => $this->brandId,
  78. 'aud' => $this->brandId,
  79. 'nbf' => $now,
  80. 'payload' => $data
  81. ];
  82. return JWT::encode($payload, $this->privateKey, 'ES256');
  83. }
  84. public function getDefaultJWT($user)
  85. {
  86. $user=(array) $user;
  87. $now = time();
  88. $payload = [
  89. 'iss' => $this->brandId,
  90. 'sub' => $user['GlobalUID'],
  91. 'name' => $user['NickName'],
  92. 'iat' => $now,
  93. 'exp' => $now + 3600,
  94. 'jti' => strval(rand()),
  95. 'lang'=>$user['DefaultLanguage']??GlobalUserInfo::getLocale(),
  96. 'currency'=>env('CONFIG_24680_CURRENCY')
  97. ];
  98. return JWT::encode($payload, $this->privateKey, 'ES256');
  99. }
  100. /**
  101. * 处理请求异常
  102. *
  103. * @param RequestException $e 异常对象
  104. * @return array 错误信息
  105. */
  106. private function handleRequestException(RequestException $e): array
  107. {
  108. $response = $e->getResponse();
  109. if ($response) {
  110. $body = json_decode($response->getBody(), true);
  111. if (isset($body['error'])) {
  112. (new TelegramBot())->sendMsgWithEnv($body);
  113. return [
  114. 'error' => new ErrorItem($body['error'])
  115. ];
  116. }
  117. }
  118. return [
  119. 'error' => new ErrorItem(['name' => 'UnknownError', 'description' => 'An unknown error occurred.'])
  120. ];
  121. }
  122. /**
  123. * 处理响应数据
  124. *
  125. * @param $response
  126. * @return mixed
  127. */
  128. private function handleResponse($response)
  129. {
  130. $data = json_decode($response->getBody(), true);
  131. $data= $this->decodePayload($data);
  132. if (isset($data['error'])) {
  133. (new TelegramBot())->sendMsgWithEnv($data);
  134. return ['error' => new ErrorItem($data['error'])];
  135. }
  136. return $data;
  137. }
  138. public function ping()
  139. {
  140. try {
  141. $response = $this->client->get($this->baseUrl . '/ping' );
  142. $data = json_decode($response->getBody(),true);
  143. return $data;
  144. } catch (RequestException $e) {
  145. return $this->handleRequestException($e);
  146. }
  147. }
  148. /**
  149. * 调用Betby的PLAYER_DETAILS接口
  150. * 获取玩家详细信息
  151. *
  152. * @param string $externalPlayerId 玩家ID
  153. * @param string $segment 分段
  154. * @param array $depositMethods 存款方式
  155. * @return PlayerDetails|array
  156. */
  157. public function getPlayerDetails(string $externalPlayerId, string $segment, array $depositMethods)
  158. {
  159. $payload = $this->encodePayload([
  160. 'external_player_id' => $externalPlayerId,
  161. 'segment' => $segment,
  162. 'deposit_methods' => $depositMethods
  163. ]);
  164. try {
  165. $response = $this->client->post($this->baseUrl . '/player_details', [
  166. 'headers' => ['Content-Type' => 'application/json'],
  167. 'json' => ['payload' => $payload]
  168. ]);
  169. $data = $this->handleResponse($response);
  170. if (isset($data['error'])) {
  171. return $data;
  172. }
  173. return new PlayerDetails($data);
  174. } catch (RequestException $e) {
  175. return $this->handleRequestException($e);
  176. }
  177. }
  178. /**
  179. * 调用Betby的PLAYER_SEGMENT接口
  180. * 获取玩家分段信息
  181. *
  182. * @param string $externalPlayerId 玩家ID
  183. * @return PlayerSegment|array
  184. */
  185. public function getPlayerSegment(string $externalPlayerId)
  186. {
  187. $payload = $this->encodePayload([
  188. 'brand_id' => $this->brandId,
  189. 'external_player_id' => $externalPlayerId
  190. ]);
  191. try {
  192. $response = $this->client->post($this->baseUrl . '/player/segment', [
  193. 'headers' => ['Content-Type' => 'application/json'],
  194. 'json' => ['payload' => $payload]
  195. ]);
  196. $data = $this->handleResponse($response);
  197. if (isset($data['error'])) {
  198. return $data;
  199. }
  200. return new PlayerSegment($data);
  201. } catch (RequestException $e) {
  202. return $this->handleRequestException($e);
  203. }
  204. }
  205. /**
  206. * 调用Betby的TEMPLATES接口
  207. * 获取所有奖金模板
  208. *
  209. * @return array<TemplateItem>|array
  210. */
  211. public function getBonusTemplates()
  212. {
  213. $payload = $this->encodePayload(['operator_id' => $this->operatorId]);
  214. try {
  215. $response = $this->client->post($this->baseUrl . '/bonus/templates', [
  216. 'headers' => ['Content-Type' => 'application/json'],
  217. 'json' => ['payload' => $payload]
  218. ]);
  219. $data = $this->handleResponse($response);
  220. if (isset($data['error'])) {
  221. return $data;
  222. }
  223. return array_map(function($template) {
  224. return new TemplateItem($template);
  225. }, $data['items']);
  226. } catch (RequestException $e) {
  227. return $this->handleRequestException($e);
  228. }
  229. }
  230. /**
  231. * 调用Betby的TEMPLATE接口
  232. * 获取指定ID的奖金模板
  233. *
  234. * @param string $templateId 奖金模板ID
  235. * @return TemplateItem|array
  236. */
  237. public function getBonusTemplate(string $templateId)
  238. {
  239. $payload = $this->encodePayload([
  240. 'operator_id' => $this->operatorId,
  241. 'template_id' => $templateId
  242. ]);
  243. try {
  244. $response = $this->client->post($this->baseUrl . '/bonus/template', [
  245. 'headers' => ['Content-Type' => 'application/json'],
  246. 'json' => ['payload' => $payload]
  247. ]);
  248. $data = $this->handleResponse($response);
  249. if (isset($data['error'])) {
  250. return $data;
  251. }
  252. return new TemplateItem($data);
  253. } catch (RequestException $e) {
  254. return $this->handleRequestException($e);
  255. }
  256. }
  257. /**
  258. * 调用Betby的PLAYER_BONUSES接口
  259. * 获取玩家的所有奖金
  260. *
  261. * @param string $externalPlayerId 玩家ID
  262. * @return array<BonusItem>|array
  263. */
  264. public function getPlayerBonuses(string $externalPlayerId)
  265. {
  266. $payload = $this->encodePayload([
  267. 'brand_id' => $this->brandId,
  268. 'external_player_id' => $externalPlayerId
  269. ]);
  270. try {
  271. $response = $this->client->post($this->baseUrl . '/bonus/player_bonuses', [
  272. 'headers' => ['Content-Type' => 'application/json'],
  273. 'json' => ['payload' => $payload]
  274. ]);
  275. $data = $this->handleResponse($response);
  276. if (isset($data['error'])) {
  277. return $data;
  278. }
  279. return array_map(function($bonus) {
  280. return new BonusItem($bonus);
  281. }, $data['items']);
  282. } catch (RequestException $e) {
  283. return $this->handleRequestException($e);
  284. }
  285. }
  286. /**
  287. * 调用Betby的BONUS接口
  288. * NB. Because of technical features one request can contain around 1000 players.
  289. * Please, split your list of players in case there are significantly more than 1000 players.
  290. * 获取指定ID的奖金信息
  291. *
  292. * @param string $bonusId 奖金ID
  293. * @return BonusItem|array
  294. */
  295. public function getBonus(string $bonusId)
  296. {
  297. $payload = $this->encodePayload([
  298. 'brand_id' => $this->brandId,
  299. 'bonus_id' => $bonusId
  300. ]);
  301. try {
  302. $response = $this->client->post($this->baseUrl . '/bonus', [
  303. 'headers' => ['Content-Type' => 'application/json'],
  304. 'json' => ['payload' => $payload]
  305. ]);
  306. $data = $this->handleResponse($response);
  307. if (isset($data['error'])) {
  308. return $data;
  309. }
  310. return new BonusItem($data);
  311. } catch (RequestException $e) {
  312. return $this->handleRequestException($e);
  313. }
  314. }
  315. /**
  316. * 调用Betby的MASS_GIVE_BONUS接口
  317. * 批量授予奖金
  318. *
  319. * @param string $brandId 品牌ID
  320. * @param string $templateId 奖金模板ID
  321. * @param PlayerDataItem[] $playersData 玩家数据
  322. * @return array 响应数据
  323. */
  324. public function massGiveBonus(string $brandId, string $templateId, array $playersData): array
  325. {
  326. $payload = $this->encodePayload([
  327. 'brand_id' => $brandId,
  328. 'template_id' => $templateId,
  329. 'players_data' => array_map(function($playerData) {
  330. return [
  331. 'external_player_id' => $playerData->externalPlayerId,
  332. 'currency' => $playerData->currency,
  333. 'amount' => $playerData->amount,
  334. 'force_activated' => $playerData->forceActivated,
  335. ];
  336. }, $playersData)
  337. ]);
  338. try {
  339. $response = $this->client->post($this->baseUrl . '/bonus/mass_give_bonus', [
  340. 'headers' => ['Content-Type' => 'application/json'],
  341. 'json' => ['payload' => $payload]
  342. ]);
  343. $data=$this->handleResponse($response);
  344. if (isset($data['error'])) {
  345. return $data;
  346. }
  347. $bonusItems = [];
  348. foreach ( $data['result'] as $BonusPlayerItem){
  349. foreach ($BonusPlayerItem as $playerID => $bonus) {
  350. if(isset($bonus['error'])){
  351. $bonusItems[$playerID]= ['error'=>new ErrorItem($bonus['error'])];
  352. }else{
  353. $bonusItems[$playerID] = new BonusItem($bonus);
  354. }
  355. }
  356. }
  357. return $bonusItems;
  358. } catch (RequestException $e) {
  359. return $this->handleRequestException($e);
  360. }
  361. }
  362. /**
  363. * 调用Betby的REVOKE_BONUS接口
  364. * 撤销指定ID的奖金
  365. *
  366. * @param string[] $bonuses_ids 奖金IDs
  367. * @return array<BonusItem>|array
  368. */
  369. public function revokeBonus(array $bonuses_ids)
  370. {
  371. $payload = $this->encodePayload([
  372. 'brand_id' => $this->brandId,
  373. 'bonus_ids' => $bonuses_ids
  374. ]);
  375. try {
  376. $response = $this->client->post($this->baseUrl . '/bonus/revoke', [
  377. 'headers' => ['Content-Type' => 'application/json'],
  378. 'json' => ['payload' => $payload]
  379. ]);
  380. $data=$this->handleResponse($response);
  381. if (isset($data['error'])) {
  382. return $data;
  383. }
  384. $bonusItems = [];
  385. foreach ( $data['result'] as $BonusPlayerItem){
  386. foreach ($BonusPlayerItem as $playerID => $bonus) {
  387. if(isset($bonus['error'])){
  388. $bonusItems[$playerID]= ['error'=>new ErrorItem($bonus['error'])];
  389. }else{
  390. $bonusItems[$playerID] = new BonusItem($bonus);
  391. }
  392. }
  393. }
  394. return $bonusItems;
  395. } catch (RequestException $e) {
  396. return $this->handleRequestException($e);
  397. }
  398. }
  399. }