PaymentService.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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\OrderModel;
  18. use App\Models\PaymentModel;
  19. use App\Models\VideoOrderModel;
  20. use App\Models\VipModel;
  21. use App\Services\Api\MessageService;
  22. use Illuminate\Support\Facades\DB;
  23. use Yansongda\Pay\Pay;
  24. use Yansongda\Pay\Provider\Wechat;
  25. /**
  26. * 支付-服务类
  27. * @author laravel开发员
  28. * @since 2020/11/11
  29. * Class PaymentService
  30. * @package App\Services\Api
  31. */
  32. class PaymentService extends BaseService
  33. {
  34. protected static $instance = null;
  35. private $config = [];
  36. /**
  37. * 构造函数
  38. * @author laravel开发员
  39. * @since 2020/11/11
  40. * PaymentService constructor.
  41. */
  42. public function __construct()
  43. {
  44. $this->model = new PaymentModel();
  45. }
  46. /**
  47. * 静态入口
  48. * @return static|null
  49. */
  50. public static function make()
  51. {
  52. if (!self::$instance) {
  53. self::$instance = (new static());
  54. }
  55. return self::$instance;
  56. }
  57. /**
  58. * 创建支付
  59. * @param string $scene 场景,vip-VIP会员
  60. * @param int $payType
  61. * @param int $isMin 是否是小程序
  62. * @return false|\Yansongda\Pay\Provider\Alipay|Wechat
  63. */
  64. public function createPay($scene, $payType = 10, $payPt= '')
  65. {
  66. $config = ConfigService::make()->getConfigOptionByGroup(6);
  67. if ($payType == 10) {
  68. $appid = isset($config['wxpay_appid']) ? $config['wxpay_appid'] : '';
  69. $mpAppid = isset($config['wxpay_mp_appid']) ? $config['wxpay_mp_appid'] : '';
  70. $minAppid = isset($config['wxpay_min_appid']) ? $config['wxpay_min_appid'] : '';
  71. $mchid = isset($config['wxpay_mchd']) ? $config['wxpay_mchd'] : '';
  72. $secretV3Key = isset($config['wxpay_key_v3']) ? $config['wxpay_key_v3'] : '';
  73. $secretV2Key = isset($config['wxpay_key_v2']) ? $config['wxpay_key_v2'] : '';
  74. $wxpaySecretCert = isset($config['wxpay_secret_cert']) ? $config['wxpay_secret_cert'] : '';
  75. $wxpayPublicCert = isset($config['wxpay_public_cert']) ? $config['wxpay_public_cert'] : '';
  76. if (empty($appid) || empty($mchid) || empty($secretV3Key)) {
  77. $this->error = 2616;
  78. return false;
  79. }
  80. // 支付参数
  81. $payConfig = config('payment.wechat');
  82. $payConfig['wechat']['default']['mch_id'] = $mchid;
  83. if($payPt == 'min'){
  84. // 小程序支付
  85. $payConfig['wechat']['default']['mini_app_id'] = $minAppid? $minAppid : $appid;
  86. }else if($payPt == 'mp'){
  87. // 公众号
  88. $payConfig['wechat']['default']['mp_app_id'] = $mpAppid? $mpAppid : $appid;
  89. }else{
  90. // APP支付
  91. $payConfig['wechat']['default']['app_id'] = $appid;
  92. }
  93. if($secretV3Key){
  94. $payConfig['wechat']['default']['mch_secret_key'] = $secretV3Key;
  95. }else if($secretV2Key){
  96. $payConfig['wechat']['default']['mch_secret_key_v2'] = $secretV2Key;
  97. }
  98. if ($wxpaySecretCert) {
  99. $payConfig['wechat']['default']['mch_secret_cert'] = $wxpaySecretCert;
  100. }
  101. if ($wxpayPublicCert) {
  102. $payConfig['wechat']['default']['mch_public_cert_path'] = $wxpayPublicCert;
  103. }
  104. //$payConfig['wechat']['default']['notify_url'] = url('/api/notify/' . $scene . '/10');
  105. $payConfig['wechat']['default']['notify_url'] = url(env('APP_URL').'api/notify/' . $scene . '/10');
  106. $this->config = $payConfig;
  107. return Pay::wechat($payConfig);
  108. } else if ($payType == 20) {
  109. $appid = isset($config['alipay_appid']) ? $config['alipay_appid'] : '';
  110. $appSecretCert = isset($config['alipay_secret_cert']) ? $config['alipay_secret_cert'] : '';
  111. $appPublicCert = isset($config['alipay_app_public_cert_path']) ? $config['alipay_app_public_cert_path'] : '';
  112. $alipayPublicCert = isset($config['alipay_public_cert_path']) ? $config['alipay_public_cert_path'] : '';
  113. $alipayRootCert = isset($config['alipay_root_cert_path']) ? $config['alipay_root_cert_path'] : '';
  114. if (empty($appid) || empty($appSecretCert)) {
  115. $this->error = 2619;
  116. return false;
  117. }
  118. // 支付参数
  119. $payConfig = config('payment.alipay');
  120. $payConfig['alipay']['default']['app_id'] = $appid;
  121. $payConfig['alipay']['default']['app_secret_cert'] = $appSecretCert;
  122. if ($appPublicCert) {
  123. $payConfig['alipay']['default']['app_public_cert_path'] = $appPublicCert;
  124. }
  125. if ($alipayPublicCert) {
  126. $payConfig['alipay']['default']['alipay_public_cert_path'] = $alipayPublicCert;
  127. }
  128. if ($alipayRootCert) {
  129. $payConfig['alipay']['default']['alipay_root_cert_path'] = $alipayRootCert;
  130. }
  131. $payConfig['alipay']['default']['notify_url'] = url('/api/notify/' . $scene . '/20');
  132. $this->config = $payConfig;
  133. return Pay::alipay($payConfig);
  134. } else if ($payType == 30) {
  135. return true;
  136. }
  137. return false;
  138. }
  139. /**
  140. * 微信小程序支付
  141. * @param $userInfo
  142. * @param $order
  143. * @param string $scene
  144. * @return false|\Yansongda\Supports\Collection
  145. */
  146. public function minPay($userInfo, $order, $scene = 'pay')
  147. {
  148. $amount = isset($order['pay_money']) ? $order['pay_money'] : 0;
  149. $openid = isset($order['openid']) ? $order['openid'] : '';
  150. if ($amount < 0) {
  151. $this->error = 2615;
  152. return false;
  153. }
  154. if(empty($openid)){
  155. $this->error = 2614;
  156. return false;
  157. }
  158. $outTradeNo = isset($order['order_no']) && $order['order_no'] ? $order['order_no'] : get_order_num('PR');
  159. // 是否调用过支付,是则用新的支付单号
  160. if ($outTradeNo && $this->model->where(['out_trade_no' => $outTradeNo, 'mark' => 1])->value('id')) {
  161. $outTradeNo = $outTradeNo . date('is') . rand(1, 9);
  162. }
  163. $body = isset($order['body']) ? $order['body'] : '';
  164. $payData = [
  165. 'out_trade_no' => $outTradeNo,
  166. 'description' => $body ? $body : '订单支付',
  167. 'amount' => [
  168. 'total' => intval($amount * 1000/10),
  169. 'currency' => 'CNY'
  170. ],
  171. 'payer' => [
  172. 'openid' => $openid,
  173. ],
  174. ];
  175. // 创建支付
  176. try {
  177. $pay = $this->createPay($scene, 10, 'min');
  178. RedisService::set("caches:payments:wechat:{$scene}_{$outTradeNo}", ['order' => $order, 'config' => $this->config], 7200);
  179. if (empty($pay)) {
  180. $this->error = 2616;
  181. return false;
  182. }
  183. $pay = $pay->mini($payData);
  184. } catch (\Exception $exception) {
  185. RedisService::set("caches:payments:wechat:{$scene}_{$outTradeNo}_error", ['order' => $order,'error'=>$exception->getTrace(), 'config' => $this->config], 7200);
  186. $this->error = $exception->getMessage();
  187. return false;
  188. }
  189. if ($pay->package) {
  190. $data = [
  191. 'user_id' => $userInfo['id'],
  192. 'out_trade_no' => $outTradeNo,
  193. 'order_no' => $order['order_no'],
  194. 'params' => json_encode($pay, 256),
  195. 'total_fee' => $amount,
  196. 'pay_type' => 10,
  197. 'create_time' => time(),
  198. 'status' => 2,
  199. 'mark' => 1,
  200. ];
  201. if ($this->model->insertGetId($data)) {
  202. $this->error = 2617;
  203. return $pay;
  204. }
  205. }
  206. $this->error = 2618;
  207. return false;
  208. }
  209. /**
  210. * 微信支付
  211. * @param $userInfo
  212. * @param $order
  213. * @param string $scene
  214. * @return false|\Yansongda\Supports\Collection
  215. */
  216. public function wechatPay($userInfo, $order, $scene = 'pay')
  217. {
  218. $amount = isset($order['pay_money']) ? $order['pay_money'] : 0;
  219. if ($amount < 0) {
  220. $this->error = 2615;
  221. return false;
  222. }
  223. $outTradeNo = isset($order['order_no']) && $order['order_no'] ? $order['order_no'] : get_order_num('PR');
  224. // 是否调用过支付,是则用新的支付单号
  225. if ($outTradeNo && $this->model->where(['out_trade_no' => $outTradeNo, 'mark' => 1])->value('id')) {
  226. $outTradeNo = $outTradeNo . date('is') . rand(1, 9);
  227. }
  228. $body = isset($order['body']) ? $order['body'] : '';
  229. $type = isset($order['type']) ? $order['type'] : 0;
  230. $payData = [
  231. 'out_trade_no' => $outTradeNo,
  232. 'attach' => "order-{$type}",
  233. 'description' => $body ? $body : '订单支付',
  234. 'amount' => [
  235. 'total' => intval($amount * 100),
  236. 'currency' => 'CNY'
  237. ],
  238. ];
  239. // 创建支付
  240. try {
  241. $pay = $this->createPay($scene, 10);
  242. RedisService::set("caches:payments:wechat:{$scene}_{$outTradeNo}", ['order' => $order, 'config' => $this->config], 7200);
  243. if (empty($pay)) {
  244. $this->error = 2616;
  245. return false;
  246. }
  247. $pay = $pay->app($payData);
  248. } catch (\Exception $exception) {
  249. RedisService::set("caches:payments:wechat:{$scene}_{$outTradeNo}_error", ['order' => $order,'error'=>$exception->getTrace(), 'config' => $this->config], 7200);
  250. $this->error = $exception->getMessage();
  251. return false;
  252. }
  253. if ($pay->prepayid) {
  254. $data = [
  255. 'user_id' => $userInfo['id'],
  256. 'out_trade_no' => $outTradeNo,
  257. 'order_no' => $order['order_no'],
  258. 'params' => json_encode($pay, 256),
  259. 'total_fee' => $amount,
  260. 'pay_type' => 10,
  261. 'create_time' => time(),
  262. 'status' => 2,
  263. 'mark' => 1,
  264. ];
  265. if ($this->model->insertGetId($data)) {
  266. $this->error = 2617;
  267. return $pay;
  268. }
  269. }
  270. $this->error = 2618;
  271. return false;
  272. }
  273. /**
  274. * 支付宝支付
  275. * @param $userInfo
  276. * @param $order
  277. * @return bool
  278. */
  279. public function aliPay($userInfo, $order, $scene = 'deposit')
  280. {
  281. $amount = isset($order['pay_money']) ? $order['pay_money'] : 0;
  282. if ($amount < 0) {
  283. $this->error = 2615;
  284. return false;
  285. }
  286. // 是否调用过支付,是则用新的支付单号
  287. $outTradeNo = isset($order['order_no']) && $order['order_no'] ? $order['order_no'] : get_order_num('PY');
  288. if ($outTradeNo && $this->model->where(['out_trade_no' => $outTradeNo, 'mark' => 1])->value('id')) {
  289. $outTradeNo = $outTradeNo . date('is') . rand(1, 9);
  290. }
  291. $body = isset($order['body']) ? $order['body'] : '';
  292. $payData = [
  293. 'out_trade_no' => $outTradeNo,
  294. 'subject' => $body ? $body : '订单支付',
  295. 'total_amount' => $amount,
  296. ];
  297. // 创建支付
  298. $pay = $this->createPay($scene, 20);
  299. RedisService::set("caches:payments:alipay:{$scene}_{$outTradeNo}", ['order' => $order, 'config' => $this->config], 7200);
  300. if (empty($pay)) {
  301. $this->error = 2619;
  302. return false;
  303. }
  304. $pay = $pay->app($payData);
  305. if ($pay->getStatusCode() == 200) {
  306. $data = [
  307. 'user_id' => $userInfo['id'],
  308. 'out_trade_no' => $outTradeNo,
  309. 'order_no' => $order['order_no'],
  310. 'params' => json_encode($pay, 256),
  311. 'total_fee' => $amount,
  312. 'pay_type' => 20,
  313. 'create_time' => time(),
  314. 'status' => 2,
  315. 'mark' => 1,
  316. ];
  317. if ($this->model->insertGetId($data)) {
  318. $this->error = 2620;
  319. return $pay->getBody()->getContents();
  320. }
  321. }
  322. $this->error = 2621;
  323. return false;
  324. }
  325. /**
  326. * 订单支付回调处理
  327. * @param string $scene 场景 deposit-保证金,depositRefund-保证金退款,withdraw-收入提现
  328. * @param int $payType 支付方式,10-微信支付,20-支付宝支付
  329. * @param array $data 回调数据
  330. * @return bool
  331. */
  332. public function catchNotify($scene, $payType, $data)
  333. {
  334. $outTradeNo = '';
  335. $payTotal = 0;
  336. $transactionId = '';
  337. $payAt = '';
  338. $notifyData = [];
  339. try {
  340. // 微信支付
  341. if ($payType == 10) {
  342. $resource = isset($data['resource']) ? $data['resource'] : [];
  343. $ciphertext = isset($resource['ciphertext']) ? $resource['ciphertext'] : [];
  344. $tradeStatus = isset($ciphertext['trade_state']) ? $ciphertext['trade_state'] : '';
  345. if ($tradeStatus != 'SUCCESS') {
  346. $this->error = 2622;
  347. return false;
  348. }
  349. $outTradeNo = isset($ciphertext['out_trade_no']) ? $ciphertext['out_trade_no'] : '';
  350. $transactionId = isset($ciphertext['transaction_id']) ? $ciphertext['transaction_id'] : '';
  351. if (empty($outTradeNo)) {
  352. $this->error = 2623;
  353. return false;
  354. }
  355. $payAt = isset($ciphertext['success_time']) ? date('Y-m-d H:i:s', strtotime($ciphertext['success_time'])) : date('Y-m-d H:i:s');
  356. $amount = isset($ciphertext['amount']) ? $ciphertext['amount'] : [];
  357. $payTotal = isset($amount['total']) ? moneyFormat($amount['total'] / 100, 3) : 0;
  358. $notifyData = $ciphertext;
  359. if ($payTotal <= 0) {
  360. $this->error = 2624;
  361. return false;
  362. }
  363. } // 支付宝支付
  364. else if ($payType == 20) {
  365. // TRADE_SUCCESS
  366. $tradeStatus = isset($data['trade_status']) ? $data['trade_status'] : '';
  367. if ($tradeStatus != 'TRADE_SUCCESS' && $tradeStatus != 'TRADE_FINISHED') {
  368. $this->error = 2622;
  369. return false;
  370. }
  371. $outTradeNo = isset($data['out_trade_no']) ? $data['out_trade_no'] : '';
  372. if (empty($outTradeNo)) {
  373. $this->error = 2623;
  374. return false;
  375. }
  376. $payTotal = isset($data['total_amount']) ? floatval($data['total_amount']) : 0;
  377. $transactionId = isset($data['trade_no']) ? trim($data['trade_no']) : '';
  378. $payAt = isset($data['send_pay_date']) ? trim($data['send_pay_date']) : date('Y-m-d H:i:s');
  379. $notifyData = $data;
  380. if ($payTotal <= 0) {
  381. $this->error = 2624;
  382. return false;
  383. }
  384. }
  385. // 支付信息
  386. $paymentInfo = $this->model->with(['user'])->where(['out_trade_no' => $outTradeNo, 'mark' => 1])
  387. ->select(['user_id', 'order_no', 'pay_type', 'total_fee', 'status'])
  388. ->first();
  389. $status = isset($paymentInfo['status']) ? $paymentInfo['status'] : 0;
  390. $totalFee = isset($paymentInfo['total_fee']) ? $paymentInfo['total_fee'] : 0;
  391. $paymentPayType = isset($paymentInfo['pay_type']) ? $paymentInfo['pay_type'] : 0;
  392. $orderNo = isset($paymentInfo['order_no']) ? $paymentInfo['order_no'] : '';
  393. $payUserId = isset($paymentInfo['user_id']) ? $paymentInfo['user_id'] : 0;
  394. $payUser = isset($paymentInfo['user']) ? $paymentInfo['user'] : [];
  395. if (empty($paymentInfo) || empty($orderNo) || $payUserId <= 0) {
  396. $this->error = 2625;
  397. return false;
  398. }
  399. // 验证支付状态
  400. if ($status == 1) {
  401. $this->error = 2626;
  402. return false;
  403. }
  404. // 验证支付方式
  405. if ($paymentPayType != $payType) {
  406. $this->error = 2627;
  407. return false;
  408. }
  409. if ($payTotal != $totalFee || $payTotal <= 0) {
  410. $this->error = 2628;
  411. return false;
  412. }
  413. // 删除久远旧记录
  414. $this->model->where(['mark' => 1])->where('create_time', '<=', time() - 60 * 86400)->delete();
  415. // 更新订单数据
  416. DB::beginTransaction();
  417. $updateData = ['transaction_id' => $transactionId, 'result' => json_encode($notifyData, 256), 'pay_at' => $payAt, 'status' => 1, 'update_time' => time()];
  418. if (!$this->model->where(['out_trade_no' => $outTradeNo, 'mark' => 1])->update($updateData)) {
  419. $this->error = 2632;
  420. DB::rollBack();
  421. return false;
  422. }
  423. /* TODO 订单验证和状态处理 */
  424. $orderInfo = [];
  425. // VIP购买
  426. if ($scene == 'vip') {
  427. $orderInfo = OrderModel::with(['vip'])->where(['order_no' => $orderNo, 'mark' => 1])
  428. ->select(['id as order_id', 'user_id', 'goods_id', 'expired_at', 'order_no', 'total as pay_money', 'pay_at as pay_time', 'remark', 'status'])
  429. ->first();
  430. $orderStatus = isset($orderInfo['status']) ? $orderInfo['status'] : 0;
  431. // 验证订单
  432. if (empty($orderInfo)) {
  433. DB::rollBack();
  434. $this->error = 2629;
  435. return false;
  436. }
  437. // 订单状态
  438. if ($orderStatus != 1) {
  439. DB::rollBack();
  440. $this->error = 2630;
  441. return false;
  442. }
  443. $updateData = ['pay_at' => $payAt, 'transaction_id' => $transactionId, 'status' => 2, 'update_time' => time()];
  444. if (!OrderModel::where(['order_no' => $orderNo, 'mark' => 1])->update($updateData)) {
  445. $this->error = 2633;
  446. DB::rollBack();
  447. return false;
  448. }
  449. } // 视频单集购买
  450. if ($scene == 'course') {
  451. $orderInfo = VideoOrderModel::where(['order_no' => $orderNo, 'mark' => 1])
  452. ->select(['id as order_id', 'user_id', 'goods_id', 'expired_at', 'order_no', 'total as pay_money', 'pay_at as pay_time', 'remark', 'status'])
  453. ->first();
  454. $orderStatus = isset($orderInfo['status']) ? $orderInfo['status'] : 0;
  455. // 验证订单
  456. if (empty($orderInfo)) {
  457. DB::rollBack();
  458. $this->error = 2629;
  459. return false;
  460. }
  461. // 订单状态
  462. if ($orderStatus != 1) {
  463. DB::rollBack();
  464. $this->error = 2630;
  465. return false;
  466. }
  467. $updateData = ['pay_at' => $payAt, 'transaction_id' => $transactionId, 'status' => 2, 'update_time' => time()];
  468. if (!VideoOrderModel::where(['order_no' => $orderNo, 'mark' => 1])->update($updateData)) {
  469. $this->error = 2633;
  470. DB::rollBack();
  471. return false;
  472. }
  473. } // VIP退款
  474. else if ($scene == 'refund') {
  475. $orderInfo = OrderModel::where(['order_no' => $orderNo, 'mark' => 1])
  476. ->select(['id as order_id', 'user_id', 'order_no', 'total as pay_money', 'remark', 'pay_at as pay_time', 'refund_status as status'])
  477. ->first();
  478. $orderStatus = isset($orderInfo['status']) ? $orderInfo['status'] : 0;
  479. // 验证订单
  480. if (empty($orderInfo)) {
  481. DB::rollBack();
  482. $this->error = 2629;
  483. return false;
  484. }
  485. // 订单状态
  486. if ($orderStatus != 2) {
  487. DB::rollBack();
  488. $this->error = 2639;
  489. return false;
  490. }
  491. // 订单打款状态
  492. if ($orderStatus == 4) {
  493. DB::rollBack();
  494. $this->error = 2630;
  495. return false;
  496. }
  497. $updateData = ['refund_status' => 4, 'update_time' => time()];
  498. if (!OrderModel::where(['order_no' => $orderNo, 'mark' => 1])->update($updateData)) {
  499. $this->error = 2633;
  500. DB::rollBack();
  501. return false;
  502. }
  503. }
  504. // TODO 场景业务回调处理
  505. $orderUserId = isset($orderInfo['user_id']) ? $orderInfo['user_id'] : 0;
  506. RedisService::set("caches:payments:notify_{$scene}:catch_{$orderNo}_{$orderUserId}", ['order' => $orderInfo, 'notify' => $data], 7200);
  507. switch ($scene) {
  508. case 'vip': // 购买VIP
  509. $vipInfo = isset($orderInfo['vip']) ? $orderInfo['vip'] : [];
  510. $vipType = isset($vipInfo['type']) ? $vipInfo['type'] : 0;
  511. $expiredAt = isset($orderInfo['expired_at']) ? $orderInfo['expired_at'] : '';
  512. $price = isset($orderInfo['pay_money']) ? $orderInfo['pay_money'] : 0;
  513. $remark = isset($orderInfo['remark']) && $orderInfo['remark'] ? $orderInfo['remark'] : '购买VIP会员';
  514. if (empty($vipInfo) || $expiredAt <= date('Y-m-d H:i:s') || $vipType <= 0) {
  515. DB::rollBack();
  516. $this->error = 'VIP参数错误';
  517. return false;
  518. }
  519. $field = "zg_vip";
  520. if ($vipType == 2) {
  521. $field = "zsb_vip";
  522. } else if ($vipType == 3) {
  523. $field = "video_vip";
  524. }
  525. $updateData = ["is_{$field}" => 1, "{$field}_expired" => $expiredAt, 'update_time' => time()];
  526. RedisService::set("caches:payments:notify_{$scene}:catch_{$orderNo}_{$orderUserId}_update", ['order' => $orderInfo, 'update' => $updateData, 'notify' => $data], 600);
  527. if (!MemberModel::where(['id' => $orderUserId, 'mark' => 1])->update($updateData)) {
  528. DB::rollBack();
  529. $this->error = 2639;
  530. return false;
  531. }
  532. // 账单记录
  533. $log = [
  534. 'user_id' => $orderUserId,
  535. 'source_order_no' => $orderNo,
  536. 'type' => 1,
  537. 'money' => $price,
  538. 'date' => date('Y-m-d H:i:s'),
  539. 'create_time' => time(),
  540. 'remark' => $remark,
  541. 'status' => 1,
  542. 'mark' => 1
  543. ];
  544. RedisService::set("caches:payments:notify_{$scene}:catch_{$orderNo}_{$orderUserId}_log", ['order' => $orderInfo, 'log' => $log, 'notify' => $data], 600);
  545. if (!AccountLogModel::insertGetId($log)) {
  546. $this->error = 2635;
  547. return false;
  548. }
  549. break;
  550. case 'course':
  551. $price = isset($orderInfo['pay_money']) ? $orderInfo['pay_money'] : 0;
  552. $remark = isset($orderInfo['remark']) && $orderInfo['remark'] ? $orderInfo['remark'] : '购买VIP会员';
  553. // 账单记录
  554. $log = [
  555. 'user_id' => $orderUserId,
  556. 'source_order_no' => $orderNo,
  557. 'type' => 2,
  558. 'money' => $price,
  559. 'date' => date('Y-m-d H:i:s'),
  560. 'create_time' => time(),
  561. 'remark' => $remark,
  562. 'status' => 1,
  563. 'mark' => 1
  564. ];
  565. RedisService::set("caches:payments:notify_{$scene}:catch_{$orderNo}_{$orderUserId}_log", ['order' => $orderInfo, 'log' => $log, 'notify' => $data], 600);
  566. if (!AccountLogModel::insertGetId($log)) {
  567. $this->error = 2635;
  568. return false;
  569. }
  570. break;
  571. default:
  572. break;
  573. }
  574. $this->error = 2638;
  575. DB::commit();
  576. return true;
  577. } catch (\Exception $exception){
  578. $this->error = $exception->getMessage();
  579. RedisService::set("caches:payments:notify_{$scene}:catch_" . $orderNo.'_error', ['notify' => $data,'error' => $exception->getMessage(),'trace'=>$exception->getTrace()], 7200);
  580. return false;
  581. }
  582. }
  583. /**
  584. * 退款请求
  585. * @param $order
  586. * @param string $scene
  587. * @return bool
  588. * @throws \Yansongda\Pay\Exception\ContainerException
  589. * @throws \Yansongda\Pay\Exception\InvalidParamsException
  590. * @throws \Yansongda\Pay\Exception\ServiceNotFoundException
  591. */
  592. public function refund($order, $scene='vip')
  593. {
  594. $money = isset($order['money'])? $order['money'] : 0;
  595. $payType = isset($order['pay_type'])? $order['pay_type'] : 0;
  596. $orderNo = isset($order['order_no'])? $order['order_no'] : '';
  597. $outTradeNo = isset($order['out_trade_no'])? $order['out_trade_no'] : '';
  598. $transactionId = isset($order['transaction_id'])? $order['transaction_id'] : '';
  599. $remark = isset($order['remark']) && $order['remark']? $order['remark'] : '退款';
  600. $pay = PaymentService::make()->createPay($scene, $payType);
  601. if (empty($pay)) {
  602. DB::rollBack();
  603. $this->error = 2171;
  604. return false;
  605. }
  606. // 保证金退款处理
  607. $refundStatus = false;
  608. switch ($payType) {
  609. case 10: // 微信支付
  610. $data = [
  611. 'out_trade_no' => $outTradeNo,
  612. 'out_refund_no' => get_order_num('RF'),
  613. 'transaction_id' => $transactionId,
  614. 'notify_url' => url("/api/notify/{$scene}/{$payType}"),
  615. 'reason' => $remark,
  616. 'amount' => [
  617. 'refund' => intval($money * 100),
  618. 'total' => intval($money * 100),
  619. 'currency' => 'CNY',
  620. ],
  621. ];
  622. // 请求退款
  623. $pay = $pay->refund($data);
  624. RedisService::set("caches:refunds:order:{$orderNo}_wxpay", ['data' => $data,'pay'=>$pay, 'type' => $payType, 'date' => date('Y-m-d H:i:s')], 7200);
  625. if ($pay->status == 'SUCCESS' || $pay->status == 'PROCESSING') {
  626. $refundStatus = true;
  627. } else {
  628. DB::rollBack();
  629. $this->error = 2172;
  630. return false;
  631. }
  632. break;
  633. case 20: // 支付宝
  634. $data = [
  635. 'out_request_no' => $outTradeNo,
  636. 'trade_no' => $transactionId,
  637. 'refund_amount' => $money,
  638. 'query_options' => ['deposit_back_info'],
  639. 'refund_reason' => $remark,
  640. ];
  641. $payResult = $pay->refund($data);
  642. RedisService::set("caches:refunds:order:{$orderNo}_alipay", ['data' => $data,'pay'=>$payResult, 'type' => $payType, 'date' => date('Y-m-d H:i:s')], 7200);
  643. if ($payResult->code == 10000 || intval($payResult->code) == 40004) {
  644. $refundStatus = true;
  645. } else {
  646. $this->error = 2173;
  647. return false;
  648. }
  649. break;
  650. default:
  651. $this->error = 2179;
  652. return false;
  653. }
  654. $this->error = 2176;
  655. return $refundStatus;
  656. }
  657. }