PayTrait.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  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. if($result && $result->paySign){
  321. if($order_type == 1){
  322. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'mini', 'order_serial_platform'=>0]);
  323. }else{
  324. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'mini', 'serial_platform'=>0]);
  325. }
  326. // todo优化: 返回二维码,以及url 微信文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/jsapi-transfer-payment.html
  327. return $this->success([
  328. 'appId' => $result->appId,
  329. 'timeStamp' => $result->timeStamp,
  330. 'nonceStr' => $result->nonceStr,
  331. 'package' => $result->package,
  332. 'signType' => $result->signType,
  333. 'paySign' => $result->paySign,
  334. 'query_state_no' => $outTradeNoPre
  335. ]);
  336. }
  337. }catch (\Exception $e){
  338. _logger_(__file__, __line__, $e->getMessage());
  339. return $this->fail(200006);
  340. }
  341. }
  342. }else if($provider == 'alipay'){
  343. $config = json_decode(Cache::get('app_pay_config'), true);
  344. if(_empty_($config)){
  345. CheckBatchCahceDataLossJob::dispatch();
  346. }
  347. if($order_type == 1){
  348. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'ali_order_');
  349. }else{
  350. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'ali_shoporder_');
  351. }
  352. $config['alipay']['default']['return_url'] = route('pay.return', ['out_trade_no'=>$outTradeNoPre]);
  353. $config['alipay']['default']['notify_url'] = env('APP_URL'). '/api/v1/app/ali/notify/app';
  354. // 支付宝支付
  355. if($pay_type == 'app'){
  356. $order = [
  357. 'out_trade_no' => $out_trade_no,
  358. 'total_amount' => $total_fee,
  359. 'subject' => $body,
  360. ];
  361. if($order_type == 1){
  362. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'app', 'order_serial_platform'=>1]);
  363. }else{
  364. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'app', 'serial_platform'=>1]);
  365. }
  366. return Pay::alipay($config)->app($order);
  367. // $pay_data->query_state_no = $outTradeNoPre;
  368. }else if($pay_type == 'h5'){
  369. $config['alipay']['default']['notify_url'] = env('APP_URL'). '/api/v1/app/ali/notify/wap';
  370. $order = [
  371. 'out_trade_no' => $out_trade_no,
  372. 'total_amount' => $total_fee,
  373. 'subject' => $body,
  374. 'quit_url' => env('APP_URL'),
  375. ];
  376. $pay_data = Pay::alipay($config)->wap($order);
  377. $pay_data = $pay_data->getBody();
  378. $r = Cache::put($outTradeNoPre, Utils::payDirectHtmlForApp('', $pay_data), $seconds = 300);
  379. if($r){
  380. if($order_type == 1){
  381. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'h5', 'order_serial_platform'=>1]);
  382. }else{
  383. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'h5', 'serial_platform'=>1]);
  384. }
  385. return $this->success(['direct'=>route('pay.direct', ['out_trade_no'=>$outTradeNoPre]), 'query_state_no'=>$outTradeNoPre]);
  386. }else{
  387. return $this->fail(200005, ['msg'=>'系统写入缓存时失败,请联系管理员']);
  388. }
  389. }else if($pay_type == 'yi') {
  390. $epay_config = json_decode(Cache::get('yi_pay_config'), true);
  391. if(_empty_($epay_config)){
  392. CheckBatchCahceDataLossJob::dispatch();
  393. }
  394. if($order_type == 1){
  395. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'yi_order_');
  396. }else{
  397. $outTradeNoPre = Utils::outTradeNoPre($out_trade_no, 'yi_shoporder_');
  398. }
  399. $return_url = route('pay.return', ['out_trade_no' => $outTradeNoPre]);
  400. $epay = new EPay();
  401. $epay->key($epay_config['app_secret']);
  402. // 发起订单
  403. try {
  404. $pay_data = $epay->pid($epay_config['app_id'])
  405. ->url($epay_config['provider_url'])
  406. ->outTradeNo($out_trade_no)
  407. ->type('alipay')
  408. ->notifyUrl($epay_config['notify_url'])
  409. ->returnUrl($return_url)
  410. ->money($total_fee)
  411. ->name($body)
  412. ->sitename($epay_config['site'])
  413. ->submit()
  414. ->getHtmlForm();
  415. $r = Cache::put($outTradeNoPre, Utils::payDirectHtmlForApp('', $pay_data), $seconds = 300);
  416. if ($r) {
  417. if($order_type == 1){
  418. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>'yi', 'order_serial_platform'=>1]);
  419. }else{
  420. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>'yi', 'serial_platform'=>1]);
  421. }
  422. return $this->success(['direct' => route('pay.direct', ['out_trade_no' => $outTradeNoPre]), 'query_state_no' => $outTradeNoPre]);
  423. } else {
  424. return $this->fail(200005, ['msg' => '系统写入缓存时失败,请联系管理员']);
  425. }
  426. } catch (\Exception $e) {
  427. _logger_(__file__, __line__, $e->getMessage());
  428. return $this->fail(200006, ['msg' => '易支付生成订单出错']);
  429. }
  430. }else if($pay_type == 'code' || $pay_type == 'code_pc') {
  431. $order = [
  432. 'out_trade_no' => $out_trade_no,
  433. 'total_amount' => $total_fee,
  434. 'subject' => $body,
  435. ];
  436. $pay_data = Pay::alipay($config)->scan($order);
  437. if($pay_data && $pay_data->code_url){
  438. if($order_type == 1){
  439. WxOrder::where('order_number', $out_trade_no)->update(['order_serial_platform_type'=>$pay_type, 'order_serial_platform'=>1]);
  440. }else{
  441. WxShopOrder::where('order_id', $out_trade_no)->update(['serial_platform_type'=>$pay_type, 'serial_platform'=>1]);
  442. }
  443. return $this->success(['url'=>$pay_data->code_url, 'code'=>QrCode::size(300)->generate($pay_data->code_url), 'query_state_no'=>$outTradeNoPre]);
  444. }
  445. return $this->fail(200006, $pay_data);
  446. }
  447. }
  448. }
  449. }