NotifyController.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use App\Jobs\Shop\ShopOrderDeliverInfoManage;
  4. use App\Jobs\System\CheckBatchCahceDataLossJob;
  5. use App\Lib\ApplePay\ApplePay;
  6. use App\Lib\WeApp\WeApp;
  7. use App\Models\Shop\WxShopGoods;
  8. use App\Models\Shop\WxShopGoodsProduct;
  9. use App\Models\Shop\WxShopOrderGoods;
  10. use App\Models\Used\WxUsedGood;
  11. use App\Models\WxRefund;
  12. use App\Wen\Utils\FieldUtils;
  13. use App\Wen\Utils\OrderUtils;
  14. use App\Wen\Utils\Settings;
  15. use App\Wen\Utils\ShopUtils;
  16. use App\Wen\Utils\UserUtils;
  17. use App\Wen\Utils\Utils;
  18. use App\Lib\Pay\EPay;
  19. use App\Models\Shop\WxShopOrder;
  20. use App\Models\WxOrder;
  21. use Carbon\Carbon;
  22. use Illuminate\Http\Request;
  23. use Illuminate\Support\Facades\Cache;
  24. use Illuminate\Support\Facades\DB;
  25. use Yansongda\Pay\Pay;
  26. class NotifyController extends BaseController
  27. {
  28. /**
  29. * @param $event_type 退款状态 REFUND.SUCCESS 退款成功通知 REFUND.ABNORMAL 退款异常通知 REFUND.CLOSED 退款关闭通知
  30. * @param $out_refund_no 自己的退款单号
  31. * @param $transaction_id 退款的流水号
  32. * @param $refund_time 推荐完成时间
  33. * @param $refund_amount 退款金额
  34. * @param $refund_account 退款账号说明
  35. * @param $refund_from 0:微信 1:支付宝
  36. */
  37. private function _refund($event_type, $out_refund_no, $transaction_id, $refund_time, $refund_amount, $refund_account, $refund_from){
  38. $order_good = WxShopOrderGoods::where('refund_no', $out_refund_no)->first();
  39. if($order_good){
  40. if($order_good->state == 3){
  41. // 已退款
  42. return true;
  43. }
  44. if($order_good->recharge > 0){
  45. DB::beginTransaction();
  46. try {
  47. // todo:
  48. $shop_order = WxShopOrder::where('id', $order_good->order_id)->first();
  49. $updated_at = Carbon::now();
  50. $nr = '您购买的:「' . $order_good->name . '(规格:' . $order_good->product . ')」×' . $order_good->quantity . '件的退款申请已处理完毕';
  51. // 退款记录
  52. $refund_record = new WxRefund();
  53. $refund_record->user_id = $order_good->buyer_user_id;
  54. $refund_record->order_good_id = $order_good->id;
  55. $refund_record->order_id = $order_good->order_id;
  56. $refund_record->goods_id = $order_good->goods_id;
  57. $refund_record->product_id = $order_good->product_id;
  58. $refund_record->amount = $order_good->recharge;
  59. $refund_record->goods_type = $order_good->goods_type;
  60. $refund_record->save();
  61. if($event_type == 'REFUND.SUCCESS'){
  62. WxShopOrderGoods::where('id', $order_good->id)->update([
  63. 'refund_no' => $out_refund_no,
  64. 'state' => 3,
  65. 'refund_from' => $refund_from,
  66. 'refund_amount' => $refund_amount,
  67. 'refund_time' => $refund_time,
  68. 'refund_account' => $refund_account
  69. ]);
  70. }else if($event_type == 'REFUND.ABNORMAL' || $event_type == 'REFUND.CLOSED'){
  71. WxShopOrderGoods::where('id', $order_good->id)->update([
  72. 'state' => 3,
  73. 'updated_at' => $updated_at
  74. ]);
  75. $financial_type = 3;
  76. if($order_good->goods_type == 7){
  77. $financial_type = 29;
  78. }
  79. if($shop_order->serial_number){
  80. UserUtils::update_user_financial($order_good->buyer_user_id,$financial_type, $order_good->recharge,
  81. '您收到了一笔来自「' . $order_good->name . '(规格:' . $order_good->product . ')」×' . $order_good->quantity . '件的退款,退款金额¥' . $order_good->recharge
  82. );
  83. }
  84. }else{
  85. return false;
  86. }
  87. UserUtils::add_user_notice(6002, $order_good->buyer_user_id, '商品退款通知', $nr, 100);
  88. $result_ = DB::table('wx_shop_order_goods as wg')
  89. ->select(DB::raw('COUNT(*) as total_count, SUM(CASE WHEN state = 3 THEN 1 ELSE 0 END) as state_3_count'))
  90. ->where('wg.order_id', $order_good->id)
  91. ->first();
  92. if ($result_->total_count == $result_->state_3_count) {
  93. WxShopOrder::where('id', $order_good->order_id)->update([
  94. 'pay_status' => 3,
  95. 'status' => 4,
  96. 'updated_at' => $updated_at
  97. ]);
  98. }
  99. if(in_array($order_good->goods_type, FieldUtils::getShopGoodTypes())){
  100. WxShopGoods::where('id', $order_good->goods_id)->decrement('buys', (int)($order_good->quantity));
  101. WxShopGoodsProduct::where('id', $order_good->product_id)->increment('stock', (int)($order_good->quantity));
  102. }else{
  103. if($order_good->goods_type == 6){
  104. WxUsedGood::where('id', $order_good->goods_id)->update([
  105. 'status' => 6
  106. ]);
  107. }
  108. }
  109. DB::commit();
  110. return true;
  111. } catch (\Exception $e) {
  112. DB::rollBack();
  113. _logger_(__file__, __line__, $e->getMessage());
  114. return false;
  115. }
  116. }
  117. }
  118. }
  119. /**
  120. * 所有支付的 处理支付通知 函数
  121. * @param $out_trade_no
  122. * @param $transaction_id
  123. * @param $payment_time
  124. * @param $payed
  125. * @param $which_pay 0:微信 1:支付宝 2:易支付 3:苹果
  126. */
  127. private function _notify($out_trade_no, $transaction_id, $payment_time, $payed, $which_pay = 0){
  128. try {
  129. // 判断订单是 来自商品 还是 来自充电或者开通会员。
  130. $WxOrder = WxOrder::where('order_number', $out_trade_no)->first();
  131. if ($WxOrder) {
  132. // 处理重复通知
  133. if(in_array($WxOrder->order_state, [1, 3, 4, 5, 6])){
  134. return true;
  135. }
  136. // 先检查是否需要微信发货管理
  137. $is_need_deliver_info_manage = false;
  138. if($WxOrder->order_serial_platform === 0 && $WxOrder->order_serial_platform_type == 'mini'){
  139. if(Settings::get('need_mini_deliver_info_manage', 0) == 1){
  140. $is_need_deliver_info_manage = true;
  141. }
  142. }
  143. if($is_need_deliver_info_manage){
  144. if($which_pay != $WxOrder->order_serial_platform){
  145. _logger_(__file__, __line__, '理论上不应出现这种情况,order_id:'.$WxOrder->id);
  146. }
  147. WxOrder::where('order_number', $out_trade_no)->update(['order_state' => 3, 'order_serial_number' => $transaction_id]);
  148. $WxOrder->order_serial_number = $transaction_id;
  149. // 尝试通知
  150. $weapp = new WeApp('mini');
  151. $manage = $weapp->getDeliverInfoManage();
  152. $manage->virtualDeliverOrder($WxOrder);
  153. }else{
  154. //修改订单状态
  155. WxOrder::where('order_number', $out_trade_no)->update(['order_state' => 1, 'order_serial_number' => $transaction_id, 'order_serial_platform'=>$which_pay]);
  156. //执行订单逻辑
  157. OrderUtils::order_obtains($WxOrder, $which_pay);
  158. }
  159. } else {
  160. // 处理重复通知
  161. if(WxShopOrder::where('order_id', $out_trade_no)->whereIn('pay_status', [2])->exists()){
  162. return true;
  163. }
  164. $WxShopOrder = WxShopOrder::where('order_id', $out_trade_no)->first();
  165. if($WxShopOrder){
  166. //SHOP
  167. $discounts_amount = $WxShopOrder->discounts_amount;
  168. $pay_way = ShopUtils::generate_payway_text($discounts_amount,0, $which_pay === 0 ? $payed : 0,
  169. $which_pay === 1 ? $payed : 0, 0, $WxShopOrder->coins_num, $which_pay === 3 ? $payed : 0,
  170. $which_pay === 2 ? $payed : 0);
  171. WxShopOrder::where('id', $WxShopOrder->id)
  172. ->update(['pay_status' => 2, 'status' => 1, 'serial_number' => $transaction_id, 'serial_platform'=>$which_pay, 'payment_time' => $payment_time, 'payed' => $payed, 'pay_way'=>$pay_way]);
  173. $this->usedGoodPaied($WxShopOrder->id);
  174. $is_need_deliver_info_manage = false;
  175. if($WxShopOrder->serial_platform === 0 && $WxShopOrder->serial_platform_type == 'mini'){
  176. if(Settings::get('need_mini_deliver_info_manage', 0) == 1){
  177. if(WxShopOrderGoods::where('order_id', $WxShopOrder->id)->whereIn('goods_type', [2,3,4])->where('state', 0)->exists()){
  178. $is_need_deliver_info_manage = true;
  179. }
  180. }
  181. }
  182. if(!$is_need_deliver_info_manage){
  183. ShopUtils::paied_content_process($WxShopOrder->id);
  184. }
  185. ShopUtils::split_shop_order_by_seller($WxShopOrder->id);
  186. ShopUtils::split_shop_order_by_type($WxShopOrder->id);
  187. ShopUtils::order_buys_and_stock($WxShopOrder->id);
  188. if($is_need_deliver_info_manage){
  189. WxShopOrder::withTrashed()->where('id', $WxShopOrder->id)->orWhere('parent_order_id', $WxShopOrder->id)->update([
  190. 'status' => 9
  191. ]);
  192. ShopOrderDeliverInfoManage::dispatch('virtual-upload', $WxShopOrder->id, null);
  193. }
  194. UserUtils::assistant_notice('admin', '您的商城有新的订单。');
  195. }else{
  196. return false;
  197. }
  198. }
  199. return true;
  200. } catch (\Exception $e) {
  201. _logger_(__file__, __line__, $e->getMessage());
  202. return false;
  203. }
  204. }
  205. public function usedGoodPaied($order_id){
  206. $goods_ids = [];
  207. WxShopOrderGoods::where('order_id', $order_id)->where('goods_type', 6)->get(['goods_id'])->map(function ($v) use (&$goods_ids){
  208. $goods_ids[] = $v->goods_id;
  209. return $v;
  210. });
  211. if($goods_ids){
  212. foreach ($goods_ids as $used_id){
  213. WxUsedGood::where('id', $used_id)->update([
  214. 'status' => 5
  215. ]);
  216. }
  217. }
  218. }
  219. /**
  220. * 微信app支付通知
  221. */
  222. public function wechat_app(){
  223. $config = json_decode(Cache::get('app_pay_config'), true);
  224. if(_empty_($config)){
  225. CheckBatchCahceDataLossJob::dispatch();
  226. }
  227. Pay::config($config);
  228. $result = Pay::wechat()->callback();
  229. if($result){
  230. if($result->event_type == 'TRANSACTION.SUCCESS'){
  231. $out_trade_no = $result->resource['ciphertext']['out_trade_no'];
  232. $transaction_id = $result->resource['ciphertext']['transaction_id'];
  233. $payment_time = Utils::strTimeFormat($result->resource['ciphertext']['success_time']);//支付回调支付时间
  234. $total_fee = $result->resource['ciphertext']['amount']['total'];
  235. $payed = $total_fee / 100;
  236. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 0)){
  237. return Pay::wechat()->success();
  238. }
  239. }else if($result->event_type == 'REFUND.SUCCESS' || $result->event_type == 'REFUND.ABNORMAL' || $result->event_type == 'REFUND.CLOSED'){
  240. $event_type = $result->event_type;
  241. $out_refund_no = $result->resource['ciphertext']['out_refund_no'];
  242. $transaction_id = $result->resource['ciphertext']['transaction_id'];
  243. $refund_time = Utils::strTimeFormat($result->resource['ciphertext']['success_time']);//支付回调支付时间
  244. $user_received_account = $result->resource['ciphertext']['user_received_account'];
  245. $refund_fee = $result->resource['ciphertext']['amount']['refund'];
  246. $refund = $refund_fee / 100;
  247. if($this->_refund($event_type, $out_refund_no, $transaction_id, $refund_time, $refund, $user_received_account, 0)){
  248. return Pay::wechat()->success();
  249. }
  250. }
  251. }
  252. }
  253. /**
  254. * 苹果支付 通知url
  255. * @param Request $request
  256. * @return \Illuminate\Http\JsonResponse
  257. */
  258. public function apple_app(Request $request){
  259. $orderId = $request->orderId;
  260. $productId = $request->productid;
  261. $user_id = $request->username;
  262. $transaction = $request->transaction;
  263. $restore = (int)($request->restore ?? 0);
  264. _logger_(__file__, __line__, $transaction);
  265. if($restore == 1){
  266. if(_empty_($transaction)){
  267. return $this->fail(200001);
  268. }
  269. }else{
  270. if(_empty_($orderId) || _empty_($productId) || _empty_($user_id) || _empty_($transaction)){
  271. return $this->fail(200001);
  272. }
  273. }
  274. if( _empty_(_array_key($transaction, 'transactionDate', '')) ||
  275. _empty_(_array_key($transaction, 'transactionIdentifier', '')) ||
  276. _empty_(_array_key($transaction, 'transactionState', '')) ||
  277. _empty_(_array_key($transaction, 'transactionReceipt', ''))
  278. ){
  279. return $this->fail(200001);
  280. }
  281. if($restore === 0){
  282. Cache::put('transactionIdentifier:'._array_key($transaction, 'transactionIdentifier', ''), json_encode(['orderId'=>$orderId, 'user_id'=>$user_id, 'productid'=>$productId]), 3600 * 24);
  283. }else{
  284. if(Cache::has('transactionIdentifier:'._array_key($transaction, 'transactionIdentifier', ''))){
  285. $cache_data = Cache::get('transactionIdentifier:'._array_key($transaction, 'transactionIdentifier', '') );
  286. if($cache_data){
  287. $cache_data = json_decode($cache_data, true);
  288. $orderId = $cache_data['orderId'];
  289. $productId = $cache_data['productid'];
  290. $user_id = $cache_data['user_id'];
  291. }
  292. }
  293. if(_empty_($orderId) || _empty_($productId)){
  294. return $this->fail(200001);
  295. }
  296. }
  297. $is_shop_order = false;
  298. $order = null;
  299. $notify_data = [
  300. ];
  301. if(strpos($productId, 'apple_pay_product_recharge') === 0){
  302. $productid_num = (int)(str_replace('apple_pay_product_recharge_', '', $productId));
  303. $order = WxOrder::find($orderId);
  304. $notify_data['type'] = 3;
  305. $notify_data['num'] = $productid_num;
  306. }else if(strpos($productId, 'apple_pay_product_vip') === 0){
  307. $product_price = (int)(str_replace('apple_pay_product_vip_', '', $productId));
  308. $order = WxOrder::find($orderId);
  309. $notify_data['type'] = 1;
  310. $notify_data['price'] = $product_price;
  311. }
  312. if(_empty_($order)){
  313. return $this->fail(200004, [], '没有与此orderId对应的订单');
  314. }
  315. // 已经处理过的订单
  316. if($is_shop_order){
  317. if($order->pay_status != 1){
  318. return $this->fail(200010, [], '该订单已经通知过');
  319. }
  320. }else{
  321. if($order->order_state != 0){
  322. return $this->fail(200010, [], '该订单已经通知过');
  323. }
  324. }
  325. $is_sand_box = false;
  326. $applePay = new ApplePay(_array_key($transaction, 'transactionReceipt', ''), Settings::get('apple_pay_password', ''));
  327. if ($applePay->verifyReceipt($is_sand_box)) {
  328. $result = $applePay->query($productId, function ($tradeNo, $returnData) use ($order, $is_shop_order, $notify_data) {
  329. // _logger_(__file__, __line__, $returnData);
  330. if($is_shop_order){
  331. }else{
  332. if($notify_data['type'] == 1){
  333. return $this->_notify($order->order_number, $tradeNo, current_time(), 0, 3);
  334. }else if($notify_data['type'] == 3){
  335. return $this->_notify($order->order_number, $tradeNo, current_time(), 0, 3);
  336. }
  337. }
  338. });
  339. if ($result) {
  340. return $this->success();
  341. } else {
  342. return $this->fail(200004, [], $applePay->getError());
  343. }
  344. } else {
  345. return $this->fail(200004, [], $applePay->getError());
  346. }
  347. }
  348. public function alipay_app(){
  349. $config = json_decode(Cache::get('app_pay_config'), true);
  350. if(_empty_($config)){
  351. CheckBatchCahceDataLossJob::dispatch();
  352. }
  353. Pay::config($config);
  354. $result = Pay::alipay()->callback();
  355. // 否则当支付处理
  356. if($result->alipay_trade_app_pay_response){
  357. $result = $result->alipay_trade_app_pay_response;
  358. }
  359. if($result->trade_status == 'TRADE_SUCCESS' || $result->code == 10000){
  360. $out_trade_no = $result->out_trade_no;
  361. $transaction_id = $result->trade_no;
  362. $payment_time = $result->timestamp;//支付回调支付时间
  363. $payed = $result->total_amount;
  364. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 1)){
  365. return Pay::alipay()->success();
  366. }
  367. }
  368. }
  369. public function alipay_wap(){
  370. $config = json_decode(Cache::get('app_pay_config'), true);
  371. if(_empty_($config)){
  372. CheckBatchCahceDataLossJob::dispatch();
  373. }
  374. Pay::config($config);
  375. $result = Pay::alipay()->callback();
  376. _logger_(__file__, __line__, $result);
  377. if($result->alipay_trade_wap_pay_response){
  378. $result = $result->alipay_trade_wap_pay_response;
  379. }
  380. if($result->trade_status == 'TRADE_SUCCESS' || $result->code == 10000){
  381. $out_trade_no = $result->out_trade_no;
  382. $transaction_id = $result->trade_no;
  383. $payment_time = $result->timestamp;//支付回调支付时间
  384. $payed = $result->total_amount;
  385. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 1)){
  386. return Pay::alipay()->success();
  387. }
  388. }
  389. }
  390. public function yi_app(Request $request){
  391. if(_empty_($request->out_trade_no) || _empty_($request->sign)){
  392. return '';
  393. }
  394. $epay_config = json_decode(Cache::get('yi_pay_config'), true);
  395. if(_empty_($epay_config)){
  396. CheckBatchCahceDataLossJob::dispatch();
  397. }
  398. $epay = new EPay();
  399. $epay->key($epay_config['app_secret']);
  400. //签名验证
  401. $data = $request->input();
  402. if(!$epay->signVerify($data)){
  403. exit();
  404. }
  405. // 交易状态## 交易成功
  406. if($data['trade_status']=='TRADE_SUCCESS'){
  407. $out_trade_no=$data['out_trade_no'];//网站自己生成的订单号
  408. $transaction_id=$data['trade_no'];//第三方平台产生的订单号
  409. $total_fee=$data['money'];//金额
  410. $payment_time = date('Y-m-d H:i:s', time());
  411. $payed=(float)$total_fee;
  412. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 2)){
  413. echo "success";
  414. }
  415. }
  416. return '';
  417. }
  418. /** 微信支付v2版本通知url
  419. * @return string
  420. */
  421. public function index()
  422. {
  423. $xml = file_get_contents('php://input');
  424. libxml_disable_entity_loader(true);
  425. $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
  426. $valArr = json_decode(json_encode($xmlstring), true);
  427. $out_trade_no = $valArr['out_trade_no'];//支付回调订单号
  428. $transaction_id = $valArr['transaction_id'];//支付回调支付号
  429. $time_end = $valArr['time_end'];//支付回调支付时间
  430. $payment_time = date('Y-m-d H:i:s', strtotime($time_end));
  431. $total_fee = $valArr['total_fee'];//支付回调支付金额
  432. $payed = $total_fee / 100;
  433. if (_empty_($out_trade_no)) {
  434. die('错误');
  435. }
  436. _logger_(__file__, __line__, $out_trade_no.'开始处理微信小程序支付通知');
  437. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 0)){
  438. return $this->successXml();
  439. }
  440. }
  441. private function successXml()
  442. {
  443. $arr = ['return_code' => 'SUCCESS', 'return_msg' => 'OK'];
  444. $xml = "<xml>";
  445. foreach ($arr as $key => $val) {
  446. if (is_array($val)) {
  447. $xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">";
  448. } else {
  449. $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
  450. }
  451. }
  452. $xml .= "</xml>";
  453. return $xml;
  454. }
  455. }