PaymentOrderService.php 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724
  1. <?php
  2. namespace App\Services;
  3. use App\Constants\HttpStatus;
  4. use App\Models\PaymentOrder;
  5. use App\Models\RechargeChannel;
  6. use App\Models\User;
  7. use App\Models\Wallet as WalletModel;
  8. use Exception;
  9. use Illuminate\Support\Facades\DB;
  10. use Illuminate\Support\Facades\Log;
  11. use App\Services\Payment\JdPayService;
  12. use App\Services\Payment\NoPayService;
  13. use App\Services\Payment\QianBaoService;
  14. use App\Services\Payment\SanJinService;
  15. use App\Services\Payment\ZimuPayService;
  16. use App\Services\Payment\PaymentProviderService;
  17. use App\Services\ConfigService;
  18. use App\Services\AdminRealtime\AdminRealtime;
  19. /**
  20. * 投注
  21. */
  22. class PaymentOrderService extends BaseService
  23. {
  24. const TYPE_PAY = 1; // 代收
  25. const TYPE_PAYOUT = 2; // 代付
  26. const TYPE_SELF_PAY = 3; // 手动充值
  27. const TYPE_SELF_PAYOUT = 4; // 手动提现打款
  28. const STATUS_STAY = 0; // 待处理
  29. const STATUS_PROCESS = 1; // 处理中
  30. const STATUS_SUCCESS = 2; // 成功
  31. const STATUS_FAIL = 3; // 失败
  32. const STATUS_USER = 4; // 待用户提交凭证
  33. const STATUS_AUDIT = 5; //待人工审核
  34. const STATUS_CANCEL = 6; //已撤回
  35. const STATUS_TIMEOUT = 7; //超时(在线充值处理中超过 PAY_TIMEOUT_MINUTES 未支付,仍可接收迟到的回调)
  36. const PAY_TIMEOUT_MINUTES = 30;
  37. const PAY_OPEN_STATUSES = [self::STATUS_PROCESS, self::STATUS_TIMEOUT]; // 在线充值可接收回调/查询入账的状态
  38. public static string $MODEL = PaymentOrder::class;
  39. /**
  40. * @description: 模型
  41. * @return {string}
  42. */
  43. public static function model(): string
  44. {
  45. return PaymentOrder::class;
  46. }
  47. /**
  48. * @description: 枚举
  49. * @return {*}
  50. */
  51. public static function enum(): string
  52. {
  53. return '';
  54. }
  55. /**
  56. * @description: 获取查询条件
  57. * @param {array} $search 查询内容
  58. * @return {array}
  59. */
  60. public static function getWhere(array $search = []): array
  61. {
  62. $where = [];
  63. if (isset($search['member_id']) && !empty($search['member_id'])) {
  64. $where[] = ['payment_orders.member_id', '=', $search['member_id']];
  65. }
  66. if (isset($search['channel']) && !empty($search['channel'])) {
  67. if ($search['channel'] == 'rgcz') {
  68. $search['type'] = 3;
  69. }
  70. $where[] = ['payment_orders.channel', '=', $search['channel']];
  71. }
  72. if (isset($search['type']) && !empty($search['type'])) {
  73. $where[] = ['payment_orders.type', '=', $search['type']];
  74. }
  75. if (isset($search['order_no']) && !empty($search['order_no'])) {
  76. $where[] = ['payment_orders.order_no', '=', $search['order_no']];
  77. }
  78. if (isset($search['id']) && !empty($search['id'])) {
  79. $where[] = ['payment_orders.id', '=', $search['id']];
  80. }
  81. if (isset($search['status']) && $search['status'] != '') {
  82. $where[] = ['payment_orders.status', '=', $search['status']];
  83. }
  84. if (isset($search['first_name']) && !empty($search['first_name'])) {
  85. $where[] = ['users.first_name', 'like', "%" . $search['first_name'] . "%"];
  86. }
  87. if (isset($search['payment_type']) && $search['payment_type'] != '') {
  88. $where[] = ['payment_orders.payment_type', '=', $search['payment_type']];
  89. }
  90. return $where;
  91. }
  92. /**
  93. * @description: 查询单条数据
  94. * @param array $search
  95. * @return \App\Models\Coin|null
  96. */
  97. public static function findOne(array $search): ?PaymentOrder
  98. {
  99. return static::$MODEL::where(self::getWhere($search))->first();
  100. }
  101. /**
  102. * @description: 查询所有数据
  103. * @param array $search
  104. * @return \Illuminate\Database\Eloquent\Collection
  105. */
  106. public static function findAll(array $search = [])
  107. {
  108. return static::$MODEL::where(self::getWhere($search))->get();
  109. }
  110. /**
  111. * @description: 分页查询
  112. * @param array $search
  113. * @return array
  114. */
  115. public static function paginate(array $search = [])
  116. {
  117. $limit = isset($search['limit']) ? $search['limit'] : 15;
  118. $paginator = static::$MODEL::where(self::getWhere($search))
  119. ->join('users', 'users.member_id', '=', 'payment_orders.member_id')
  120. ->select("payment_orders.*", "users.first_name","users.admin_note as user_admin_note") // 提前查好需要的字段
  121. ->orderByDesc('payment_orders.created_at')
  122. ->paginate($limit);
  123. $totalAmount = 0;
  124. $totalSuccess = 0;
  125. $totalFail = 0;
  126. $list = $paginator->items();
  127. foreach ($list as $item) {
  128. $item['amount'] = floatval($item['amount']);
  129. $totalAmount += $item['amount'];
  130. if (in_array($item['status'], [1, 2])) $totalSuccess += $item['amount'];
  131. if ($item['status'] == 3) $totalFail += $item['amount'];
  132. }
  133. return [
  134. 'total' => $paginator->total(),
  135. 'total_amount' => $totalAmount,
  136. 'total_success' => $totalSuccess,
  137. 'total_fail' => $totalFail,
  138. 'data' => $list
  139. ];
  140. }
  141. /**
  142. * @description:
  143. * @param {*} $params
  144. * @return {*}
  145. */
  146. public static function submit($params = [])
  147. {
  148. $result = false;
  149. $msg['code'] = self::NOT;
  150. $msg['msg'] = '';
  151. // 2. 判断是否是更新
  152. if (!empty($params['id'])) {
  153. // 更新
  154. $info = self::findOne(['id' => $params['id']]);
  155. if (!$info) {
  156. $msg['msg'] = '数据不存在!';
  157. } else {
  158. $result = $info->update($params);
  159. $id = $params['id'];
  160. }
  161. } else {
  162. // 创建
  163. $result = $info = static::$MODEL::create($params);
  164. $id = $result->id;
  165. }
  166. if ($result) {
  167. $msg['code'] = self::YES;
  168. $msg['msg'] = '设置成功';
  169. $msg['key'] = $id;
  170. } else {
  171. $msg['msg'] = empty($msg['msg']) ? '操作失败' : $msg['msg'];
  172. }
  173. return $msg;
  174. }
  175. /**
  176. * @description: 创建代收订单
  177. * @param {*} $memberId
  178. * @param {*} $amount
  179. * @param {*} $paymentType 支付类型:支付宝、数字人民币
  180. * @return {*}
  181. */
  182. public static function createPay($memberId, $amount, $paymentType)
  183. {
  184. $result = [];
  185. $result['chat_id'] = $memberId;
  186. $result['code'] = 0;
  187. $result['url'] = '';
  188. if (!PaymentProviderService::requestEnabled((string)$paymentType, 1)) {
  189. $result['code'] = 20001;
  190. $result['text'] = '支付公司已禁用';
  191. return $result;
  192. }
  193. $user = User::query()->where('member_id', $memberId)->first();
  194. $groupId = (int)($user->recharge_channel_group_id ?? 1);
  195. $channelConfig = RechargeChannel::checkRechargeChannel($paymentType, $groupId);
  196. if ($channelConfig === false) {
  197. $result['text'] = '不支持此充值方式';
  198. $result['code'] = 20001;
  199. return $result;
  200. }
  201. $bonusSnapshot = RechargeBonusService::capture((string)$channelConfig->type, (string)$memberId);
  202. $channel = ''; // 支付的通道
  203. $product = SanJinService::product($groupId);
  204. $max = (float)($channelConfig->max ?? 0);
  205. $min = (float)($channelConfig->min ?? 0);
  206. $rate = (float)($channelConfig->rate ?? 0);
  207. $selectedProduct = null;
  208. $geText = '';
  209. foreach ($product as $k => $v) {
  210. if ($v['type'] == $paymentType) {
  211. $selectedProduct = $v;
  212. if ($v['type'] == 'zfbge') {
  213. if (in_array($amount, $v['fixed'])) {
  214. $channel = $k;
  215. } else {
  216. $geText .= "❌ 此充值通道固定充值金额为" . implode(',', $v['fixed']) . "请选择上述固定金额!";
  217. }
  218. } else {
  219. if ($amount >= $v['min'] && $amount <= $v['max']) {
  220. $channel = $k;
  221. $rate = $v['rate'];
  222. }
  223. if ($min == 0) {
  224. $min = $v['min'];
  225. }
  226. if ($max == 0) {
  227. $max = $v['max'];
  228. }
  229. if ($min > $v['min']) {
  230. $min = $v['min'];
  231. }
  232. if ($max < $v['max']) {
  233. $max = $v['max'];
  234. }
  235. }
  236. }
  237. }
  238. $isDirectProvider = JdPayService::isChannel($paymentType)
  239. || NoPayService::isRechargeChannel($paymentType)
  240. || ZimuPayService::isRechargeChannel($paymentType);
  241. if ($isDirectProvider && ($amount < $min || ($max > 0 && $amount > $max))) {
  242. $result['text'] = "❌ 此充值通道充值金额{$min}-{$max}请务必输入区间金额!";
  243. $result['code'] = 20001;
  244. return $result;
  245. }
  246. // 没有找到支付通道
  247. if (empty($channel) && !JdPayService::isChannel($paymentType) && !NoPayService::isRechargeChannel($paymentType) && !ZimuPayService::isRechargeChannel($paymentType)) {
  248. // $text = "发起充值失败 \n";
  249. // $text .= "最低充值:" . $min . " \n";
  250. // $text .= "最高充值:" . $max . " \n";
  251. // $text .= "请重新填写充值的金额!";
  252. $text = "❌ 此充值通道充值金额{$min}-{$max}请务必输入区间金额!";
  253. $result['text'] = $text;
  254. if ($geText) {
  255. $result['text'] = $geText;
  256. }
  257. $result['code'] = 20001;
  258. return $result;
  259. }
  260. if (ZimuPayService::isRechargeChannel($paymentType) || ZimuPayService::isRechargeChannel($channel)) {
  261. $channel = ZimuPayService::CHANNEL_RECHARGE;
  262. $bankName = $selectedProduct['name'] ?? '808充值';
  263. $data = [];
  264. $data['type'] = self::TYPE_PAY;
  265. $data['member_id'] = $memberId;
  266. $data['amount'] = ZimuPayService::amount($amount);
  267. $data['channel'] = $channel;
  268. $data['fee'] = $amount * $rate;
  269. $data['bank_name'] = $bankName;
  270. $order_no = self::createOrderNo('zm' . $data['type'] . '_', $memberId);
  271. $data['order_no'] = $order_no;
  272. $data['callback_url'] = ZimuPayService::getPayNotifyUrl();
  273. $data['remark'] = '充值费率:' . $rate;
  274. if ($bonusSnapshot !== null) $data['recharge_bonus_snapshot'] = $bonusSnapshot;
  275. $data['status'] = self::STATUS_STAY;
  276. $ret = ZimuPayService::pay($amount, $order_no, (string)$memberId);
  277. Log::channel('payment')->info('ZIMU支付发起', [
  278. 'order_no' => $order_no,
  279. 'member_id' => $memberId,
  280. 'amount' => $data['amount'],
  281. 'response' => $ret,
  282. ]);
  283. if (($ret['code'] ?? -1) == 0) {
  284. $item = $ret['data'] ?? [];
  285. $payUrl = $item['payUrl'] ?? '';
  286. $data['status'] = self::STATUS_PROCESS;
  287. $data['pay_no'] = $item['orderNo'] ?? '';
  288. $data['pay_url'] = $payUrl;
  289. $data['pay_data'] = json_encode($ret, JSON_UNESCAPED_UNICODE);
  290. static::$MODEL::create($data);
  291. if ($payUrl) {
  292. $result['image'] = asset(self::createPaymentQrCode($payUrl));
  293. }
  294. $result['text'] = "{$bankName}确认 \n";
  295. $result['text'] .= "请使用浏览器扫码或者复制支付地址到浏览器打开 \n";
  296. $result['text'] .= "支付地址:{$payUrl}\n";
  297. $result['text'] .= "支付金额:" . $amount . " RMB \n";
  298. $result['text'] .= "支付完成后请耐心等待,支付到账会第一时间通知您! \n";
  299. $result['url'] = $payUrl;
  300. } else {
  301. $result['text'] = $ret['msg'] ?? 'ZIMU支付发起失败';
  302. $result['code'] = 20002;
  303. }
  304. return $result;
  305. }
  306. if (NoPayService::isRechargeChannel($paymentType) || NoPayService::isRechargeChannel($channel)) {
  307. $channel = NoPayService::isRechargeChannel($paymentType) ? $paymentType : $channel;
  308. $bankName = $selectedProduct['name'] ?? 'NO快捷充值';
  309. $data = [];
  310. $data['type'] = self::TYPE_PAY;
  311. $data['member_id'] = $memberId;
  312. $data['amount'] = NoPayService::amount($amount);
  313. $data['channel'] = $channel;
  314. $data['fee'] = $amount * $rate;
  315. $data['bank_name'] = $bankName;
  316. $order_no = self::createOrderNo('no' . $data['type'] . '_', $memberId);
  317. $data['order_no'] = $order_no;
  318. $data['callback_url'] = NoPayService::getDepositNotifyUrl();
  319. $data['remark'] = '充值费率:' . $rate;
  320. if ($bonusSnapshot !== null) $data['recharge_bonus_snapshot'] = $bonusSnapshot;
  321. $data['status'] = self::STATUS_STAY;
  322. $ret = NoPayService::pay($amount, $order_no, (string)$memberId, $channel);
  323. Log::channel('payment')->info('NO支付发起', [
  324. 'order_no' => $order_no,
  325. 'member_id' => $memberId,
  326. 'amount' => $data['amount'],
  327. 'channel' => $channel,
  328. 'response' => $ret,
  329. ]);
  330. if (($ret['code'] ?? -1) == 0) {
  331. $payUrl = $ret['data']['url'] ?? '';
  332. $data['status'] = self::STATUS_PROCESS;
  333. $data['pay_url'] = $payUrl;
  334. $data['pay_data'] = json_encode($ret, JSON_UNESCAPED_UNICODE);
  335. static::$MODEL::create($data);
  336. if ($payUrl) {
  337. $result['image'] = asset(self::createPaymentQrCode($payUrl));
  338. }
  339. $result['text'] = "{$bankName}充值确认 \n";
  340. $result['text'] .= "请使用浏览器扫码或者复制支付地址到浏览器打开 \n";
  341. $result['text'] .= "支付地址:{$payUrl}\n";
  342. $result['text'] .= "支付金额:" . $amount . " RMB \n";
  343. $result['text'] .= "支付完成后请耐心等待,支付到账会第一时间通知您! \n";
  344. $result['url'] = $payUrl;
  345. } else {
  346. $result['text'] = $ret['msg'] ?? 'NO支付发起失败';
  347. $result['code'] = 20002;
  348. }
  349. return $result;
  350. }
  351. if (JdPayService::isChannel($paymentType) || JdPayService::isChannel($channel)) {
  352. $channel = JdPayService::CHANNEL;
  353. $bankName = $selectedProduct['name'] ?? 'JD钱包';
  354. $data = [];
  355. $data['type'] = self::TYPE_PAY;
  356. $data['member_id'] = $memberId;
  357. $data['amount'] = JdPayService::amount($amount);
  358. $data['channel'] = $channel;
  359. $data['fee'] = $amount * $rate;
  360. $data['bank_name'] = $bankName;
  361. $order_no = self::createOrderNo('jd' . $data['type'] . '_', $memberId);
  362. $data['order_no'] = $order_no;
  363. $data['callback_url'] = JdPayService::getNotifyUrl();
  364. $data['remark'] = '充值费率:' . $rate;
  365. if ($bonusSnapshot !== null) $data['recharge_bonus_snapshot'] = $bonusSnapshot;
  366. $data['status'] = self::STATUS_STAY;
  367. $ret = JdPayService::pay($amount, $order_no);
  368. Log::channel('payment')->info('JD支付发起', $ret);
  369. if (($ret['code'] ?? 0) == 200) {
  370. $item = $ret['data'] ?? [];
  371. $payUrl = $item['url'] ?? '';
  372. $data['status'] = self::STATUS_PROCESS;
  373. $data['pay_no'] = $item['orderNo'] ?? '';
  374. $data['pay_url'] = $payUrl;
  375. $data['pay_data'] = json_encode($ret, JSON_UNESCAPED_UNICODE);
  376. static::$MODEL::create($data);
  377. if ($payUrl) {
  378. $result['image'] = asset(self::createPaymentQrCode($payUrl));
  379. }
  380. $text = "{$bankName}充值确认 \n";
  381. $text .= "请使用浏览器扫码或者复制支付地址到浏览器打开 \n";
  382. $text .= "支付地址:{$payUrl}\n";
  383. $text .= "支付金额:" . $amount . " RMB \n";
  384. $text .= "请按实际支付金额进行付款,否则影响到账 \n";
  385. $text .= "支付完成后请耐心等待,支付到账会第一时间通知您! \n";
  386. $result['text'] = $text;
  387. $result['url'] = $payUrl;
  388. } else {
  389. $result['text'] = $ret['message'] ?? 'JD支付发起失败';
  390. $result['code'] = 20002;
  391. }
  392. return $result;
  393. }
  394. $data = [];
  395. $data['type'] = self::TYPE_PAY;
  396. $data['member_id'] = $memberId;
  397. $data['amount'] = $amount;
  398. $data['channel'] = $channel;
  399. $data['fee'] = $amount * $rate;
  400. $data['bank_name'] = SanJinService::getChannel($paymentType) ?? '';
  401. $order_no = self::createOrderNo('sj' . $data['type'] . '_', $memberId);
  402. $data['order_no'] = $order_no;
  403. $data['callback_url'] = SanJinService::getNotifyUrl();
  404. $data['remark'] = '充值费率:' . $rate;
  405. if ($bonusSnapshot !== null) $data['recharge_bonus_snapshot'] = $bonusSnapshot;
  406. $data['status'] = self::STATUS_STAY;
  407. $ret = SanJinService::pay(($amount * 100), $order_no, $channel);
  408. Log::error('三斤支付发起:', $ret);
  409. if ($ret['code'] == 0) {
  410. $qrCode = asset(self::createPaymentQrCode($ret['data']['payUrl']));
  411. $result['image'] = $qrCode;
  412. $item = $ret['data'];
  413. $data['status'] = self::STATUS_PROCESS;
  414. $data['pay_no'] = $item['tradeNo'];
  415. $data['pay_url'] = $item['payUrl'];
  416. $data['pay_data'] = json_encode($ret, JSON_UNESCAPED_UNICODE);
  417. $info = static::$MODEL::create($data);
  418. // $text = "✅ 支付提示 \n";
  419. $text = "{$data['bank_name']}充值确认 \n";
  420. // $text .= "支付方式:{$data['bank_name']} \n";
  421. $text .= "请使用浏览器扫码或者复制支付地址到浏览器打开 \n";
  422. $text .= "支付地址:{$ret['data']['payUrl']}\n";
  423. $text .= "支付金额:" . $amount . " RMB \n";
  424. $text .= "请按实际支付金额进行付款,否则影响到账 \n";
  425. $text .= "支付完成后请耐心等待,支付到账会第一时间通知您! \n";
  426. $result['text'] = $text;
  427. $result['url'] = $ret['data']['payUrl'];
  428. } else {
  429. $result['text'] = $ret['message'];
  430. $result['code'] = 20002;
  431. }
  432. return $result;
  433. }
  434. /**
  435. * @description: 接收支付的通知
  436. * @param {*} $params
  437. * @return {*}
  438. */
  439. public static function receivePay($params)
  440. {
  441. if (ZimuPayService::getAppId() !== '' && ($params['appId'] ?? '') === ZimuPayService::getAppId()) {
  442. $info = self::findOne(['order_no' => $params['mchOrderNo'] ?? '']);
  443. if (!$info || $info->type != self::TYPE_PAY || !ZimuPayService::isRechargeChannel($info->channel)) {
  444. Log::error('ZIMU充值回调订单不存在或类型不匹配', [
  445. 'mch_order_no' => $params['mchOrderNo'] ?? '',
  446. 'order_id' => $info->id ?? null,
  447. 'order_type' => $info->type ?? null,
  448. 'channel' => $info->channel ?? null,
  449. ]);
  450. return false;
  451. }
  452. if (!ZimuPayService::verifyNotify($params)) {
  453. Log::error('ZIMU充值回调验签失败', [
  454. 'mch_order_no' => $params['mchOrderNo'] ?? '',
  455. 'app_id' => $params['appId'] ?? '',
  456. 'signature' => ZimuPayService::notifySignatureDiagnostics($params),
  457. ]);
  458. return false;
  459. }
  460. if (bccomp(ZimuPayService::amount($info->amount), ZimuPayService::amount($params['amount'] ?? 0), 2) !== 0) {
  461. Log::error('ZIMU充值回调金额不一致', [
  462. 'mch_order_no' => $params['mchOrderNo'] ?? '',
  463. 'order_amount' => $info->amount,
  464. 'callback_amount' => $params['amount'] ?? null,
  465. ]);
  466. return false;
  467. }
  468. if (!in_array((int)$info->status, self::PAY_OPEN_STATUSES, true)) {
  469. return true;
  470. }
  471. $processed = self::applyPayCallback(
  472. $info,
  473. ZimuPayService::amount($info->amount),
  474. (string)$params['status'],
  475. ZimuPayService::PAY_STATUS_SUCCESS,
  476. ZimuPayService::PAY_STATUS_FAIL,
  477. $params
  478. );
  479. if ($processed && (string)$params['status'] === ZimuPayService::PAY_STATUS_SUCCESS) {
  480. self::notifyUser($info->member_id, "✅ 支付成功 \n充值金额:{$info->amount} RMB \n订单号:{$info->order_no} \n您充值的金额已到账,请注意查收!");
  481. }
  482. return true;
  483. }
  484. if (NoPayService::getDepositMerchantId() !== '' && ($params['appId'] ?? '') === NoPayService::getDepositMerchantId()) {
  485. $info = self::findOne(['order_no' => $params['merchantOrderNo'] ?? '']);
  486. if (!$info || $info->type != self::TYPE_PAY || !NoPayService::isRechargeChannel($info->channel)) {
  487. Log::error('NO钱包充值回调订单不存在或类型不匹配', [
  488. 'merchant_order_no' => $params['merchantOrderNo'] ?? '',
  489. 'order_id' => $info->id ?? null,
  490. 'order_type' => $info->type ?? null,
  491. 'channel' => $info->channel ?? null,
  492. ]);
  493. return false;
  494. }
  495. if (!NoPayService::verifyDepositNotify($params)) {
  496. Log::error('NO钱包充值回调验签失败', [
  497. 'merchant_order_no' => $params['merchantOrderNo'] ?? '',
  498. 'app_id' => $params['appId'] ?? '',
  499. 'signature' => NoPayService::depositNotifySignatureDiagnostics($params),
  500. ]);
  501. return false;
  502. }
  503. if (bccomp(NoPayService::amount($info->amount), NoPayService::amount($params['amount'] ?? 0), 2) !== 0) {
  504. Log::error('NO钱包充值回调金额不一致', [
  505. 'merchant_order_no' => $params['merchantOrderNo'] ?? '',
  506. 'order_amount' => $info->amount,
  507. 'callback_amount' => $params['amount'] ?? null,
  508. ]);
  509. return false;
  510. }
  511. if (!in_array((int)$info->status, self::PAY_OPEN_STATUSES, true)) {
  512. return true;
  513. }
  514. $processed = self::applyPayCallback(
  515. $info,
  516. NoPayService::amount($info->amount),
  517. (string)$params['state'],
  518. NoPayService::STATE_SUCCESS,
  519. NoPayService::STATE_FAIL,
  520. $params
  521. );
  522. if ($processed && (string)$params['state'] === NoPayService::STATE_SUCCESS) {
  523. self::notifyUser($info->member_id, "✅ 支付成功 \n充值金额:{$info->amount} RMB \n订单号:{$info->order_no} \n您充值的金额已到账,请注意查收!");
  524. }
  525. return true;
  526. }
  527. if (($params['userCode'] ?? '') === JdPayService::getMerchantId()) {
  528. $info = self::findOne(['order_no' => $params['orderCode'] ?? ''])
  529. ?: static::$MODEL::where('pay_no', $params['orderCode'] ?? '')->first();
  530. if (!$info || $info->type != self::TYPE_PAY) {
  531. return false;
  532. }
  533. if (!JdPayService::verifyPayNotify($params)) {
  534. return false;
  535. }
  536. if (bccomp(JdPayService::amount($info->amount), JdPayService::amount($params['amount'] ?? 0), 2) !== 0) {
  537. return false;
  538. }
  539. if (!in_array((int)$info->status, self::PAY_OPEN_STATUSES, true)) {
  540. return true;
  541. }
  542. $processed = self::applyPayCallback($info, JdPayService::amount($info->amount), (string)$params['status'], JdPayService::PAY_STATUS_SUCCESS, JdPayService::PAY_STATUS_FAIL, $params);
  543. if ($processed && (string)$params['status'] === JdPayService::PAY_STATUS_SUCCESS) {
  544. $text = "✅ 支付成功 \n";
  545. $text .= "充值金额:{$info->amount} RMB \n";
  546. $text .= "订单号:{$info->order_no} \n";
  547. $text .= "您充值的金额已到账,请注意查收!";
  548. self::notifyUser($info->member_id, $text);
  549. }
  550. return true;
  551. }
  552. // 判断商户号
  553. if (($params['mchId'] ?? '') == SanJinService::getMerchantId()) {
  554. $must = ['mchId', 'productId', 'tradeNo', 'outTradeNo', 'amount', 'payAmount', 'state', 'createTime', 'payTime'];
  555. $info = self::findOne(['order_no' => $params['outTradeNo']]);
  556. if ($info) {
  557. // 平台以分计算转成元
  558. $payAmount = $params['payAmount'] / 100;
  559. // 判断金额是不是正确认
  560. if ($info->amount != $payAmount) {
  561. $text = '❌ 支付失败提醒 \n';
  562. $text .= "订单金额:{$info->amount} \n";
  563. $text .= "实际支付:{$payAmount} \n";
  564. $text .= "订单号:{$params['outTradeNo']} \n";
  565. $text .= "失败原因:支付金额与订单金额不一致 \n";
  566. $text .= "请联系客服处理!";
  567. self::notifyUser($info->member_id, $text);
  568. return false;
  569. }
  570. if ($params['sign'] != SanJinService::signature($params, $must)) {
  571. return false;
  572. }
  573. if (!in_array((int)$info->status, self::PAY_OPEN_STATUSES, true)) {
  574. return false;
  575. }
  576. // 付款
  577. if ($info->type == self::TYPE_PAY) {
  578. $processed = self::applyPayCallback($info, $payAmount, (string)$params['state'], '1', null, $params);
  579. if ($processed && $params['state'] == 1) {
  580. $text = "✅ 支付成功 \n";
  581. $text .= "充值金额:{$payAmount} RMB \n";
  582. $text .= "订单号:{$params['outTradeNo']} \n";
  583. $text .= "您充值的金额已到账,请注意查收!";
  584. self::notifyUser($info->member_id, $text);
  585. } else {
  586. $text = "❌ 支付失败 \n";
  587. $text .= "充值金额:{$payAmount} RMB \n";
  588. $text .= "订单号:{$params['outTradeNo']} \n";
  589. }
  590. return true;
  591. }
  592. }
  593. }
  594. }
  595. // 充值笔笔返
  596. public static function rechargesBibiReturn($memberId,$payAmount,$info_id)
  597. {
  598. $rate = ConfigService::getVal('recharges_bibi_return');
  599. $amount = $payAmount * $rate;
  600. if($rate > 0){
  601. $wallet = WalletService::findOne(['member_id' => $memberId]);
  602. $balance = $wallet->available_balance;
  603. $available_balance = bcadd($balance, $amount, 10);
  604. $wallet->available_balance = $available_balance;
  605. $wallet->save();
  606. BalanceLogService::addLog($memberId, $amount, $balance, $available_balance, '优惠活动', $info_id, '充值笔笔返');
  607. }
  608. }
  609. private static function applyPayCallback(PaymentOrder $info, $payAmount, string $status, string $successStatus, ?string $failStatus, array $params): bool
  610. {
  611. return DB::transaction(function () use ($info, $payAmount, $status, $successStatus, $failStatus, $params) {
  612. $order = PaymentOrder::where('id', $info->id)->lockForUpdate()->first();
  613. if (!$order || (int)$order->type !== self::TYPE_PAY || !in_array((int)$order->status, self::PAY_OPEN_STATUSES, true)) {
  614. return false;
  615. }
  616. $order->callback_data = json_encode($params, JSON_UNESCAPED_UNICODE);
  617. $order->state = $status;
  618. if ($status === $successStatus) {
  619. $order->status = self::STATUS_SUCCESS;
  620. $order->save();
  621. $wallet = WalletModel::where('member_id', $order->member_id)->lockForUpdate()->first();
  622. if (!$wallet) {
  623. throw new Exception('钱包不存在', HttpStatus::CUSTOM_ERROR);
  624. }
  625. $balance = $wallet->available_balance;
  626. $availableBalance = bcadd($balance, $payAmount, 10);
  627. $wallet->available_balance = $availableBalance;
  628. $wallet->save();
  629. $principal = BalanceLogService::addLog($order->member_id, $payAmount, $balance, $availableBalance, '三方充值', $order->id, '');
  630. RechargeBonusService::grant('payment_order', $order, $principal);
  631. // Deposit hooks can grant an immediately available first-deposit reward.
  632. $wallet->refresh();
  633. $rate = ConfigService::getVal('recharges_bibi_return');
  634. $bonusAmount = $payAmount * $rate;
  635. if ($rate > 0 && $bonusAmount > 0) {
  636. $bonusBeforeBalance = $wallet->available_balance;
  637. $bonusAfterBalance = bcadd($bonusBeforeBalance, $bonusAmount, 10);
  638. $wallet->available_balance = $bonusAfterBalance;
  639. $wallet->save();
  640. BalanceLogService::addLog($order->member_id, $bonusAmount, $bonusBeforeBalance, $bonusAfterBalance, '优惠活动', $order->id, '充值笔笔返');
  641. }
  642. return true;
  643. }
  644. if ($failStatus === null || $status === $failStatus) {
  645. $order->status = self::STATUS_FAIL;
  646. }
  647. $order->save();
  648. return true;
  649. }, 3);
  650. }
  651. /**
  652. * 拒绝提现
  653. * @description 改变订单状态为失败,将金额退给用户,并记录提现退款的资金日
  654. * @param int $orderId 订单ID PaymentOrder 表的ID
  655. * @param string $remark 失败原因
  656. * @return array
  657. */
  658. public static function withdrawalFailed($orderId, $remark): array
  659. {
  660. try {
  661. $order = PaymentOrder::where('id', $orderId)
  662. ->whereIn('type', [2,4])
  663. ->where('status', self::STATUS_STAY)
  664. ->first();
  665. if (!$order) throw new Exception("订单不存在_{$orderId}", HttpStatus::CUSTOM_ERROR);
  666. // 更新提现记录状态为失败
  667. $order->status = self::STATUS_FAIL;
  668. $order->remark = $remark;
  669. if (!$order->save()) {
  670. throw new Exception("更新失败,请重试", HttpStatus::CUSTOM_ERROR);
  671. }
  672. //钱包余额增加
  673. $wallet = WalletService::findOne(['member_id' => $order->member_id]);
  674. $beforeBalance = $wallet->available_balance;
  675. $availableBalance = bcadd($wallet->available_balance, $order->amount, 10);
  676. $wallet->available_balance = $availableBalance;
  677. if (!$wallet->save()) {
  678. throw new Exception("更新失败,请重试", HttpStatus::CUSTOM_ERROR);
  679. }
  680. // 记录退款日志
  681. BalanceLogService::addLog($order->member_id, $order->amount, $beforeBalance, $availableBalance, '三方提现', $order->id, '提现失败退款');
  682. } catch (Exception $e) {
  683. return ['code' => HttpStatus::CUSTOM_ERROR, 'msg' => $e->getMessage()];
  684. }
  685. return ['code' => 0, 'msg' => 'ok'];
  686. }
  687. /**
  688. * 创建代付订单 (根据 orderId 将钱转到用户指定账户)
  689. * @param int $orderId PaymentOrder 表的ID
  690. */
  691. public static function createPayout(int $orderId): array
  692. {
  693. try {
  694. $order = PaymentOrder::where('id', $orderId)
  695. ->where('type', 2)
  696. ->where('status', self::STATUS_STAY)
  697. ->first();
  698. if (!$order) throw new Exception("订单不存在_{$orderId}", HttpStatus::CUSTOM_ERROR);
  699. $amount = $order->amount;
  700. if (!PaymentProviderService::requestEnabled((string)$order->channel, 2)) {
  701. throw new Exception('支付公司已禁用', HttpStatus::CUSTOM_ERROR);
  702. }
  703. if (!RechargeChannel::query()->where('data_type', 2)->where('type', $order->channel)->available()->exists()) {
  704. throw new Exception('提现通道已停用或删除', HttpStatus::CUSTOM_ERROR);
  705. }
  706. $amount = number_format($amount, 2, '.', '');
  707. if (NoPayService::isWithdrawChannel($order->channel)) {
  708. $ret = NoPayService::withdraw($amount, $order->order_no, (string)$order->member_id, (string)$order->account, (string)$order->card_no);
  709. Log::channel('payment')->info('NO提现接口调用', [
  710. 'order_id' => $order->id,
  711. 'order_no' => $order->order_no,
  712. 'member_id' => $order->member_id,
  713. 'amount' => $amount,
  714. 'q_account' => $order->card_no,
  715. 'response' => $ret,
  716. ]);
  717. if (($ret['code'] ?? -1) != 0) {
  718. throw new Exception($ret['msg'] ?? 'NO提现失败', HttpStatus::CUSTOM_ERROR);
  719. }
  720. $order->pay_no = $ret['data']['orderNo'] ?? '';
  721. $order->pay_data = json_encode($ret, JSON_UNESCAPED_UNICODE);
  722. } elseif (ZimuPayService::isPayoutChannel($order->channel)) {
  723. self::assertZimuBalanceEnough($amount, [
  724. 'order_id' => $order->id,
  725. 'order_no' => $order->order_no,
  726. 'member_id' => $order->member_id,
  727. 'channel' => $order->channel,
  728. ]);
  729. if (ZimuPayService::isCashChannel($order->channel)) {
  730. $ret = ZimuPayService::cash(
  731. $amount,
  732. $order->order_no,
  733. (string)$order->account,
  734. (string)$order->card_no,
  735. (string)$order->bank_name,
  736. ZimuPayService::cashPayMethod((string)$order->bank_name)
  737. );
  738. } else {
  739. $ret = ZimuPayService::withdraw($amount, $order->order_no, (string)$order->member_id, (string)$order->card_no);
  740. }
  741. Log::channel('payment')->info('ZIMU提现/代付接口调用', [
  742. 'order_id' => $order->id,
  743. 'order_no' => $order->order_no,
  744. 'member_id' => $order->member_id,
  745. 'amount' => $amount,
  746. 'channel' => $order->channel,
  747. 'response' => $ret,
  748. ]);
  749. if (($ret['code'] ?? -1) != 0) {
  750. throw new Exception($ret['msg'] ?? 'ZIMU提现/代付失败', HttpStatus::CUSTOM_ERROR);
  751. }
  752. $order->pay_no = $ret['data']['orderNo'] ?? '';
  753. $order->pay_data = json_encode($ret, JSON_UNESCAPED_UNICODE);
  754. } elseif (JdPayService::isChannel($order->channel)) {
  755. self::assertJdBalanceEnough($amount, [
  756. 'order_id' => $order->id,
  757. 'order_no' => $order->order_no,
  758. 'member_id' => $order->member_id,
  759. 'address' => $order->card_no,
  760. ]);
  761. $ret = JdPayService::remit($amount, $order->order_no, $order->card_no);
  762. Log::channel('payment')->info('JD下发接口调用', [
  763. 'order_id' => $order->id,
  764. 'order_no' => $order->order_no,
  765. 'member_id' => $order->member_id,
  766. 'amount' => $amount,
  767. 'address' => $order->card_no,
  768. 'response' => $ret,
  769. ]);
  770. if (($ret['code'] ?? 0) != 200) {
  771. $logContext = [
  772. 'order_id' => $order->id,
  773. 'order_no' => $order->order_no,
  774. 'member_id' => $order->member_id,
  775. 'amount' => $amount,
  776. 'address' => $order->card_no,
  777. 'response' => $ret,
  778. ];
  779. Log::channel('payment_error')->error('JD下发接口失败', $logContext);
  780. Log::error('JD下发接口失败', $logContext);
  781. throw new Exception($ret['message'] ?? 'JD下发失败', HttpStatus::CUSTOM_ERROR);
  782. }
  783. $order->pay_no = $ret['data']['orderNo'] ?? '';
  784. $order->pay_data = json_encode($ret, JSON_UNESCAPED_UNICODE);
  785. } else {
  786. $ret = QianBaoService::payout($amount, $order->order_no, $order->bank_name, $order->account, $order->card_no);
  787. Log::error('第三方代付接口调用:' . json_encode($ret, JSON_UNESCAPED_UNICODE));
  788. if ($ret['code'] != 200) throw new Exception($ret['msg'], HttpStatus::CUSTOM_ERROR);
  789. }
  790. $order->status = self::STATUS_PROCESS;
  791. $order->save();
  792. } catch (Exception $e) {
  793. Log::channel('payment_error')->error('创建代付订单失败', [
  794. 'order_id' => $orderId,
  795. 'error' => $e->getMessage(),
  796. ]);
  797. Log::error('创建代付订单失败', [
  798. 'order_id' => $orderId,
  799. 'error' => $e->getMessage(),
  800. ]);
  801. return ['code' => HttpStatus::CUSTOM_ERROR, 'msg' => $e->getMessage()];
  802. }
  803. return ['code' => 0, 'msg' => 'ok'];
  804. }
  805. /**
  806. * @description: 创建代付订单 (自动直接到账,包括用户钱包的扣款和提现记录的生成以及余额日志的创建)
  807. * @param {*} $memberId 会员ID
  808. * @param {*} $amount 代付金额
  809. * @param {*} $channel 提现通道 DF001 支付宝转卡/DF002 支付宝转支付宝
  810. * @param {*} $bank_name 银行名称/支付宝
  811. * @param {*} $account 姓名
  812. * @param {*} $card_no 银行卡号/支付宝账号
  813. * @return {*}
  814. */
  815. public static function autoCreatePayout($memberId, $amount, $channel, $bank_name, $account, $card_no)
  816. {
  817. $default_amount = $amount;
  818. $result = [];
  819. $result['chat_id'] = $memberId;
  820. if (!PaymentProviderService::requestEnabled((string)$channel, 2)) {
  821. $result['text'] = '支付公司已禁用';
  822. return $result;
  823. }
  824. $user = User::query()->where('member_id', $memberId)->first();
  825. $groupId = (int)($user->recharge_channel_group_id ?? 1);
  826. $channelConfig = RechargeChannel::checkWithdrawChannel($channel, $groupId);
  827. if ($channelConfig === false) {
  828. $result['text'] = '不支持此提现方式';
  829. return $result;
  830. }
  831. $minAmount = (float)($channelConfig->min ?? 0);
  832. $maxAmount = (float)($channelConfig->max ?? 0);
  833. if ($amount < $minAmount || ($maxAmount > 0 && $amount > $maxAmount)) {
  834. $result['text'] = "提现金额范围为{$minAmount}-{$maxAmount}";
  835. return $result;
  836. }
  837. $channelRate = (float)($channelConfig->rate ?? 0);
  838. // 在调用三方支付前开始事务
  839. DB::beginTransaction();
  840. try {
  841. $wallet = WalletService::findOne(['member_id' => $memberId]);
  842. if (!$wallet) {
  843. DB::rollBack();
  844. $result['text'] = '钱包不存在!';
  845. return $result;
  846. }
  847. $balance = $wallet->available_balance;
  848. if (bccomp($balance, $amount, 2) < 0) {
  849. DB::rollBack();
  850. $result['text'] = '您的钱包余额不足!';
  851. return $result;
  852. }
  853. $available_balance = bcsub($balance, $amount, 10);
  854. $data = [];
  855. $data['type'] = self::TYPE_PAYOUT;
  856. $order_no = self::createOrderNo('sj' . $data['type'] . '_', $memberId);
  857. $data['order_no'] = $order_no;
  858. $data['member_id'] = $memberId;
  859. $data['fee'] = $amount * $channelRate + 2;
  860. $amount = number_format($amount, 2, '.', '');
  861. $data['amount'] = $amount;
  862. $data['channel'] = $channel;
  863. $data['bank_name'] = $bank_name;
  864. $data['account'] = $account;
  865. $data['card_no'] = $card_no;
  866. if (NoPayService::isWithdrawChannel($channel)) {
  867. $data['callback_url'] = NoPayService::getWithdrawNotifyUrl();
  868. } elseif (JdPayService::isChannel($channel)) {
  869. $data['callback_url'] = JdPayService::getRemitNotifyUrl();
  870. } elseif (ZimuPayService::isPayoutChannel($channel)) {
  871. $data['callback_url'] = ZimuPayService::getPayoutNotifyUrl();
  872. } else {
  873. $data['callback_url'] = QianBaoService::getNotifyUrl();
  874. }
  875. $data['status'] = self::STATUS_STAY;
  876. $data['remark'] = '提现费率:' . ($channelRate * 100) . '%+2';
  877. // 先预扣款(锁定资金)
  878. $wallet->available_balance = $available_balance;
  879. if (!$wallet->save()) {
  880. DB::rollBack();
  881. $result['text'] = '钱包更新失败!';
  882. return $result;
  883. }
  884. // 创建待处理状态的提现记录
  885. $info = static::$MODEL::create($data);
  886. $id = $info->id;
  887. // 记录余额变动日志
  888. BalanceLogService::addLog($memberId, $default_amount * -1, $balance, $available_balance, '三方提现', $id, '钱宝提现费率:' . ($channelRate * 100) . '%+2');
  889. // 提交事务,确保预扣款成功
  890. DB::commit();
  891. } //
  892. catch (Exception $e) {
  893. // 预扣款失败,回滚事务
  894. DB::rollBack();
  895. $result['text'] = '系统繁忙,请稍后重试!';
  896. Log::error('提现预扣款失败: ' . $e->getMessage(), [
  897. 'member_id' => $memberId,
  898. 'amount' => $amount
  899. ]);
  900. return $result;
  901. }
  902. // 调用三方支付接口(在事务外)
  903. if (NoPayService::isWithdrawChannel($channel)) {
  904. try {
  905. $ret = NoPayService::withdraw($amount, $order_no, (string)$memberId, (string)$account, (string)$card_no);
  906. Log::channel('payment')->info('NO提现接口调用', [
  907. 'order_no' => $order_no,
  908. 'member_id' => $memberId,
  909. 'amount' => $amount,
  910. 'q_account' => $card_no,
  911. 'response' => $ret,
  912. ]);
  913. $success = (($ret['code'] ?? -1) == 0);
  914. $failureMessage = $ret['msg'] ?? 'NO提现失败';
  915. } catch (Exception $e) {
  916. $ret = ['msg' => $e->getMessage()];
  917. $success = false;
  918. $failureMessage = $e->getMessage();
  919. Log::channel('payment_error')->error('NO提现接口异常', [
  920. 'order_no' => $order_no,
  921. 'member_id' => $memberId,
  922. 'amount' => $amount,
  923. 'q_account' => $card_no,
  924. 'error' => $e->getMessage(),
  925. ]);
  926. }
  927. } elseif (ZimuPayService::isPayoutChannel($channel)) {
  928. try {
  929. self::assertZimuBalanceEnough($amount, [
  930. 'order_no' => $order_no,
  931. 'member_id' => $memberId,
  932. 'channel' => $channel,
  933. ]);
  934. if (ZimuPayService::isCashChannel($channel)) {
  935. $ret = ZimuPayService::cash(
  936. $amount,
  937. $order_no,
  938. (string)$account,
  939. (string)$card_no,
  940. (string)$bank_name,
  941. ZimuPayService::cashPayMethod((string)$bank_name)
  942. );
  943. } else {
  944. $ret = ZimuPayService::withdraw($amount, $order_no, (string)$memberId, (string)$card_no);
  945. }
  946. Log::channel('payment')->info('ZIMU提现/代付接口调用', [
  947. 'order_no' => $order_no,
  948. 'member_id' => $memberId,
  949. 'amount' => $amount,
  950. 'channel' => $channel,
  951. 'response' => $ret,
  952. ]);
  953. $success = (($ret['code'] ?? -1) == 0);
  954. $failureMessage = $ret['msg'] ?? 'ZIMU提现/代付失败';
  955. } catch (Exception $e) {
  956. $ret = ['msg' => $e->getMessage()];
  957. $success = false;
  958. $failureMessage = $e->getMessage();
  959. Log::channel('payment_error')->error('ZIMU提现/代付接口异常', [
  960. 'order_no' => $order_no,
  961. 'member_id' => $memberId,
  962. 'amount' => $amount,
  963. 'channel' => $channel,
  964. 'error' => $e->getMessage(),
  965. ]);
  966. }
  967. } elseif (JdPayService::isChannel($channel)) {
  968. try {
  969. self::assertJdBalanceEnough($amount, [
  970. 'order_no' => $order_no,
  971. 'member_id' => $memberId,
  972. 'address' => $card_no,
  973. ]);
  974. $ret = JdPayService::remit($amount, $order_no, $card_no);
  975. Log::channel('payment')->info('JD下发接口调用', [
  976. 'order_no' => $order_no,
  977. 'member_id' => $memberId,
  978. 'amount' => $amount,
  979. 'address' => $card_no,
  980. 'response' => $ret,
  981. ]);
  982. $success = (($ret['code'] ?? 0) == 200);
  983. $failureMessage = $ret['message'] ?? 'JD下发失败';
  984. if (!$success) {
  985. $logContext = [
  986. 'order_no' => $order_no,
  987. 'member_id' => $memberId,
  988. 'amount' => $amount,
  989. 'address' => $card_no,
  990. 'response' => $ret,
  991. ];
  992. Log::channel('payment_error')->error('JD下发接口失败', $logContext);
  993. Log::error('JD下发接口失败', $logContext);
  994. }
  995. } catch (Exception $e) {
  996. $ret = ['message' => $e->getMessage()];
  997. $success = false;
  998. $failureMessage = $e->getMessage();
  999. $logContext = [
  1000. 'order_no' => $order_no,
  1001. 'member_id' => $memberId,
  1002. 'amount' => $amount,
  1003. 'address' => $card_no,
  1004. 'error' => $e->getMessage(),
  1005. ];
  1006. Log::channel('payment_error')->error('JD下发接口异常', $logContext);
  1007. Log::error('JD下发接口异常', $logContext);
  1008. }
  1009. } else {
  1010. $ret = QianBaoService::payout($amount, $order_no, $bank_name, $account, $card_no);
  1011. Log::error('第三方代付接口调用:' . json_encode($ret, JSON_UNESCAPED_UNICODE));
  1012. $success = (($ret['code'] ?? 0) == 200);
  1013. $failureMessage = $ret['msg'] ?? '代付失败';
  1014. }
  1015. if ($success) {
  1016. // 更新提现记录状态为处理中
  1017. DB::beginTransaction();
  1018. try {
  1019. $info->status = self::STATUS_PROCESS;
  1020. if (NoPayService::isWithdrawChannel($channel) || JdPayService::isChannel($channel) || ZimuPayService::isPayoutChannel($channel)) {
  1021. $info->pay_no = $ret['data']['orderNo'] ?? '';
  1022. $info->pay_data = json_encode($ret, JSON_UNESCAPED_UNICODE);
  1023. }
  1024. $info->save();
  1025. DB::commit();
  1026. $text = "✅ 提现申请已提交!\n\n";
  1027. $text .= "钱包余额:{$available_balance} RMB\n";
  1028. $text .= "提现金额:{$default_amount} RMB\n";
  1029. $text .= "⌛️请等待系统处理, 到账时间可能需要几分钟!\n";
  1030. $result['text'] = $text;
  1031. } catch (Exception $e) {
  1032. DB::rollBack();
  1033. // 状态更新失败,但资金已扣款且三方支付已成功
  1034. // 这里可以记录告警,需要人工干预
  1035. Log::error('提现状态更新失败: ' . $e->getMessage(), [
  1036. 'member_id' => $memberId,
  1037. 'order_no' => $order_no
  1038. ]);
  1039. $result['text'] = '提现申请已提交,系统处理中...';
  1040. }
  1041. } //
  1042. else {
  1043. // 三方支付失败,需要回滚之前的预扣款
  1044. DB::beginTransaction();
  1045. try {
  1046. // 恢复钱包余额
  1047. $wallet->available_balance = $balance;
  1048. $wallet->save();
  1049. // 更新提现记录状态为失败
  1050. $info->status = self::STATUS_FAIL;
  1051. $info->remark = $failureMessage;
  1052. $info->save();
  1053. // 记录退款日志
  1054. BalanceLogService::addLog($memberId, $default_amount, $available_balance, $balance, '三方提现', $id, '提现失败退款');
  1055. DB::commit();
  1056. $result['text'] = $failureMessage;
  1057. } catch (Exception $e) {
  1058. DB::rollBack();
  1059. // 回滚失败,需要记录告警,人工干预
  1060. Log::error('提现失败回滚异常: ' . $e->getMessage(), [
  1061. 'member_id' => $memberId,
  1062. 'order_no' => $order_no,
  1063. 'amount' => $amount
  1064. ]);
  1065. $result['text'] = '提现失败,请联系客服处理退款!';
  1066. }
  1067. }
  1068. return $result;
  1069. }
  1070. /**
  1071. * @description: 接收三方订单
  1072. * @param {*} $params
  1073. * @return {*}
  1074. */
  1075. public static function receiveOrder($params)
  1076. {
  1077. if (ZimuPayService::getAppId() !== '' && ($params['appId'] ?? '') === ZimuPayService::getAppId()) {
  1078. $info = self::findOne(['order_no' => $params['mchOrderNo'] ?? '']);
  1079. if (!$info || $info->type != self::TYPE_PAYOUT || !ZimuPayService::isPayoutChannel($info->channel)) {
  1080. Log::error('ZIMU提现/代付回调订单不存在或类型不匹配', [
  1081. 'mch_order_no' => $params['mchOrderNo'] ?? '',
  1082. 'order_id' => $info->id ?? null,
  1083. 'order_type' => $info->type ?? null,
  1084. 'channel' => $info->channel ?? null,
  1085. ]);
  1086. return false;
  1087. }
  1088. if (!ZimuPayService::verifyNotify($params)) {
  1089. Log::error('ZIMU提现/代付回调验签失败', [
  1090. 'mch_order_no' => $params['mchOrderNo'] ?? '',
  1091. 'app_id' => $params['appId'] ?? '',
  1092. 'signature' => ZimuPayService::notifySignatureDiagnostics($params),
  1093. ]);
  1094. return false;
  1095. }
  1096. if (bccomp(ZimuPayService::amount($info->amount), ZimuPayService::amount($params['amount'] ?? 0), 2) !== 0) {
  1097. Log::error('ZIMU提现/代付回调金额不一致', [
  1098. 'mch_order_no' => $params['mchOrderNo'] ?? '',
  1099. 'order_amount' => $info->amount,
  1100. 'callback_amount' => $params['amount'] ?? null,
  1101. ]);
  1102. return false;
  1103. }
  1104. self::onSubmitZimuPayout($params, $info);
  1105. return true;
  1106. }
  1107. if (NoPayService::getWithdrawMerchantId() !== '' && ($params['appId'] ?? '') === NoPayService::getWithdrawMerchantId()) {
  1108. $info = self::findOne(['order_no' => $params['merchantOrderNo'] ?? '']);
  1109. if (!$info || $info->type != self::TYPE_PAYOUT || !NoPayService::isWithdrawChannel($info->channel)) {
  1110. Log::error('NO钱包提现回调订单不存在或类型不匹配', [
  1111. 'merchant_order_no' => $params['merchantOrderNo'] ?? '',
  1112. 'order_id' => $info->id ?? null,
  1113. 'order_type' => $info->type ?? null,
  1114. 'channel' => $info->channel ?? null,
  1115. ]);
  1116. return false;
  1117. }
  1118. if (!NoPayService::verifyWithdrawNotify($params)) {
  1119. Log::error('NO钱包提现回调验签失败', [
  1120. 'merchant_order_no' => $params['merchantOrderNo'] ?? '',
  1121. 'app_id' => $params['appId'] ?? '',
  1122. 'signature' => NoPayService::withdrawNotifySignatureDiagnostics($params),
  1123. ]);
  1124. return false;
  1125. }
  1126. if (bccomp(NoPayService::amount($info->amount), NoPayService::amount($params['amount'] ?? 0), 2) !== 0) {
  1127. Log::error('NO钱包提现回调金额不一致', [
  1128. 'merchant_order_no' => $params['merchantOrderNo'] ?? '',
  1129. 'order_amount' => $info->amount,
  1130. 'callback_amount' => $params['amount'] ?? null,
  1131. ]);
  1132. return false;
  1133. }
  1134. self::onSubmitNoPayout($params, $info);
  1135. return true;
  1136. }
  1137. if (($params['userCode'] ?? '') === JdPayService::getMerchantId()) {
  1138. $info = self::findOne(['order_no' => $params['customerOrderCode'] ?? ''])
  1139. ?: static::$MODEL::where('pay_no', $params['orderCode'] ?? '')->first();
  1140. if (!$info || $info->type != self::TYPE_PAYOUT) {
  1141. return false;
  1142. }
  1143. if (!JdPayService::verifyRemitNotify($params)) {
  1144. return false;
  1145. }
  1146. if (bccomp(JdPayService::amount($info->amount), JdPayService::amount($params['amount'] ?? 0), 2) !== 0) {
  1147. return false;
  1148. }
  1149. self::onSubmitJdPayout($params, $info);
  1150. return true;
  1151. }
  1152. // 判断商户号是否一致
  1153. if (($params['merchantNum'] ?? '') == QianBaoService::getMerchantId()) {
  1154. $info = self::findOne(['order_no' => $params['orderNo']]);
  1155. if ($info) {
  1156. // 判断金额是不是正确认
  1157. if ($info->amount != $params['amount']) {
  1158. return false;
  1159. }
  1160. // 验证签名
  1161. $sign = QianBaoService::verifyNotifySign($params['state'], $params['orderNo'], $params['amount']);
  1162. if ($params['sign'] != $sign) {
  1163. return false;
  1164. }
  1165. // 代付
  1166. if ($info->type == self::TYPE_PAYOUT) {
  1167. self::onSubmitPayout($params, $info);
  1168. }
  1169. // 代收
  1170. if ($info->type == self::TYPE_PAY) {
  1171. }
  1172. }
  1173. }
  1174. }
  1175. /**
  1176. * @description: 处理代付订单
  1177. * @param {*} $params
  1178. * @return {*}
  1179. */
  1180. public static function onSubmitJdPayout($params, $info)
  1181. {
  1182. if ($info->status != self::STATUS_PROCESS) {
  1183. return;
  1184. }
  1185. $memberId = $info->member_id;
  1186. $amount = JdPayService::amount($params['amount'] ?? $info->amount);
  1187. $data = [];
  1188. $chat_id = $info->member_id;
  1189. $data['callback_data'] = json_encode($params, JSON_UNESCAPED_UNICODE);
  1190. DB::beginTransaction();
  1191. try {
  1192. if ((string)$params['status'] === JdPayService::REMIT_STATUS_SUCCESS) {
  1193. $data['status'] = self::STATUS_SUCCESS;
  1194. $res = static::$MODEL::where(['id' => $info->id])->update($data);
  1195. AdminRealtime::record('payment_orders', $info->id);
  1196. if ($res) {
  1197. $text = "✅ 提现通知 \n";
  1198. $text .= "提现平台:{$info->bank_name} \n";
  1199. $text .= "收款人:{$info->account} \n";
  1200. $text .= "收款地址:{$info->card_no} \n";
  1201. $text .= "提现金额:{$info->amount} \n";
  1202. $text .= "提现成功,金额已到账,请注意查收!";
  1203. self::notifyUser($chat_id, $text);
  1204. }
  1205. } elseif ((string)$params['status'] === JdPayService::REMIT_STATUS_FAIL) {
  1206. $data['status'] = self::STATUS_FAIL;
  1207. $res = static::$MODEL::where(['id' => $info->id])->update($data);
  1208. AdminRealtime::record('payment_orders', $info->id);
  1209. $wallet = WalletService::findOne(['member_id' => $info->member_id]);
  1210. $balance = $wallet->available_balance;
  1211. $available_balance = bcadd($balance, $amount, 10);
  1212. $wallet->available_balance = $available_balance;
  1213. $wallet->save();
  1214. BalanceLogService::addLog($memberId, $amount, $balance, $available_balance, '三方提现', $info->id, '提现失败退款');
  1215. if ($res) {
  1216. $text = "❌ 提现通知 \n";
  1217. $text .= "提现平台:{$info->bank_name} \n";
  1218. $text .= "收款人:{$info->account} \n";
  1219. $text .= "收款地址:{$info->card_no} \n";
  1220. $text .= "提现金额:{$info->amount} \n";
  1221. $text .= "提现失败,金额已返回钱包,请注意查收!";
  1222. self::notifyUser($chat_id, $text);
  1223. }
  1224. }
  1225. DB::commit();
  1226. } catch (Exception $e) {
  1227. DB::rollBack();
  1228. Log::error('JD提现回调处理异常: ' . $e->getMessage(), $params);
  1229. }
  1230. }
  1231. public static function onSubmitNoPayout($params, $info)
  1232. {
  1233. $notification = null;
  1234. try {
  1235. DB::transaction(function () use ($params, $info, &$notification) {
  1236. $order = PaymentOrder::where('id', $info->id)->lockForUpdate()->first();
  1237. if (!$order || $order->status != self::STATUS_PROCESS) {
  1238. return;
  1239. }
  1240. $order->callback_data = json_encode($params, JSON_UNESCAPED_UNICODE);
  1241. if ((string)$params['state'] === NoPayService::STATE_SUCCESS) {
  1242. $order->status = self::STATUS_SUCCESS;
  1243. $order->save();
  1244. $notification = "✅ 提现通知 \n提现平台:{$order->bank_name} \n收款人:{$order->account} \nNO钱包账号:{$order->card_no} \n提现金额:{$order->amount} \n提现成功,金额已到账,请注意查收!";
  1245. } elseif ((string)$params['state'] === NoPayService::STATE_FAIL) {
  1246. $order->status = self::STATUS_FAIL;
  1247. $order->save();
  1248. $wallet = WalletModel::where('member_id', $order->member_id)->lockForUpdate()->first();
  1249. if (!$wallet) {
  1250. throw new Exception('钱包不存在', HttpStatus::CUSTOM_ERROR);
  1251. }
  1252. $balance = $wallet->available_balance;
  1253. $availableBalance = bcadd($balance, NoPayService::amount($order->amount), 10);
  1254. $wallet->available_balance = $availableBalance;
  1255. $wallet->save();
  1256. BalanceLogService::addLog($order->member_id, $order->amount, $balance, $availableBalance, '三方提现', $order->id, '提现失败退款');
  1257. $notification = "❌ 提现通知 \n提现平台:{$order->bank_name} \n收款人:{$order->account} \nNO钱包账号:{$order->card_no} \n提现金额:{$order->amount} \n提现失败,金额已返回钱包,请注意查收!";
  1258. } else {
  1259. $order->save();
  1260. }
  1261. }, 3);
  1262. if ($notification !== null) {
  1263. self::notifyUser($info->member_id, $notification);
  1264. }
  1265. } catch (Exception $e) {
  1266. Log::channel('payment_error')->error('NO提现回调处理异常', [
  1267. 'order_id' => $info->id,
  1268. 'params' => $params,
  1269. 'error' => $e->getMessage(),
  1270. ]);
  1271. }
  1272. }
  1273. public static function onSubmitZimuPayout($params, $info)
  1274. {
  1275. $notification = null;
  1276. $successStatus = ZimuPayService::isCashChannel($info->channel)
  1277. ? ZimuPayService::CASH_STATUS_SUCCESS
  1278. : ZimuPayService::WITHDRAW_STATUS_SUCCESS;
  1279. $failStatus = ZimuPayService::isCashChannel($info->channel)
  1280. ? ZimuPayService::CASH_STATUS_FAIL
  1281. : ZimuPayService::WITHDRAW_STATUS_FAIL;
  1282. try {
  1283. DB::transaction(function () use ($params, $info, &$notification, $successStatus, $failStatus) {
  1284. $order = PaymentOrder::where('id', $info->id)->lockForUpdate()->first();
  1285. if (!$order || $order->status != self::STATUS_PROCESS) {
  1286. return;
  1287. }
  1288. $order->callback_data = json_encode($params, JSON_UNESCAPED_UNICODE);
  1289. if ((string)$params['status'] === $successStatus) {
  1290. $order->status = self::STATUS_SUCCESS;
  1291. $order->save();
  1292. $accountLabel = ZimuPayService::isCashChannel($order->channel) ? '收款账号' : '808钱包地址';
  1293. $notification = "✅ 提现通知 \n提现平台:{$order->bank_name} \n收款人:{$order->account} \n{$accountLabel}:{$order->card_no} \n提现金额:{$order->amount} \n提现成功,金额已到账,请注意查收!";
  1294. } elseif ((string)$params['status'] === $failStatus) {
  1295. $order->status = self::STATUS_FAIL;
  1296. $order->save();
  1297. $wallet = WalletModel::where('member_id', $order->member_id)->lockForUpdate()->first();
  1298. if (!$wallet) {
  1299. throw new Exception('钱包不存在', HttpStatus::CUSTOM_ERROR);
  1300. }
  1301. $balance = $wallet->available_balance;
  1302. $availableBalance = bcadd($balance, ZimuPayService::amount($order->amount), 10);
  1303. $wallet->available_balance = $availableBalance;
  1304. $wallet->save();
  1305. BalanceLogService::addLog($order->member_id, $order->amount, $balance, $availableBalance, '三方提现', $order->id, '提现失败退款');
  1306. $accountLabel = ZimuPayService::isCashChannel($order->channel) ? '收款账号' : '808钱包地址';
  1307. $notification = "❌ 提现通知 \n提现平台:{$order->bank_name} \n收款人:{$order->account} \n{$accountLabel}:{$order->card_no} \n提现金额:{$order->amount} \n提现失败,金额已返回钱包,请注意查收!";
  1308. } else {
  1309. $order->save();
  1310. }
  1311. }, 3);
  1312. if ($notification !== null) {
  1313. self::notifyUser($info->member_id, $notification);
  1314. }
  1315. } catch (Exception $e) {
  1316. Log::channel('payment_error')->error('ZIMU提现/代付回调处理异常', [
  1317. 'order_id' => $info->id,
  1318. 'params' => $params,
  1319. 'error' => $e->getMessage(),
  1320. ]);
  1321. }
  1322. }
  1323. public static function onSubmitPayout($params, $info)
  1324. {
  1325. $memberId = $info->member_id;
  1326. $amount = $params['amount'];
  1327. $data = [];
  1328. $result = [];
  1329. $chat_id = $info->member_id;
  1330. $data['callback_data'] = json_encode($params, JSON_UNESCAPED_UNICODE);
  1331. DB::beginTransaction();
  1332. try {
  1333. if ($params['state'] == 1) {
  1334. $data['status'] = self::STATUS_SUCCESS;
  1335. $res = static::$MODEL::where(['order_no' => $params['orderNo']])->update($data);
  1336. AdminRealtime::record('payment_orders', $info->id);
  1337. if ($res) {
  1338. $text = "✅ 提现通知 \n";
  1339. $text .= "提现平台:{$info->bank_name} \n";
  1340. $text .= "收款人:{$info->account} \n";
  1341. $text .= "收款卡号:{$info->card_no} \n";
  1342. $text .= "提现金额:{$info->amount} \n";
  1343. $text .= "提现成功,金额已到账,请注意查收!";
  1344. self::notifyUser($chat_id, $text);
  1345. }
  1346. } else {
  1347. $data['status'] = self::STATUS_FAIL;
  1348. $res = static::$MODEL::where(['order_no' => $params['orderNo']])->update($data);
  1349. AdminRealtime::record('payment_orders', $info->id);
  1350. $wallet = WalletService::findOne(['member_id' => $info->member_id]);
  1351. $balance = $wallet->available_balance; // 钱包当前余额
  1352. $available_balance = bcadd($balance, $params['amount'], 10);
  1353. $wallet->available_balance = $available_balance;
  1354. $wallet->save();
  1355. // 记录退款日志
  1356. BalanceLogService::addLog($memberId, $amount, $balance, $available_balance, '三方提现', $info->id, '提现失败退款');
  1357. if ($res) {
  1358. $text = "❌ 提现通知 \n";
  1359. $text .= "提现平台:{$info->bank_name} \n";
  1360. $text .= "收款人:{$info->account} \n";
  1361. $text .= "收款卡号:{$info->card_no} \n";
  1362. $text .= "提现金额:{$info->amount} \n";
  1363. $text .= "提现失败,金额已返回钱包,请注意查收!";
  1364. self::notifyUser($chat_id, $text);
  1365. }
  1366. }
  1367. DB::commit();
  1368. } catch (Exception $e) {
  1369. DB::rollBack();
  1370. // 回滚失败,需要记录告警,人工干预
  1371. Log::error('提现失败回滚异常: ' . $e->getMessage(), $params);
  1372. }
  1373. }
  1374. /**
  1375. * @description: 查询支付订单
  1376. * @param {*} $id
  1377. * @return {*}
  1378. */
  1379. public static function singlePayOrder($id)
  1380. {
  1381. $msg = [];
  1382. $msg['code'] = self::NOT;
  1383. $info = self::findOne(['id' => $id]);
  1384. if ($info && (int)$info->type === self::TYPE_PAY && in_array((int)$info->status, self::PAY_OPEN_STATUSES, true)) {
  1385. if (NoPayService::isRechargeChannel($info->channel)) {
  1386. $ret = NoPayService::queryPayOrder($info->order_no, (string)$info->member_id);
  1387. Log::channel('payment')->info('NO支付查询订单', $ret);
  1388. if (($ret['code'] ?? -1) == 0) {
  1389. $item = $ret['data'] ?? [];
  1390. if ((string)($item['state'] ?? '') === NoPayService::STATE_SUCCESS) {
  1391. $processed = self::applyPayCallback(
  1392. $info,
  1393. NoPayService::amount($info->amount),
  1394. NoPayService::STATE_SUCCESS,
  1395. NoPayService::STATE_SUCCESS,
  1396. NoPayService::STATE_FAIL,
  1397. $ret
  1398. );
  1399. $msg['code'] = $processed ? self::YES : self::NOT;
  1400. $msg['msg'] = $processed ? '支付成功' : '订单已处理';
  1401. } elseif ((string)($item['state'] ?? '') === NoPayService::STATE_FAIL) {
  1402. self::applyPayCallback(
  1403. $info,
  1404. NoPayService::amount($info->amount),
  1405. NoPayService::STATE_FAIL,
  1406. NoPayService::STATE_SUCCESS,
  1407. NoPayService::STATE_FAIL,
  1408. $ret
  1409. );
  1410. $msg['msg'] = '支付失败';
  1411. } else {
  1412. $msg['msg'] = '支付中';
  1413. }
  1414. } else {
  1415. $msg['msg'] = '查询失败:' . ($ret['msg'] ?? '');
  1416. }
  1417. return $msg;
  1418. }
  1419. if (ZimuPayService::isRechargeChannel($info->channel)) {
  1420. $ret = ZimuPayService::queryPayOrder($info->order_no);
  1421. Log::channel('payment')->info('ZIMU支付查询订单', $ret);
  1422. if (($ret['code'] ?? -1) == 0) {
  1423. $item = $ret['data'] ?? [];
  1424. if ((string)($item['status'] ?? '') === ZimuPayService::PAY_STATUS_SUCCESS) {
  1425. $processed = self::applyPayCallback(
  1426. $info,
  1427. ZimuPayService::amount($info->amount),
  1428. ZimuPayService::PAY_STATUS_SUCCESS,
  1429. ZimuPayService::PAY_STATUS_SUCCESS,
  1430. ZimuPayService::PAY_STATUS_FAIL,
  1431. $ret
  1432. );
  1433. $msg['code'] = $processed ? self::YES : self::NOT;
  1434. $msg['msg'] = $processed ? '支付成功' : '订单已处理';
  1435. } elseif ((string)($item['status'] ?? '') === ZimuPayService::PAY_STATUS_FAIL) {
  1436. self::applyPayCallback(
  1437. $info,
  1438. ZimuPayService::amount($info->amount),
  1439. ZimuPayService::PAY_STATUS_FAIL,
  1440. ZimuPayService::PAY_STATUS_SUCCESS,
  1441. ZimuPayService::PAY_STATUS_FAIL,
  1442. $ret
  1443. );
  1444. $msg['msg'] = '支付失败';
  1445. } else {
  1446. $msg['msg'] = '支付中';
  1447. }
  1448. } else {
  1449. $msg['msg'] = '查询失败:' . ($ret['msg'] ?? '');
  1450. }
  1451. return $msg;
  1452. }
  1453. if (JdPayService::isChannel($info->channel)) {
  1454. $ret = JdPayService::queryPayOrder($info->pay_no ?? '', $info->order_no);
  1455. Log::channel('payment')->info('JD支付查询订单', $ret);
  1456. if (($ret['code'] ?? 0) == 200) {
  1457. $item = $ret['data'] ?? [];
  1458. if ((string)($item['status'] ?? '') === JdPayService::PAY_STATUS_SUCCESS) {
  1459. $processed = self::applyPayCallback($info, JdPayService::amount($info->amount), JdPayService::PAY_STATUS_SUCCESS,
  1460. JdPayService::PAY_STATUS_SUCCESS, JdPayService::PAY_STATUS_FAIL, $ret);
  1461. $msg['code'] = $processed ? self::YES : self::NOT;
  1462. $msg['msg'] = $processed ? '支付成功' : '订单已处理';
  1463. } else {
  1464. $msg['msg'] = '支付中';
  1465. }
  1466. } else {
  1467. $msg['msg'] = '查询失败:' . ($ret['message'] ?? '');
  1468. }
  1469. return $msg;
  1470. }
  1471. $ret = SanJinService::queryOrder($info->order_no);
  1472. Log::error('三斤支付查询订单:', $ret);
  1473. if ($ret['code'] == 0) {
  1474. $item = [];
  1475. $item['state'] = $ret['data']['state'];
  1476. if ($ret['data']['state'] == 1) {
  1477. $processed = self::applyPayCallback($info, (string)$info->amount, '1', '1', null, $ret);
  1478. $msg['code'] = $processed ? self::YES : self::NOT;
  1479. $msg['msg'] = $processed ? '支付成功' : '订单已处理';
  1480. } else {
  1481. $msg['msg'] = '支付中';
  1482. }
  1483. } else {
  1484. $msg['msg'] = '查询失败:' . $ret['message'];
  1485. }
  1486. } else {
  1487. $msg['msg'] = '该状态无法查询';
  1488. }
  1489. return $msg;
  1490. }
  1491. private static function assertJdBalanceEnough($amount, array $context = []): void
  1492. {
  1493. $ret = JdPayService::balance();
  1494. Log::channel('payment')->info('JD余额查询', $context + [
  1495. 'amount' => $amount,
  1496. 'response' => $ret,
  1497. ]);
  1498. if (($ret['code'] ?? 0) != 200) {
  1499. $logContext = $context + [
  1500. 'amount' => $amount,
  1501. 'response' => $ret,
  1502. ];
  1503. Log::channel('payment_error')->error('JD余额查询失败', $logContext);
  1504. Log::error('JD余额查询失败', $logContext);
  1505. throw new Exception($ret['message'] ?? 'JD余额查询失败', HttpStatus::CUSTOM_ERROR);
  1506. }
  1507. $balance = $ret['data']['balance'] ?? null;
  1508. if ($balance === null || bccomp((string)$balance, JdPayService::amount($amount), 2) < 0) {
  1509. $logContext = $context + [
  1510. 'amount' => $amount,
  1511. 'balance' => $balance,
  1512. 'response' => $ret,
  1513. ];
  1514. Log::channel('payment_error')->error('JD商户余额不足', $logContext);
  1515. Log::error('JD商户余额不足', $logContext);
  1516. throw new Exception('JD商户余额不足', HttpStatus::CUSTOM_ERROR);
  1517. }
  1518. }
  1519. private static function assertZimuBalanceEnough($amount, array $context = []): void
  1520. {
  1521. $ret = ZimuPayService::balance();
  1522. Log::channel('payment')->info('ZIMU余额查询', $context + [
  1523. 'amount' => $amount,
  1524. 'response' => $ret,
  1525. ]);
  1526. if (($ret['code'] ?? -1) != 0) {
  1527. $logContext = $context + [
  1528. 'amount' => $amount,
  1529. 'response' => $ret,
  1530. ];
  1531. Log::channel('payment_error')->error('ZIMU余额查询失败', $logContext);
  1532. Log::error('ZIMU余额查询失败', $logContext);
  1533. throw new Exception($ret['msg'] ?? 'ZIMU余额查询失败', HttpStatus::CUSTOM_ERROR);
  1534. }
  1535. $balance = $ret['data']['balance'] ?? null;
  1536. if ($balance === null || bccomp((string)$balance, ZimuPayService::amount($amount), 2) < 0) {
  1537. $logContext = $context + [
  1538. 'amount' => $amount,
  1539. 'balance' => $balance,
  1540. 'response' => $ret,
  1541. ];
  1542. Log::channel('payment_error')->error('ZIMU商户余额不足', $logContext);
  1543. Log::error('ZIMU商户余额不足', $logContext);
  1544. throw new Exception('ZIMU商户余额不足', HttpStatus::CUSTOM_ERROR);
  1545. }
  1546. }
  1547. private static function notifyUser($chatId, string $text): void
  1548. {
  1549. if ((int)User::where('member_id', $chatId)->value('from') !== -1) {
  1550. return;
  1551. }
  1552. try {
  1553. self::sendMessage($chatId, $text);
  1554. } catch (\Throwable $e) {
  1555. Log::channel('payment_error')->error('支付订单用户通知失败', [
  1556. 'chat_id' => $chatId,
  1557. 'error' => $e->getMessage(),
  1558. ]);
  1559. }
  1560. }
  1561. public static function syncPayOrder()
  1562. {
  1563. $list = static::$MODEL::where('state', 0)->where('type', self::TYPE_PAY)->take(100)->get();
  1564. // foreach($list->toArray() as $k => $v){
  1565. // $item= [];
  1566. // if($v['status'] == self::STATUS_SUCCESS){
  1567. // $item['state'] = 1;
  1568. // static::$MODEL::where(['id'=>$v['id']])->update($item);
  1569. // }else{
  1570. // $ret = SanJinService::queryOrder($v['order_no']);
  1571. // var_dump($ret);
  1572. // if($ret['code'] == 0){
  1573. // $item['state'] = $ret['data']['state'];
  1574. // static::$MODEL::where(['id'=>$v['id']])->update($item);
  1575. // }
  1576. // }
  1577. // }
  1578. }
  1579. }