PaymentService.php 32 KB

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