BetbyService.php 14 KB

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