AiPayCashierLogic.php 8.8 KB

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