2
0

AiPayCashierLogic.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <?php
  2. namespace App\Http\logic\api;
  3. use App\dao\Estatisticas\RechargeWithDraw;
  4. use App\dao\Pay\AccountPayInfo;
  5. use App\Http\helper\NumConfig;
  6. use App\Inter\CashierInterFace;
  7. use App\Models\PrivateMail;
  8. use App\Models\RecordUserDataStatistics;
  9. use App\Services\AiPay;
  10. use App\Services\StoredProcedure;
  11. use App\Util;
  12. use Illuminate\Support\Facades\DB;
  13. use Illuminate\Support\Facades\Log;
  14. use Illuminate\Support\Facades\Redis;
  15. class AiPayCashierLogic implements CashierInterFace
  16. {
  17. const AGENT = 101;
  18. protected $agent = 101;
  19. public function payment($RecordID, $amount, $accountName, $phone, $email, $OrderId, $PixNum, $PixType, $IFSCNumber, $BranchBank, $BankNO)
  20. {
  21. $query = DB::connection('write')->table('QPAccountsDB.dbo.OrderWithDraw')->where('RecordID', $RecordID)->first();
  22. if (!$query) {
  23. return 'fail';
  24. }
  25. $service = new AiPay('AiPayOut');
  26. $config = $service->getConfig();
  27. if(!$accountName){
  28. $dao = new AccountPayInfo();
  29. $$accountName = $dao->randUserName($accountName);
  30. }
  31. $paymentMethod = $this->resolvePaymentMethod($PixType);
  32. $accountData = $this->buildAccountData($paymentMethod, $accountName, $PixNum, $email, $phone, $IFSCNumber, $BranchBank, $BankNO);
  33. if ($paymentMethod === null || empty($accountData)) {
  34. Util::WriteLog('AiPay', 'unsupported PixType for withdraw: ' . $PixType.'_'.$accountName);
  35. return 'fail';
  36. }
  37. $amountDecimal = number_format($amount / NumConfig::NUM_VALUE, 2, '.', '');
  38. $params = [
  39. 'mchNo' => $config['mchNo'] ?? '',
  40. 'mchOrderNo' => $OrderId,
  41. 'paymentMethod' => $paymentMethod,
  42. 'amount' => $amountDecimal,
  43. 'notifyUrl' => $config['cashNotify'] ?? '',
  44. 'timestamp' => (string)round(microtime(true) * 1000),
  45. 'accountData' => $accountData,
  46. ];
  47. $signedParams = $service->sign($params);
  48. $result = $service->post('/api/transfer', $signedParams);
  49. Log::info('AiPay withdraw request', $signedParams);
  50. Log::info('AiPay withdraw response', [$result]);
  51. try {
  52. $data = \GuzzleHttp\json_decode($result, true);
  53. } catch (\Throwable $e) {
  54. Util::WriteLog('AiPay_error', $e->getMessage());
  55. return 'fail';
  56. }
  57. if (isset($data['code']) && (int)$data['code'] === 0) {
  58. return $data;
  59. }else{
  60. if ($query->State == 5) {
  61. // 同步失败,发送邮件给玩家,退还金币
  62. $msg = 'Withdraw Fail';
  63. $WithDraw = $query->WithDraw + $query->ServiceFee;
  64. $bonus = '30000,' . $WithDraw;
  65. PrivateMail::failMail($query->UserID, $OrderId, $WithDraw, $msg, $bonus);
  66. $withdraw_data = ['State' => 6, 'agent' => 1043, 'finishDate' => now(),'remark' => json_encode($data)];
  67. DB::connection('write')->table('QPAccountsDB.dbo.OrderWithDraw')->where('OrderId', $query->OrderId)->update($withdraw_data);
  68. $RecordData = ['after_state' => 6, 'update_at' => now()];
  69. DB::connection('write')->table('QPAccountsDB.dbo.AccountsRecord')->where('type', 1)->where('RecordID', $RecordID)->update($RecordData);
  70. }
  71. return 'fail';
  72. }
  73. }
  74. public function notify($post)
  75. {
  76. if (!is_array($post)) {
  77. $post = \GuzzleHttp\json_decode($post, true);
  78. }
  79. Util::WriteLog('AiPay', 'cash callback: ' . json_encode($post, JSON_UNESCAPED_UNICODE));
  80. $OrderId = $post['mchOrderNo'] ?? '';
  81. $query = DB::connection('write')->table('QPAccountsDB.dbo.OrderWithDraw')->where('OrderId', $OrderId)->first();
  82. if (!$query) {
  83. Util::WriteLog('AiPay', 'withdraw order not found: ' . $OrderId);
  84. return '{"success":true,"message":"order not found"}';
  85. }
  86. if (!in_array($query->State, [5, 7])) {
  87. Util::WriteLog('AiPay', $OrderId . ' already handled');
  88. return 'success';
  89. }
  90. $agentID = DB::connection('write')->table('agent.dbo.admin_configs')
  91. ->where('config_value', self::AGENT)
  92. ->where('type', 'cash')
  93. ->value('id') ?? '';
  94. $status = (int)($post['status'] ?? 0);
  95. $now = now();
  96. $withdraw_data = [];
  97. $notifyState = 0;
  98. $msg = $post['msg'] ?? '';
  99. $TakeMoney = $query->WithDraw + $query->ServiceFee;
  100. $UserID = $query->UserID;
  101. switch ($status) {
  102. case 3: // success
  103. $withdraw_data = [
  104. 'State' => 2,
  105. 'agent' => $agentID,
  106. 'finishDate' => $now,
  107. ];
  108. $this->handleSuccess($UserID, $TakeMoney, $OrderId, $query->ServiceFee);
  109. $notifyState = 1;
  110. break;
  111. case 4: // fail
  112. case 5: // reversal
  113. $msg = $msg ?: 'Withdraw rejected';
  114. $bonus = '30000,' . $TakeMoney;
  115. PrivateMail::failMail($UserID, $OrderId, $TakeMoney, $msg, $bonus);
  116. Util::WriteLog('AiPayEmail', [$UserID, $OrderId, $TakeMoney, $msg, $bonus]);
  117. $withdraw_data = ['State' => 6, 'agent' => $agentID, 'remark' => $msg];
  118. $notifyState = 2;
  119. break;
  120. default:
  121. Util::WriteLog('AiPay', 'cash notify pending: ' . $OrderId);
  122. return 'success';
  123. }
  124. $recordData = [
  125. 'before_state' => $query->State,
  126. 'after_state' => $withdraw_data['State'] ?? $query->State,
  127. 'RecordID' => $query->RecordID,
  128. 'update_at' => date('Y-m-d H:i:s'),
  129. ];
  130. DB::connection('write')->table('QPAccountsDB.dbo.AccountsRecord')
  131. ->updateOrInsert(['RecordID' => $query->RecordID, 'type' => 1], $recordData);
  132. DB::connection('write')->table('QPAccountsDB.dbo.OrderWithDraw')
  133. ->where('OrderId', $OrderId)
  134. ->update($withdraw_data);
  135. return 'success';
  136. }
  137. protected function resolvePaymentMethod($PixType): ?string
  138. {
  139. $map = [
  140. 1 => '1201', // CashApp
  141. 2 => '1202', // PayPal
  142. 3 => '1203', // Venmo
  143. 4 => '1204', // CARD
  144. ];
  145. return $map[$PixType] ?? null;
  146. }
  147. protected function buildAccountData(?string $paymentMethod, $accountName, $PixNum, $email, $phone, $IFSCNumber, $BranchBank, $BankNO): array
  148. {
  149. $splitName = $this->splitName($accountName);
  150. switch ($paymentMethod) {
  151. case '1201':
  152. $account = $PixNum ?: $phone;
  153. if ($account && strpos($account, '$') !== 0) {
  154. $account = '$' . $account;
  155. }
  156. return ['account' => $account];
  157. case '1202':
  158. case '1203':
  159. return [
  160. 'account' => $email ?: $PixNum,
  161. 'firstName' => $splitName['first'],
  162. 'lastName' => $splitName['last'],
  163. ];
  164. case '1204':
  165. return [
  166. 'cardNo' => $PixNum ?: $BankNO,
  167. 'cardExpireMonth' => $this->extractMonth($BranchBank),
  168. 'cardExpireYear' => $this->extractYear($BranchBank),
  169. 'cardSecurityCode' => substr($IFSCNumber ?? '', 0, 4),
  170. 'cardBrand' => 'visa',
  171. 'firstName' => $splitName['first'],
  172. 'lastName' => $splitName['last'],
  173. ];
  174. }
  175. return [];
  176. }
  177. // protected function splitName($name): array
  178. // {
  179. // $name = trim($name ?: 'User');
  180. // $parts = preg_split('/\s+/', $name, 2);
  181. // return [
  182. // 'first' => $parts[0] ?? 'User',
  183. // 'last' => $parts[1] ?? 'Edward',
  184. // ];
  185. // }
  186. protected function splitName(string $name = null): array
  187. {
  188. $name = trim($name ?? '');
  189. if ($name === '') $name = 'Willam Edson';
  190. // // 处理 "Last, First"
  191. // if (str_contains($name, ',')) {
  192. // $tmp = array_map('trim', explode(',', $name, 2));
  193. // $name = ($tmp[1] ?? '') . ' ' . ($tmp[0] ?? '');
  194. // $name = trim($name);
  195. // }
  196. // 清洗:保留字母、空格、-、'
  197. $name = preg_replace("/[^A-Za-z\\s\\-']/u", ' ', $name);
  198. $name = trim(preg_replace('/\s+/', ' ', $name));
  199. $parts = explode(' ', $name, 2);
  200. return [
  201. 'first' => $parts[0] ?: 'Willam',
  202. 'last' => $parts[1] ?? 'Edward', // 或 'Edward' / 'User'
  203. ];
  204. }
  205. protected function extractMonth($value): string
  206. {
  207. if (preg_match('/(\d{2})/', (string)$value, $match)) {
  208. return $match[1];
  209. }
  210. return '01';
  211. }
  212. protected function extractYear($value): string
  213. {
  214. if (preg_match('/(\d{2,4})/', (string)$value, $match)) {
  215. return substr($match[1], -2);
  216. }
  217. return '25';
  218. }
  219. protected function handleSuccess($UserID, $TakeMoney, $OrderId, $serviceFee): void
  220. {
  221. $first = DB::connection('write')->table('QPAccountsDB.dbo.UserTabData')->where('UserID', $UserID)->first();
  222. if ($first) {
  223. DB::connection('write')->table('QPAccountsDB.dbo.UserTabData')->where('UserID', $UserID)->increment('TakeMoney', $TakeMoney);
  224. } else {
  225. DB::connection('write')->table('QPAccountsDB.dbo.UserTabData')->insert(['TakeMoney' => $TakeMoney, 'UserID' => $UserID]);
  226. try {
  227. PrivateMail::praiseSendMail($UserID);
  228. } catch (\Throwable $e) {
  229. // ignore mail failure
  230. }
  231. }
  232. try {
  233. StoredProcedure::addPlatformData($UserID, 4, $TakeMoney);
  234. } catch (\Throwable $exception) {
  235. Util::WriteLog('StoredProcedure', $exception->getMessage());
  236. }
  237. $withdrawal_position_log = DB::connection('write')->table('agent.dbo.withdrawal_position_log')->where('order_sn', $OrderId)->first();
  238. if ($withdrawal_position_log) {
  239. DB::connection('write')->table('agent.dbo.withdrawal_position_log')
  240. ->where('order_sn', $OrderId)
  241. ->update(['take_effect' => 2, 'update_at' => date('Y-m-d H:i:s')]);
  242. }
  243. RecordUserDataStatistics::updateOrAdd($UserID, $TakeMoney, 0, $serviceFee);
  244. (new RechargeWithDraw())->withDraw($UserID, $TakeMoney);
  245. $redis = Redis::connection();
  246. $redis->incr('draw_' . date('Ymd') . $UserID);
  247. StoredProcedure::user_label($UserID, 2, $TakeMoney);
  248. }
  249. }