PaymentService.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2017~2021 LARAVEL研发中心
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://www.laravel.cn
  8. // +----------------------------------------------------------------------
  9. // | Author: laravel开发员 <laravel.qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace App\Services;
  12. use App\Models\AccountLogModel;
  13. use App\Models\BalanceLogModel;
  14. use App\Models\DepositModel;
  15. use App\Models\MemberModel;
  16. use App\Models\MessageModel;
  17. use App\Models\PaymentModel;
  18. use App\Services\Api\MessageService;
  19. use Illuminate\Support\Facades\DB;
  20. use Yansongda\Pay\Pay;
  21. use Yansongda\Pay\Provider\Wechat;
  22. /**
  23. * 支付-服务类
  24. * @author laravel开发员
  25. * @since 2020/11/11
  26. * Class PaymentService
  27. * @package App\Services\Api
  28. */
  29. class PaymentService extends BaseService
  30. {
  31. protected static $instance = null;
  32. private $config = [];
  33. /**
  34. * 构造函数
  35. * @author laravel开发员
  36. * @since 2020/11/11
  37. * PaymentService constructor.
  38. */
  39. public function __construct()
  40. {
  41. $this->model = new PaymentModel();
  42. }
  43. /**
  44. * 静态入口
  45. * @return static|null
  46. */
  47. public static function make()
  48. {
  49. if (!self::$instance) {
  50. self::$instance = (new static());
  51. }
  52. return self::$instance;
  53. }
  54. /**
  55. * 创建支付
  56. * @param string $scene 场景,deposit-保证金,depositRefund-保证金退款,withdraw-提现
  57. * @param int $payType
  58. * @return false|\Yansongda\Pay\Provider\Alipay|Wechat
  59. */
  60. public function createPay($scene, $payType = 10)
  61. {
  62. $config = ConfigService::make()->getConfigOptionByGroup(6);
  63. if ($payType == 10) {
  64. $appid = isset($config['wxpay_appid']) ? $config['wxpay_appid'] : '';
  65. $mchid = isset($config['wxpay_mchd']) ? $config['wxpay_mchd'] : '';
  66. $secretV3Key = isset($config['wxpay_key_v3']) ? $config['wxpay_key_v3'] : '';
  67. $wxpaySecretCert = isset($config['wxpay_secret_cert']) ? $config['wxpay_secret_cert'] : '';
  68. $wxpayPublicCert = isset($config['wxpay_public_cert']) ? $config['wxpay_public_cert'] : '';
  69. if (empty($appid) || empty($mchid) || empty($secretV3Key)) {
  70. $this->error = 2616;
  71. return false;
  72. }
  73. // 支付参数
  74. $payConfig = config('payment.wechat');
  75. $payConfig['wechat']['default']['mch_id'] = $mchid;
  76. $payConfig['wechat']['default']['app_id'] = $appid;
  77. $payConfig['wechat']['default']['mch_secret_key'] = $secretV3Key;
  78. if ($wxpaySecretCert) {
  79. $payConfig['wechat']['default']['mch_secret_cert'] = $wxpaySecretCert;
  80. }
  81. if ($wxpayPublicCert) {
  82. $payConfig['wechat']['default']['mch_public_cert_path'] = $wxpayPublicCert;
  83. }
  84. $payConfig['wechat']['default']['notify_url'] = url('/api/notify/' . $scene . '/10');
  85. $this->config = $payConfig;
  86. return Pay::wechat($payConfig);
  87. } else if ($payType == 20) {
  88. $appid = isset($config['alipay_appid']) ? $config['alipay_appid'] : '';
  89. $appSecretCert = isset($config['alipay_secret_cert']) ? $config['alipay_secret_cert'] : '';
  90. $appPublicCert = isset($config['alipay_app_public_cert_path']) ? $config['alipay_app_public_cert_path'] : '';
  91. $alipayPublicCert = isset($config['alipay_public_cert_path']) ? $config['alipay_public_cert_path'] : '';
  92. $alipayRootCert = isset($config['alipay_root_cert_path']) ? $config['alipay_root_cert_path'] : '';
  93. if (empty($appid) || empty($appSecretCert)) {
  94. $this->error = 2619;
  95. return false;
  96. }
  97. // 支付参数
  98. $payConfig = config('payment.alipay');
  99. $payConfig['alipay']['default']['app_id'] = $appid;
  100. $payConfig['alipay']['default']['app_secret_cert'] = $appSecretCert;
  101. if ($appPublicCert) {
  102. $payConfig['alipay']['default']['app_public_cert_path'] = $appPublicCert;
  103. }
  104. if ($alipayPublicCert) {
  105. $payConfig['alipay']['default']['alipay_public_cert_path'] = $alipayPublicCert;
  106. }
  107. if ($alipayRootCert) {
  108. $payConfig['alipay']['default']['alipay_root_cert_path'] = $alipayRootCert;
  109. }
  110. $payConfig['alipay']['default']['notify_url'] = url('/api/notify/' . $scene . '/20');
  111. $this->config = $payConfig;
  112. return Pay::alipay($payConfig);
  113. } else if ($payType == 30) {
  114. return true;
  115. }
  116. return false;
  117. }
  118. /**
  119. * 微信支付
  120. * @param $userInfo
  121. * @param $order
  122. * @param string $scene
  123. * @return false|\Yansongda\Supports\Collection
  124. */
  125. public function wechatPay($userInfo, $order, $scene = 'deposit')
  126. {
  127. $amount = isset($order['pay_money']) ? $order['pay_money'] : 0;
  128. if ($amount < 0) {
  129. $this->error = 2615;
  130. return false;
  131. }
  132. $outTradeNo = isset($order['order_no']) && $order['order_no'] ? $order['order_no'] : get_order_num('PR');
  133. // 是否调用过支付,是则用新的支付单号
  134. if ($outTradeNo && $this->model->where(['out_trade_no' => $outTradeNo, 'mark' => 1])->value('id')) {
  135. $outTradeNo = $outTradeNo . date('is') . rand(1, 9);
  136. }
  137. $body = isset($order['body']) ? $order['body'] : '';
  138. $type = isset($order['type']) ? $order['type'] : 0;
  139. $payData = [
  140. 'out_trade_no' => $outTradeNo,
  141. 'attach' => "order-{$type}",
  142. 'description' => $body ? $body : '订单支付',
  143. 'amount' => [
  144. 'total' => intval($amount * 100),
  145. 'currency' => 'CNY'
  146. ],
  147. ];
  148. // 创建支付
  149. try {
  150. $pay = $this->createPay($scene, 10);
  151. RedisService::set("caches:payments:wechat:{$scene}_{$outTradeNo}", ['order' => $order, 'config' => $this->config], 7200);
  152. if (empty($pay)) {
  153. $this->error = 2616;
  154. return false;
  155. }
  156. $pay = $pay->app($payData);
  157. } catch (\Exception $exception) {
  158. RedisService::set("caches:payments:wechat:{$scene}_{$outTradeNo}_error", ['order' => $order,'error'=>$exception->getTrace(), 'config' => $this->config], 7200);
  159. $this->error = $exception->getMessage();
  160. return false;
  161. }
  162. if ($pay->prepayid) {
  163. $data = [
  164. 'user_id' => $userInfo['id'],
  165. 'out_trade_no' => $outTradeNo,
  166. 'order_no' => $order['order_no'],
  167. 'params' => json_encode($pay, 256),
  168. 'total_fee' => $amount,
  169. 'pay_type' => 10,
  170. 'create_time' => time(),
  171. 'status' => 2,
  172. 'mark' => 1,
  173. ];
  174. if ($this->model->insertGetId($data)) {
  175. $this->error = 2617;
  176. return $pay;
  177. }
  178. }
  179. $this->error = 2618;
  180. return false;
  181. }
  182. /**
  183. * 支付宝支付
  184. * @param $userInfo
  185. * @param $order
  186. * @return bool
  187. */
  188. public function aliPay($userInfo, $order, $scene = 'deposit')
  189. {
  190. $amount = isset($order['pay_money']) ? $order['pay_money'] : 0;
  191. if ($amount < 0) {
  192. $this->error = 2615;
  193. return false;
  194. }
  195. // 是否调用过支付,是则用新的支付单号
  196. $outTradeNo = isset($order['order_no']) && $order['order_no'] ? $order['order_no'] : get_order_num('PY');
  197. if ($outTradeNo && $this->model->where(['out_trade_no' => $outTradeNo, 'mark' => 1])->value('id')) {
  198. $outTradeNo = $outTradeNo . date('is') . rand(1, 9);
  199. }
  200. $body = isset($order['body']) ? $order['body'] : '';
  201. $payData = [
  202. 'out_trade_no' => $outTradeNo,
  203. 'subject' => $body ? $body : '订单支付',
  204. 'total_amount' => $amount,
  205. ];
  206. // 创建支付
  207. $pay = $this->createPay($scene, 20);
  208. RedisService::set("caches:payments:alipay:{$scene}_{$outTradeNo}", ['order' => $order, 'config' => $this->config], 7200);
  209. if (empty($pay)) {
  210. $this->error = 2619;
  211. return false;
  212. }
  213. $pay = $pay->app($payData);
  214. if ($pay->getStatusCode() == 200) {
  215. $data = [
  216. 'user_id' => $userInfo['id'],
  217. 'out_trade_no' => $outTradeNo,
  218. 'order_no' => $order['order_no'],
  219. 'params' => json_encode($pay, 256),
  220. 'total_fee' => $amount,
  221. 'pay_type' => 20,
  222. 'create_time' => time(),
  223. 'status' => 2,
  224. 'mark' => 1,
  225. ];
  226. if ($this->model->insertGetId($data)) {
  227. $this->error = 2620;
  228. return $pay->getBody()->getContents();
  229. }
  230. }
  231. $this->error = 2621;
  232. return false;
  233. }
  234. /**
  235. * 订单支付回调处理
  236. * @param string $scene 场景 deposit-保证金,depositRefund-保证金退款,withdraw-收入提现
  237. * @param int $payType 支付方式,10-微信支付,20-支付宝支付
  238. * @param array $data 回调数据
  239. * @return bool
  240. */
  241. public function catchNotify($scene, $payType, $data)
  242. {
  243. $outTradeNo = '';
  244. $payTotal = 0;
  245. $transactionId = '';
  246. $payAt = '';
  247. $notifyData = [];
  248. // 微信支付
  249. if ($payType == 10) {
  250. $resource = isset($data['resource']) ? $data['resource'] : [];
  251. $ciphertext = isset($resource['ciphertext']) ? $resource['ciphertext'] : [];
  252. $tradeStatus = isset($ciphertext['trade_state']) ? $ciphertext['trade_state'] : '';
  253. if ($tradeStatus != 'SUCCESS') {
  254. $this->error = 2622;
  255. return false;
  256. }
  257. $outTradeNo = isset($ciphertext['out_trade_no']) ? $ciphertext['out_trade_no'] : '';
  258. $transactionId = isset($ciphertext['transaction_id']) ? $ciphertext['transaction_id'] : '';
  259. if (empty($outTradeNo)) {
  260. $this->error = 2623;
  261. return false;
  262. }
  263. $payAt = isset($ciphertext['success_time']) ? date('Y-m-d H:i:s', strtotime($ciphertext['success_time'])) : date('Y-m-d H:i:s');
  264. $amount = isset($ciphertext['amount']) ? $ciphertext['amount'] : [];
  265. $payTotal = isset($amount['total']) ? moneyFormat($amount['total'] / 100, 3) : 0;
  266. $notifyData = $ciphertext;
  267. if ($payTotal <= 0) {
  268. $this->error = 2624;
  269. return false;
  270. }
  271. } // 支付宝支付
  272. else if ($payType == 20) {
  273. // TRADE_SUCCESS
  274. $tradeStatus = isset($data['trade_status']) ? $data['trade_status'] : '';
  275. if ($tradeStatus != 'TRADE_SUCCESS' && $tradeStatus != 'TRADE_FINISHED') {
  276. $this->error = 2622;
  277. return false;
  278. }
  279. $outTradeNo = isset($data['out_trade_no']) ? $data['out_trade_no'] : '';
  280. if (empty($outTradeNo)) {
  281. $this->error = 2623;
  282. return false;
  283. }
  284. $payTotal = isset($data['total_amount']) ? floatval($data['total_amount']) : 0;
  285. $transactionId = isset($data['trade_no']) ? trim($data['trade_no']) : '';
  286. $payAt = isset($data['send_pay_date']) ? trim($data['send_pay_date']) : date('Y-m-d H:i:s');
  287. $notifyData = $data;
  288. if ($payTotal <= 0) {
  289. $this->error = 2624;
  290. return false;
  291. }
  292. }
  293. // 支付信息
  294. $paymentInfo = $this->model->with(['user'])->where(['out_trade_no' => $outTradeNo, 'mark' => 1])
  295. ->select(['user_id', 'order_no', 'pay_type', 'total_fee', 'status'])
  296. ->first();
  297. $status = isset($paymentInfo['status']) ? $paymentInfo['status'] : 0;
  298. $totalFee = isset($paymentInfo['total_fee']) ? $paymentInfo['total_fee'] : 0;
  299. $paymentPayType = isset($paymentInfo['pay_type']) ? $paymentInfo['pay_type'] : 0;
  300. $orderNo = isset($paymentInfo['order_no']) ? $paymentInfo['order_no'] : '';
  301. $payUserId = isset($paymentInfo['user_id'])? $paymentInfo['user_id'] : 0;
  302. $payUser = isset($paymentInfo['user'])? $paymentInfo['user'] : [];
  303. $username = isset($payUser['mobile'])? $payUser['mobile'] : $payUserId;
  304. if (empty($paymentInfo) || empty($orderNo) || $payUserId<=0) {
  305. $this->error = 2625;
  306. return false;
  307. }
  308. // 验证支付状态
  309. if ($status == 1) {
  310. $this->error = 2626;
  311. return false;
  312. }
  313. // 验证支付方式
  314. if ($paymentPayType != $payType) {
  315. $this->error = 2627;
  316. return false;
  317. }
  318. if ($payTotal != $totalFee || $payTotal <= 0) {
  319. $this->error = 2628;
  320. return false;
  321. }
  322. // 删除久远旧记录
  323. $this->model->where(['mark' => 1])->where('create_time','<=', time() - 60 * 86400)->delete();
  324. // 更新订单数据
  325. DB::beginTransaction();
  326. $updateData = ['transaction_id' => $transactionId, 'result' => json_encode($notifyData, 256), 'pay_at' => $payAt, 'status' => 1, 'update_time' => time()];
  327. if (!$this->model->where(['out_trade_no' => $outTradeNo, 'mark' => 1])->update($updateData)) {
  328. $this->error = 2632;
  329. DB::rollBack();
  330. return false;
  331. }
  332. /* TODO 订单验证和状态处理 */
  333. // 充值订单
  334. if ($scene == 'deposit') {
  335. $orderInfo = DepositModel::where(['order_no' => $orderNo, 'type' => 1, 'mark' => 1])
  336. ->select(['id as order_id', 'user_id', 'order_no', 'money as pay_money','before_money', 'pay_at as pay_time', 'pay_status', 'status'])
  337. ->first();
  338. $orderStatus = isset($orderInfo['status']) ? $orderInfo['status'] : 0;
  339. // 验证订单
  340. if (empty($orderInfo)) {
  341. DB::rollBack();
  342. $this->error = 2629;
  343. return false;
  344. }
  345. // 订单状态
  346. if ($orderStatus != 1) {
  347. DB::rollBack();
  348. $this->error = 2630;
  349. return false;
  350. }
  351. $updateData = ['pay_status' => 2, 'pay_at' => $payAt, 'transaction_id' => $transactionId, 'status' => 3, 'update_time' => time()];
  352. if (!DepositModel::where(['order_no' => $orderNo, 'type' => 1, 'mark' => 1])->update($updateData)) {
  353. $this->error = 2633;
  354. DB::rollBack();
  355. return false;
  356. }
  357. } // 保证金退款
  358. else if ($scene == 'depositRefund') {
  359. $orderInfo = DepositModel::where(['refund_no' => $orderNo, 'type' => 1, 'mark' => 1])
  360. ->select(['id as order_id', 'user_id', 'refund_no as order_no', 'refund_money as pay_money','before_money', 'pay_at as pay_time', 'refund_status as status'])
  361. ->first();
  362. $orderStatus = isset($orderInfo['status']) ? $orderInfo['status'] : 0;
  363. // 验证订单
  364. if (empty($orderInfo)) {
  365. DB::rollBack();
  366. $this->error = 2629;
  367. return false;
  368. }
  369. // 订单状态
  370. if ($orderStatus != 2) {
  371. DB::rollBack();
  372. $this->error = 2639;
  373. return false;
  374. }
  375. // 订单打款状态
  376. if ($orderStatus == 4) {
  377. DB::rollBack();
  378. $this->error = 2630;
  379. return false;
  380. }
  381. $updateData = ['refund_pay_at' => $payAt, 'refund_transaction_id' => $transactionId, 'refund_status' => 4, 'update_time' => time()];
  382. if (!DepositModel::where(['refund_no' => $orderNo, 'type' => 1, 'mark' => 1])->update($updateData)) {
  383. $this->error = 2633;
  384. DB::rollBack();
  385. return false;
  386. }
  387. } else if($scene == 'withdraw'){
  388. $orderInfo = BalanceLogModel::where(['order_no' => $orderNo, 'type' => 2, 'mark' => 1])
  389. ->select(['id as order_id', 'user_id', 'order_no', 'money as pay_money','before_money', 'pay_at as pay_time', 'pay_status', 'status'])
  390. ->first();
  391. $orderStatus = isset($orderInfo['status']) ? $orderInfo['status'] : 0;
  392. $payStatus = isset($orderInfo['pay_status']) ? $orderInfo['pay_status'] : 0;
  393. // 验证订单
  394. if (empty($orderInfo)) {
  395. DB::rollBack();
  396. $this->error = 2629;
  397. return false;
  398. }
  399. // 订单状态
  400. if ($orderStatus != 2) {
  401. DB::rollBack();
  402. $this->error = 2639;
  403. return false;
  404. }
  405. // 订单打款状态
  406. if ($payStatus != 1) {
  407. DB::rollBack();
  408. $this->error = 2630;
  409. return false;
  410. }
  411. $updateData = ['pay_status' => 2, 'pay_at' => $payAt, 'transaction_id' => $transactionId, 'update_time' => time()];
  412. if (!BalanceLogModel::where(['order_no' => $orderNo, 'type' => 2, 'mark' => 1])->update($updateData)) {
  413. $this->error = 2633;
  414. DB::rollBack();
  415. return false;
  416. }
  417. }
  418. // TODO 场景业务回调处理
  419. $pushData = []; // 推送消息数据
  420. $orderUserId = isset($orderInfo['user_id']) ? $orderInfo['user_id'] : 0;
  421. RedisService::set("caches:notify:{$orderNo}_{$scene}:data", ['order' => $orderInfo, 'notify' => $data], 7200);
  422. switch ($scene) {
  423. case 'deposit': // 保证金充值
  424. $updateData = ['deposit'=>DB::raw("deposit + {$payTotal}"),'update_time'=>time()];
  425. if(!MemberModel::where(['id'=> $orderUserId,'mark'=>1])->update($updateData)){
  426. DB::rollBack();
  427. $this->error = 2180;
  428. return false;
  429. }
  430. // 缴纳保证金消息
  431. $message = "用户{$username}充值了{$payTotal}元保证金";
  432. $msgData = [
  433. 'from_uid' => $orderUserId,
  434. 'to_uid' => 0,
  435. 'type' => 3,// 保证金消息
  436. 'msg_type' => 4,
  437. 'title' => '充值保证金消息',
  438. 'description' => $message,
  439. 'order_no' => $orderNo,
  440. 'content' => json_encode([
  441. 'title'=> $message.'<span class="ele-text-primary">查看订单</span>',
  442. 'order_no'=> $orderNo,
  443. 'money'=> $payTotal,
  444. 'date'=> date('Y-m-d H:i:s'),
  445. 'user_id'=> $orderUserId,
  446. 'type'=> 'deposit',
  447. 'remark'=> '充值保证金',
  448. ],256),
  449. 'chat_key' => getChatKey($orderUserId,0),
  450. 'create_time' => time(),
  451. 'update_time' => time(),
  452. 'is_read' => 2,
  453. 'status' => 1
  454. ];
  455. MessageModel::insertGetId($msgData);
  456. case 'depositRefund': // 保证金退款
  457. $deposit = isset($orderInfo['before_money'])? $orderInfo['before_money'] : 0;
  458. // 明细处理
  459. $logId = AccountLogModel::where(['user_id'=>$orderUserId,'source_order_no'=> $orderNo,'mark'=>1])->value('id');
  460. $log = [
  461. 'user_id' => $orderUserId,
  462. 'source_order_no' => isset($orderInfo['order_no']) ? $orderInfo['order_no'] : '',
  463. 'type' => $scene=='deposit'? 2 : 3,
  464. 'money' => $payTotal,
  465. 'before_money' => $scene=='deposit'?$deposit:0,
  466. 'date'=> date('Y-m-d'),
  467. 'create_time' => time(),
  468. 'remark' => $scene == 'deposit'? '充值保证金' :'退保申请',
  469. 'status' => 1,
  470. 'mark' => 1,
  471. ];
  472. if($logId){
  473. AccountLogModel::where(['id'=> $logId])->update([
  474. 'status'=>1,
  475. 'update_time'=>time(),
  476. ]);
  477. }else if (!AccountLogModel::insertGetId($log)) {
  478. DB::rollBack();
  479. $this->error = 2180;
  480. return false;
  481. }
  482. break;
  483. case 'withdraw': // 收入提现// 明细处理
  484. $money = isset($orderInfo['before_money'])? $orderInfo['before_money'] : 0;
  485. $logId = AccountLogModel::where(['user_id'=>$orderUserId,'source_order_no'=> $orderNo,'mark'=>1])->value('id');
  486. $log = [
  487. 'user_id' => $orderUserId,
  488. 'source_order_no' => isset($orderInfo['order_no']) ? $orderInfo['order_no'] : '',
  489. 'type' => 4,
  490. 'money' => $payTotal,
  491. 'before_money' => $money,
  492. 'date'=> date('Y-m-d'),
  493. 'create_time' => time(),
  494. 'remark' => '收入提现',
  495. 'status' => 1,
  496. 'mark' => 1,
  497. ];
  498. if($logId){
  499. AccountLogModel::where(['id'=> $logId])->update([
  500. 'status'=>1,
  501. 'update_time'=>time(),
  502. ]);
  503. }else if (!AccountLogModel::insertGetId($log)) {
  504. DB::rollBack();
  505. $this->error = 2180;
  506. return false;
  507. }
  508. break;
  509. default:
  510. DB::rollBack();
  511. $this->error = 2631;
  512. return false;
  513. }
  514. if ($pushData && !MessageService::make()->pushMessage($orderInfo['user_id'], $pushData)) {
  515. $this->error = MessageService::make()->getError();
  516. return false;
  517. }
  518. DB::commit();
  519. return true;
  520. }
  521. /**
  522. * 退款请求
  523. * @param $order
  524. * @param string $scene
  525. * @return bool
  526. * @throws \Yansongda\Pay\Exception\ContainerException
  527. * @throws \Yansongda\Pay\Exception\InvalidParamsException
  528. * @throws \Yansongda\Pay\Exception\ServiceNotFoundException
  529. */
  530. public function refund($order, $scene='depositRefund')
  531. {
  532. $money = isset($order['money'])? $order['money'] : 0;
  533. $payType = isset($order['pay_type'])? $order['pay_type'] : 0;
  534. $orderNo = isset($order['order_no'])? $order['order_no'] : '';
  535. $outTradeNo = isset($order['out_trade_no'])? $order['out_trade_no'] : '';
  536. $transactionId = isset($order['transaction_id'])? $order['transaction_id'] : '';
  537. $remark = isset($order['remark']) && $order['remark']? $order['remark'] : '退款';
  538. $pay = PaymentService::make()->createPay($scene, $payType);
  539. if (empty($pay)) {
  540. DB::rollBack();
  541. $this->error = 2171;
  542. return false;
  543. }
  544. // 保证金退款处理
  545. $refundStatus = false;
  546. switch ($payType) {
  547. case 10: // 微信支付
  548. $data = [
  549. 'out_trade_no' => $outTradeNo,
  550. 'out_refund_no' => get_order_num('RF'),
  551. 'transaction_id' => $transactionId,
  552. 'notify_url' => url("/api/notify/{$scene}/{$payType}"),
  553. 'reason' => $remark,
  554. 'amount' => [
  555. 'refund' => intval($money * 100),
  556. 'total' => intval($money * 100),
  557. 'currency' => 'CNY',
  558. ],
  559. ];
  560. // 请求退款
  561. $pay = $pay->refund($data);
  562. RedisService::set("caches:refunds:order:{$orderNo}_wxpay", ['data' => $data,'pay'=>$pay, 'type' => $payType, 'date' => date('Y-m-d H:i:s')], 7200);
  563. if ($pay->status == 'SUCCESS' || $pay->status == 'PROCESSING') {
  564. $refundStatus = true;
  565. } else {
  566. DB::rollBack();
  567. $this->error = 2172;
  568. return false;
  569. }
  570. break;
  571. case 20: // 支付宝
  572. $data = [
  573. 'out_request_no' => $outTradeNo,
  574. 'trade_no' => $transactionId,
  575. 'refund_amount' => $money,
  576. 'query_options' => ['deposit_back_info'],
  577. 'refund_reason' => $remark,
  578. ];
  579. $payResult = $pay->refund($data);
  580. RedisService::set("caches:refunds:order:{$orderNo}_alipay", ['data' => $data,'pay'=>$payResult, 'type' => $payType, 'date' => date('Y-m-d H:i:s')], 7200);
  581. if ($payResult->code == 10000 || intval($payResult->code) == 40004) {
  582. $refundStatus = true;
  583. } else {
  584. $this->error = 2173;
  585. return false;
  586. }
  587. break;
  588. default:
  589. $this->error = 2179;
  590. return false;
  591. }
  592. $this->error = 2176;
  593. return $refundStatus;
  594. }
  595. /**
  596. * 企业打款到余额
  597. * @param $order 订单参数:order_no-单号,pay_money-打款金额,account-打款账号(微信openid或支付宝账号),body-打款备注
  598. * @param string $scene
  599. * @param int $payType
  600. * @return bool
  601. */
  602. public function transfer($order, $scene='withdraw', $payType=10)
  603. {
  604. $outTradeNo = isset($order['order_no']) && $order['order_no'] ? $order['order_no'] : get_order_num('WD');
  605. $body = isset($order['body']) ? $order['body'] : '';
  606. $amount = isset($order['pay_money']) ? $order['pay_money'] : 0;
  607. $account = isset($order['account']) ? $order['account'] : ''; // 微信openid或支付宝账号
  608. $realName = isset($order['real_name']) ? $order['real_name'] : ''; // 实名
  609. if ($amount < 0) {
  610. $this->error = 2037;
  611. return false;
  612. }
  613. if(empty($account)){
  614. $this->error = $payType == 10? 2044 : 2045;
  615. return false;
  616. }
  617. // 创建支付
  618. $result = [];
  619. try {
  620. $config = ConfigService::make()->getConfigOptionByGroup(6);
  621. if ($payType == 10) {
  622. $appid = isset($config['wxpay_appid']) ? $config['wxpay_appid'] : '';
  623. $payData = [
  624. 'appid' => $appid, // 微信小程序的app_id
  625. 'out_batch_no' => $outTradeNo, // 商家批次单号
  626. 'batch_name' => $body? $body : '收入提现', // 该笔批量转账的名称
  627. 'batch_remark' => $body? $body : '收入提现', // 转账说明
  628. 'total_amount' => intval($amount * 100), // 转账金额,单位:分
  629. 'total_num' => 1, // 转账总笔数
  630. 'transfer_detail_list' => [
  631. [
  632. 'out_detail_no' => $outTradeNo, // 商家明细单号
  633. 'transfer_amount' => intval($amount * 100), // 转账金额
  634. 'transfer_remark' => $body? $body : '收入提现', // 单条转账备注(微信用户会收到该备注)
  635. 'openid' => $account, // 转账用户的 openid
  636. ],
  637. ],
  638. ];
  639. $pay = $this->createPay($scene, $payType);
  640. RedisService::set("caches:payments:wechat:{$scene}_{$outTradeNo}_pay", ['order' => $order, 'config' => $this->config], 7200);
  641. if (empty($pay)) {
  642. $this->error = 2616;
  643. return false;
  644. }
  645. $result = $pay->transfer($payData);
  646. RedisService::set("caches:payments:wechat:{$scene}_{$outTradeNo}_result", ['order' => $order,'result'=> $result, 'config' => $this->config], 7200);
  647. $msg = isset($result['message'])? trim($result['message']) : '';
  648. $orderId = isset($result['batch_id'])? trim($result['batch_id']) : '';
  649. if(empty($orderId)){
  650. $this->error = $msg? $msg : 2046;
  651. return false;
  652. }
  653. $this->error = 2047;
  654. return ['order_no'=> $outTradeNo,'order_id'=> $orderId];
  655. }else if ($payType == 20){
  656. $payData = [
  657. 'out_biz_no' => $outTradeNo,//商户订单号,内部订单号
  658. 'trans_amount' => $amount, //订单总金额,单位为元,精确到小数点后两位,
  659. 'biz_scene' => 'DIRECT_TRANSFER', //描述特定的业务场景,可传的参数如下:DIRECT_TRANSFER:单笔无密转账到支付宝,B2C现金红包PERSONAL_COLLECTION:C2C现金红包-领红包
  660. 'product_code' => 'TRANS_ACCOUNT_NO_PWD', //业务产品码,单笔无密转账到支付宝账户固定为:TRANS_ACCOUNT_NO_PWD;收发现金红包固定为:STD_RED_PACKET;
  661. 'remark' => $body? $body : '收入提现',//业务备注
  662. 'order_title' => $body? $body : '收入提现',//转账业务的标题,用于在支付宝用户的账单里显示
  663. 'payee_info' => [
  664. 'identity' => $account,//参与方的唯一标识'208823395231'
  665. 'name' => $realName,//参与方的唯一标识'208823395231'
  666. 'identity_type' => 'ALIPAY_LOGON_ID',//参与方的标识类型,目前支持如下类型:1、ALIPAY_USER_ID 支付宝的会员ID2、ALIPAY_LOGON_ID:支付宝登录号,支持邮箱和手机号格式3、ALIPAY_OPEN_ID:支付宝openid
  667. ],//收款方信息
  668. ];
  669. $pay = $this->createPay($scene, $payType);
  670. RedisService::set("caches:payments:alipay:{$scene}_{$outTradeNo}_pay", ['order' => $order, 'config' => $this->config], 7200);
  671. if (empty($pay)) {
  672. $this->error = 2616;
  673. return false;
  674. }
  675. $result = $pay->transfer($payData);
  676. RedisService::set("caches:payments:alipay:{$scene}_{$outTradeNo}_result", ['order' => $order,'result'=> $result, 'config' => $this->config], 7200);
  677. $code = isset($result['code'])? intval($result['code']) : '';
  678. $msg = isset($result['sub_msg'])? trim($result['sub_msg']) : '';
  679. if($code != 10000){
  680. $this->error = $msg? $msg : 2046;
  681. return false;
  682. }
  683. $this->error = 2047;
  684. return $result;
  685. }
  686. $this->error = 2046;
  687. return false;
  688. } catch (\Exception $exception) {
  689. RedisService::set("caches:payments:trabsfer:{$scene}_{$outTradeNo}_error", ['order' => $order,'error'=>$exception->getTrace(), 'config' => $this->config], 7200);
  690. $this->error = $exception->getMessage();
  691. return false;
  692. }
  693. }
  694. }