PayTrait.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. <?php
  2. namespace App\Http\Controllers\Api\Traits;
  3. use App\Jobs\System\CheckBatchCahceDataLossJob;
  4. use App\Models\Shop\WxShopOrder;
  5. use App\Models\User\WxUser;
  6. use App\Models\User\WxUserWithdrawal;
  7. use App\Models\WxOrder;
  8. use App\Wen\Utils\Settings;
  9. use App\Wen\Utils\UserUtils;
  10. use Yansongda\Pay\Pay;
  11. use App\Wen\Utils\Utils;
  12. use App\Lib\Pay\EPay;
  13. use Illuminate\Support\Facades\Cache;
  14. use SimpleSoftwareIO\QrCode\Facades\QrCode;
  15. use Yansongda\Pay\Plugin\Wechat\Fund\Transfer\QueryBatchIdPlugin;
  16. trait PayTrait{
  17. public function transferBatchQuery($provider, $batch_id, $need_query_detail = false){
  18. if($provider == 'wxpay'){
  19. $config = json_decode(Cache::get('app_pay_config'), true);
  20. if(_empty_($config)){
  21. CheckBatchCahceDataLossJob::dispatch();
  22. }
  23. $pay = Pay::wechat($config);
  24. $plugins = $pay->mergeCommonPlugins([QueryBatchIdPlugin::class]);
  25. // need_query_detail:【是否查询转账明细单】 true-是;false-否,默认否。商户可选择是否查询指定状态的转账明细单,当转账批次单状态为“FINISHED”(已完成)时,才会返回满足条件的转账明细单
  26. $result = $pay->pay($plugins ,['batch_id' => $batch_id, 'need_query_detail' => true, 'detail_status' => 'ALL']);
  27. $result_arr = $result->toArray();
  28. if(!_empty_(_array_key($result_arr, 'transfer_batch', ''))){
  29. // 查询成功
  30. return $result_arr;
  31. }else{
  32. if(_array_key($result_arr, 'code', '') == 'NOT_FOUND'){
  33. WxUserWithdrawal::whereIn('wx_batch_id', $batch_id)->where('state', 3)->update([
  34. 'state' => 2,
  35. 'refuse_tip' => '查询的转账批次单不存在',
  36. ]);
  37. }else{
  38. _logger_(__file__, __line__, '微信商家转账到零钱-查询批次单失败-批次号'.$batch_id.'-:'.json_encode($result_arr, JSON_UNESCAPED_UNICODE));
  39. UserUtils::assistant_notice('admin', '微信商家转账到零钱失败-查询批次单失败-批次号'.$batch_id.'-:'.json_encode($result_arr, JSON_UNESCAPED_UNICODE));
  40. }
  41. return null;
  42. }
  43. }else{
  44. throw new \Exception('暂时未实现');
  45. }
  46. }
  47. /**
  48. * @param $provider
  49. * @param $pay_type
  50. * @param $list
  51. * 'transfer_detail_list' => [
  52. [
  53. 'out_detail_no' => (string)(time() + 5), // 商家明细单号
  54. 'transfer_amount' => 0.1 * 100, // 转账金额
  55. 'transfer_remark' => '商家付款到零钱', // 单条转账备注(微信用户会收到该备注)
  56. 'openid' => 'ozxxxxxxxxxx', // 转账用户的 openid
  57. ],
  58. ],
  59. * @param $batch_name 余额提现
  60. * @param $batch_remark 平台转账到用户微信零钱
  61. * @throws \Exception
  62. */
  63. public function transferBatch($provider, $pay_type, $list, $batch_name = '余额提现', $batch_remark = '平台转账到用户微信零钱'){
  64. $total_amount = 0;
  65. if(!_empty_($list)){
  66. foreach ($list as $item){
  67. $total_amount += $item['transfer_amount'];
  68. }
  69. }
  70. if($provider == 'wxpay'){
  71. $config = json_decode(Cache::get('app_pay_config'), true);
  72. if(_empty_($config)){
  73. CheckBatchCahceDataLossJob::dispatch();
  74. }
  75. if($pay_type == 'app'){
  76. $appid = Settings::get('app_app_id', '');
  77. }else if($pay_type == 'mp'){
  78. $appid = Settings::get('mp_app_id', '');
  79. }else if($pay_type == 'mini'){
  80. $appid = Settings::get('app_id', '');
  81. }else{
  82. $appid = Settings::get('app_id', '');
  83. }
  84. $result = Pay::wechat($config)->transfer([
  85. 'appid' => $appid, // 微信小程序的app_id
  86. 'out_batch_no' => (string)time(), // 商家批次单号
  87. 'batch_name' => $batch_name, // 该笔批量转账的名称
  88. 'batch_remark' => $batch_remark, // 转账说明
  89. 'total_amount' => (int)$total_amount, // 转账金额,单位:分
  90. 'total_num' => count($list), // 转账总笔数
  91. 'transfer_detail_list' => $list,
  92. ]);
  93. $result_arr = $result->toArray();
  94. if(!_empty_(_array_key($result_arr, 'batch_id', ''))){
  95. // 转账成功
  96. return _array_key($result_arr, 'batch_id', '');
  97. }else{
  98. if(_array_key($result_arr, 'code', '') == 'NOT_ENOUGH'){
  99. throw new \Exception('资金不足');
  100. }
  101. _logger_(__file__, __line__, '微信商家转账到零钱(总'. ($total_amount/100) .'元)失败:'.json_encode($result_arr, JSON_UNESCAPED_UNICODE));
  102. UserUtils::assistant_notice('admin', '微信商家转账到零钱(总'. ($total_amount/100) .'元)失败:'.json_encode($result_arr, JSON_UNESCAPED_UNICODE));
  103. return null;
  104. }
  105. }else if($provider == 'alipay'){
  106. throw new \Exception('暂时未实现');
  107. $config = json_decode(Cache::get('app_pay_config'), true);
  108. if(_empty_($config)){
  109. CheckBatchCahceDataLossJob::dispatch();
  110. }
  111. if($pay_type == 'app'){
  112. }else if($pay_type == 'mp'){
  113. }else if($pay_type == 'mini'){
  114. }
  115. $order = [
  116. 'out_biz_no' => time(),
  117. 'trans_amount' => 0.1,
  118. 'product_code' => 'TRANS_ACCOUNT_NO_PWD',
  119. 'payee_info' => [
  120. 'identity' => 'ghdhjw7124@sandbox.com',
  121. 'name' => '张三',
  122. 'identity_type' => 'ALIPAY_LOGON_ID',
  123. ],
  124. 'remark'=>'赔付金'//相当于转账里面的备注
  125. ];
  126. $result = Pay::alipay($config)->transfer($order);
  127. }
  128. }
  129. /**
  130. * @param $uid
  131. * @param $provider
  132. * @param $pay_type
  133. * @param $total_fee
  134. * @param $body
  135. * @param $out_trade_no
  136. * @param int $order_type 1: 普通订单 2:商城订单
  137. * @return \Illuminate\Http\JsonResponse
  138. */
  139. public function payHandler($uid, $provider, $pay_type, $total_fee, $body, $out_trade_no, $order_type = 1){
  140. global $__MINI_GLOBAL_DEVICE__;
  141. if($__MINI_GLOBAL_DEVICE__ == 'h5'){
  142. if($provider == 'wxpay'){
  143. if($pay_type != 'mp' && $pay_type != 'h5'){
  144. $pay_type = 'h5';
  145. }
  146. }else{
  147. $pay_type = 'h5';
  148. }
  149. }
  150. if($provider == 'wxpay'){
  151. $config = json_decode(Cache::get('app_pay_config'), true);
  152. if(_empty_($config)){
  153. CheckBatchCahceDataLossJob::dispatch();
  154. }
  155. if($order_type == 1){
  156. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'wx_order_');
  157. }else{
  158. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'wx_shoporder_');
  159. }
  160. $config['wechat']['default']['return_url'] = route('pay.return', ['out_trade_no'=>$outTradeNoPre]);
  161. // 微信支付
  162. if($pay_type == 'app'){
  163. // $openid = WxUser::where('id', $uid)->value('weixin_app_openid');
  164. // 支付
  165. $order = [
  166. 'out_trade_no' => $out_trade_no,
  167. 'description' => $body,
  168. 'amount' => [
  169. 'total' => (int)($total_fee * 100),
  170. ],
  171. ];
  172. $pay_data = Pay::wechat($config)->app($order);
  173. $pay_data['query_state_no'] = $outTradeNoPre;
  174. if($order_type == 1){
  175. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'app', 'order_serial_platform'=>0]);
  176. }else{
  177. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'app', 'serial_platform'=>0]);
  178. }
  179. return $this->success($pay_data);
  180. }else if($pay_type == 'h5'){
  181. global $__MINI_GLOBAL_IP__;
  182. $order = [
  183. 'out_trade_no' => $out_trade_no,
  184. 'description' => $body,
  185. 'amount' => [
  186. 'total' => (int)($total_fee * 100),
  187. ],
  188. 'scene_info' => [
  189. 'payer_client_ip' => $__MINI_GLOBAL_IP__,
  190. 'h5_info' => [
  191. 'type' => 'Wap',
  192. ]
  193. ],
  194. ];
  195. $pay_data = Pay::wechat($config)->wap($order);
  196. $h5_url = $pay_data->h5_url;
  197. $cache_html = '<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="refresh" content="0;url='.$h5_url.'"> <title>Redirecting</title> </head> <body></body> </html>';
  198. $r = Cache::put($outTradeNoPre, $cache_html, $seconds = 300);
  199. if($r){
  200. if($order_type == 1){
  201. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'h5', 'order_serial_platform'=>0]);
  202. }else{
  203. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'h5', 'serial_platform'=>0]);
  204. }
  205. return $this->success(['direct'=>route('pay.direct', ['out_trade_no'=>$outTradeNoPre]), 'query_state_no'=>$outTradeNoPre]);
  206. }else{
  207. return $this->fail(200005, ['msg'=>'系统写入缓存时失败,请联系管理员']);
  208. }
  209. }else if($pay_type == 'yi') {
  210. $epay_config = json_decode(Cache::get('yi_pay_config'), true);
  211. if(_empty_($epay_config)){
  212. CheckBatchCahceDataLossJob::dispatch();
  213. }
  214. if($order_type == 1){
  215. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'yi_order_');
  216. }else{
  217. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'yi_shoporder_');
  218. }
  219. $return_url = route('pay.return', ['out_trade_no' => $outTradeNoPre]);
  220. $epay = new EPay();
  221. $epay->key($epay_config['app_secret']);
  222. // 发起订单
  223. try {
  224. $pay_data = $epay->pid($epay_config['app_id'])
  225. ->url($epay_config['provider_url'])
  226. ->outTradeNo($out_trade_no)
  227. ->type('wxpay')
  228. ->notifyUrl($epay_config['notify_url'])
  229. ->returnUrl($return_url)
  230. ->money($total_fee)
  231. ->name($body)
  232. ->sitename($epay_config['site'])
  233. ->submit()
  234. ->getHtmlForm();
  235. $r = Cache::put($outTradeNoPre, Utils::payDirectHtmlForApp('', $pay_data), $seconds = 300);
  236. if ($r) {
  237. if($order_type == 1){
  238. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'yi', 'order_serial_platform'=>0]);
  239. }else{
  240. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'yi', 'serial_platform'=>0]);
  241. }
  242. return $this->success(['direct' => route('pay.direct', ['out_trade_no' => $outTradeNoPre]), 'query_state_no' => $outTradeNoPre]);
  243. } else {
  244. return $this->fail(200005, ['msg' => '系统写入缓存时失败,请联系管理员']);
  245. }
  246. } catch (\Exception $e) {
  247. _logger_(__file__, __line__, $e->getMessage());
  248. return $this->fail(200006, ['msg' => '易支付生成订单出错']);
  249. }
  250. }else if($pay_type == 'code' || $pay_type == 'code_pc') {
  251. // native支付
  252. $order = [
  253. 'out_trade_no' => $out_trade_no,
  254. 'description' => $body,
  255. 'amount' => [
  256. 'total' => (int)($total_fee * 100),
  257. ],
  258. ];
  259. $pay_data = Pay::wechat($config)->scan($order);
  260. if($pay_data && $pay_data->code_url){
  261. if($order_type == 1){
  262. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>$pay_type, 'order_serial_platform'=>0]);
  263. }else{
  264. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>$pay_type, 'serial_platform'=>0]);
  265. }
  266. return $this->success(['url'=>$pay_data->code_url, 'code'=>QrCode::size(300)->generate($pay_data->code_url), 'query_state_no'=>$outTradeNoPre]);
  267. }
  268. return $this->fail(200006, $pay_data);
  269. }else if($pay_type == 'mp') {
  270. $order = [
  271. 'out_trade_no' => $out_trade_no,
  272. 'description' => $body,
  273. 'amount' => [
  274. 'total' => (int)($total_fee * 100),
  275. ],
  276. 'payer' => [
  277. 'openid' => WxUser::where('id', $uid)->value('weixin_mp_openid'),
  278. ],
  279. ];
  280. _logger_(__file__, __line__, '111111111111111111');
  281. try {
  282. $result = Pay::wechat($config)->mp($order);
  283. if($result && $result->paySign){
  284. if($order_type == 1){
  285. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'mp', 'order_serial_platform'=>0]);
  286. }else{
  287. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'mp', 'serial_platform'=>0]);
  288. }
  289. // todo优化: 返回二维码,以及url 微信文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/jsapi-transfer-payment.html
  290. _logger_(__file__, __line__, $result->toArray());
  291. return $this->success([
  292. 'appId' => $result->appId,
  293. 'timeStamp' => $result->timeStamp,
  294. 'nonceStr' => $result->nonceStr,
  295. 'package' => $result->package,
  296. 'signType' => $result->signType,
  297. 'paySign' => $result->paySign,
  298. 'prepayid' => $result->prepayid,
  299. 'query_state_no' => $outTradeNoPre
  300. ]);
  301. }
  302. }catch (\Exception $e){
  303. _logger_(__file__, __line__, $e->getMessage());
  304. return $this->fail(200006);
  305. }
  306. }else if($pay_type == 'mini') {
  307. $order = [
  308. 'out_trade_no' => $out_trade_no,
  309. 'description' => $body,
  310. 'amount' => [
  311. 'total' => (int)($total_fee * 100),
  312. 'currency' => 'CNY',
  313. ],
  314. 'payer' => [
  315. 'openid' => WxUser::where('id', $uid)->value('weixin_openid'),
  316. ],
  317. ];
  318. try {
  319. $result = Pay::wechat($config)->mini($order);
  320. dd(__line__);
  321. if($result && $result->paySign){
  322. if($order_type == 1){
  323. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'mini', 'order_serial_platform'=>0]);
  324. }else{
  325. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'mini', 'serial_platform'=>0]);
  326. }
  327. // todo优化: 返回二维码,以及url 微信文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/jsapi-transfer-payment.html
  328. return $this->success([
  329. 'appId' => $result->appId,
  330. 'timeStamp' => $result->timeStamp,
  331. 'nonceStr' => $result->nonceStr,
  332. 'package' => $result->package,
  333. 'signType' => $result->signType,
  334. 'paySign' => $result->paySign,
  335. 'query_state_no' => $outTradeNoPre
  336. ]);
  337. }
  338. }catch (\Exception $e){
  339. _logger_(__file__, __line__, $e->getMessage());
  340. return $this->fail(200006);
  341. }
  342. }
  343. }else if($provider == 'alipay'){
  344. $config = json_decode(Cache::get('app_pay_config'), true);
  345. if(_empty_($config)){
  346. CheckBatchCahceDataLossJob::dispatch();
  347. }
  348. if($order_type == 1){
  349. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'ali_order_');
  350. }else{
  351. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'ali_shoporder_');
  352. }
  353. $config['alipay']['default']['return_url'] = route('pay.return', ['out_trade_no'=>$outTradeNoPre]);
  354. $config['alipay']['default']['notify_url'] = env('APP_URL'). '/api/v1/app/ali/notify/app';
  355. // 支付宝支付
  356. if($pay_type == 'app'){
  357. $order = [
  358. 'out_trade_no' => $out_trade_no,
  359. 'total_amount' => $total_fee,
  360. 'subject' => $body,
  361. ];
  362. if($order_type == 1){
  363. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'app', 'order_serial_platform'=>1]);
  364. }else{
  365. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'app', 'serial_platform'=>1]);
  366. }
  367. return Pay::alipay($config)->app($order);
  368. // $pay_data->query_state_no = $outTradeNoPre;
  369. }else if($pay_type == 'h5'){
  370. $config['alipay']['default']['notify_url'] = env('APP_URL'). '/api/v1/app/ali/notify/wap';
  371. $order = [
  372. 'out_trade_no' => $out_trade_no,
  373. 'total_amount' => $total_fee,
  374. 'subject' => $body,
  375. 'quit_url' => env('APP_URL'),
  376. ];
  377. $pay_data = Pay::alipay($config)->wap($order);
  378. $pay_data = $pay_data->getBody();
  379. $r = Cache::put($outTradeNoPre, Utils::payDirectHtmlForApp('', $pay_data), $seconds = 300);
  380. if($r){
  381. if($order_type == 1){
  382. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'h5', 'order_serial_platform'=>1]);
  383. }else{
  384. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'h5', 'serial_platform'=>1]);
  385. }
  386. return $this->success(['direct'=>route('pay.direct', ['out_trade_no'=>$outTradeNoPre]), 'query_state_no'=>$outTradeNoPre]);
  387. }else{
  388. return $this->fail(200005, ['msg'=>'系统写入缓存时失败,请联系管理员']);
  389. }
  390. }else if($pay_type == 'yi') {
  391. $epay_config = json_decode(Cache::get('yi_pay_config'), true);
  392. if(_empty_($epay_config)){
  393. CheckBatchCahceDataLossJob::dispatch();
  394. }
  395. if($order_type == 1){
  396. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'yi_order_');
  397. }else{
  398. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'yi_shoporder_');
  399. }
  400. $return_url = route('pay.return', ['out_trade_no' => $outTradeNoPre]);
  401. $epay = new EPay();
  402. $epay->key($epay_config['app_secret']);
  403. // 发起订单
  404. try {
  405. $pay_data = $epay->pid($epay_config['app_id'])
  406. ->url($epay_config['provider_url'])
  407. ->outTradeNo($out_trade_no)
  408. ->type('alipay')
  409. ->notifyUrl($epay_config['notify_url'])
  410. ->returnUrl($return_url)
  411. ->money($total_fee)
  412. ->name($body)
  413. ->sitename($epay_config['site'])
  414. ->submit()
  415. ->getHtmlForm();
  416. $r = Cache::put($outTradeNoPre, Utils::payDirectHtmlForApp('', $pay_data), $seconds = 300);
  417. if ($r) {
  418. if($order_type == 1){
  419. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'yi', 'order_serial_platform'=>1]);
  420. }else{
  421. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'yi', 'serial_platform'=>1]);
  422. }
  423. return $this->success(['direct' => route('pay.direct', ['out_trade_no' => $outTradeNoPre]), 'query_state_no' => $outTradeNoPre]);
  424. } else {
  425. return $this->fail(200005, ['msg' => '系统写入缓存时失败,请联系管理员']);
  426. }
  427. } catch (\Exception $e) {
  428. _logger_(__file__, __line__, $e->getMessage());
  429. return $this->fail(200006, ['msg' => '易支付生成订单出错']);
  430. }
  431. }else if($pay_type == 'code' || $pay_type == 'code_pc') {
  432. $order = [
  433. 'out_trade_no' => $out_trade_no,
  434. 'total_amount' => $total_fee,
  435. 'subject' => $body,
  436. ];
  437. $pay_data = Pay::alipay($config)->scan($order);
  438. if($pay_data && $pay_data->code_url){
  439. if($order_type == 1){
  440. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>$pay_type, 'order_serial_platform'=>1]);
  441. }else{
  442. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>$pay_type, 'serial_platform'=>1]);
  443. }
  444. return $this->success(['url'=>$pay_data->code_url, 'code'=>QrCode::size(300)->generate($pay_data->code_url), 'query_state_no'=>$outTradeNoPre]);
  445. }
  446. return $this->fail(200006, $pay_data);
  447. }
  448. }
  449. }
  450. }