NotifyController.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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. WxOrder::where('order_number', $out_trade_no)->update(['order_state' => 1, 'order_serial_number' => $transaction_id, 'order_serial_platform'=>$which_pay]);
  155. OrderUtils::order_obtains($WxOrder, $which_pay);
  156. }
  157. } else {
  158. // 处理重复通知
  159. if(WxShopOrder::where('order_id', $out_trade_no)->whereIn('pay_status', [2])->exists()){
  160. return true;
  161. }
  162. $WxShopOrder = WxShopOrder::where('order_id', $out_trade_no)->first();
  163. if($WxShopOrder){
  164. //SHOP
  165. $discounts_amount = $WxShopOrder->discounts_amount;
  166. $pay_way = ShopUtils::generate_payway_text($discounts_amount,0, $which_pay === 0 ? $payed : 0,
  167. $which_pay === 1 ? $payed : 0, 0, $WxShopOrder->coins_num, $which_pay === 3 ? $payed : 0,
  168. $which_pay === 2 ? $payed : 0);
  169. WxShopOrder::where('id', $WxShopOrder->id)
  170. ->update(['pay_status' => 2, 'status' => 1, 'serial_number' => $transaction_id, 'serial_platform'=>$which_pay, 'payment_time' => $payment_time, 'payed' => $payed, 'pay_way'=>$pay_way]);
  171. $this->usedGoodPaied($WxShopOrder->id);
  172. $is_need_deliver_info_manage = false;
  173. if($WxShopOrder->serial_platform === 0 && $WxShopOrder->serial_platform_type == 'mini'){
  174. if(Settings::get('need_mini_deliver_info_manage', 0) == 1){
  175. if(WxShopOrderGoods::where('order_id', $WxShopOrder->id)->whereIn('goods_type', [2,3,4])->where('state', 0)->exists()){
  176. $is_need_deliver_info_manage = true;
  177. }
  178. }
  179. }
  180. if(!$is_need_deliver_info_manage){
  181. ShopUtils::paied_content_process($WxShopOrder->id);
  182. }
  183. ShopUtils::split_shop_order_by_seller($WxShopOrder->id);
  184. ShopUtils::split_shop_order_by_type($WxShopOrder->id);
  185. ShopUtils::order_buys_and_stock($WxShopOrder->id);
  186. if($is_need_deliver_info_manage){
  187. WxShopOrder::withTrashed()->where('id', $WxShopOrder->id)->orWhere('parent_order_id', $WxShopOrder->id)->update([
  188. 'status' => 9
  189. ]);
  190. ShopOrderDeliverInfoManage::dispatch('virtual-upload', $WxShopOrder->id, null);
  191. }
  192. UserUtils::assistant_notice('admin', '您的商城有新的订单。');
  193. }else{
  194. return false;
  195. }
  196. }
  197. return true;
  198. } catch (\Exception $e) {
  199. _logger_(__file__, __line__, $e->getMessage());
  200. return false;
  201. }
  202. }
  203. public function usedGoodPaied($order_id){
  204. $goods_ids = [];
  205. WxShopOrderGoods::where('order_id', $order_id)->where('goods_type', 6)->get(['goods_id'])->map(function ($v) use (&$goods_ids){
  206. $goods_ids[] = $v->goods_id;
  207. return $v;
  208. });
  209. if($goods_ids){
  210. foreach ($goods_ids as $used_id){
  211. WxUsedGood::where('id', $used_id)->update([
  212. 'status' => 5
  213. ]);
  214. }
  215. }
  216. }
  217. /**
  218. * 微信app支付通知
  219. */
  220. public function wechat_app(){
  221. $config = json_decode(Cache::get('app_pay_config'), true);
  222. if(_empty_($config)){
  223. CheckBatchCahceDataLossJob::dispatch();
  224. }
  225. Pay::config($config);
  226. $result = Pay::wechat()->callback();
  227. if($result){
  228. if($result->event_type == 'TRANSACTION.SUCCESS'){
  229. $out_trade_no = $result->resource['ciphertext']['out_trade_no'];
  230. $transaction_id = $result->resource['ciphertext']['transaction_id'];
  231. $payment_time = Utils::strTimeFormat($result->resource['ciphertext']['success_time']);//支付回调支付时间
  232. $total_fee = $result->resource['ciphertext']['amount']['total'];
  233. $payed = $total_fee / 100;
  234. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 0)){
  235. return Pay::wechat()->success();
  236. }
  237. }else if($result->event_type == 'REFUND.SUCCESS' || $result->event_type == 'REFUND.ABNORMAL' || $result->event_type == 'REFUND.CLOSED'){
  238. $event_type = $result->event_type;
  239. $out_refund_no = $result->resource['ciphertext']['out_refund_no'];
  240. $transaction_id = $result->resource['ciphertext']['transaction_id'];
  241. $refund_time = Utils::strTimeFormat($result->resource['ciphertext']['success_time']);//支付回调支付时间
  242. $user_received_account = $result->resource['ciphertext']['user_received_account'];
  243. $refund_fee = $result->resource['ciphertext']['amount']['refund'];
  244. $refund = $refund_fee / 100;
  245. if($this->_refund($event_type, $out_refund_no, $transaction_id, $refund_time, $refund, $user_received_account, 0)){
  246. return Pay::wechat()->success();
  247. }
  248. }
  249. }
  250. }
  251. /**
  252. * 苹果支付 通知url
  253. * @param Request $request
  254. * @return \Illuminate\Http\JsonResponse
  255. */
  256. public function apple_app(Request $request){
  257. $orderId = $request->orderId;
  258. $productId = $request->productid;
  259. $user_id = $request->username;
  260. $transaction = $request->transaction;
  261. $restore = (int)($request->restore ?? 0);
  262. _logger_(__file__, __line__, $transaction);
  263. if($restore == 1){
  264. if(_empty_($transaction)){
  265. return $this->fail(200001);
  266. }
  267. }else{
  268. if(_empty_($orderId) || _empty_($productId) || _empty_($user_id) || _empty_($transaction)){
  269. return $this->fail(200001);
  270. }
  271. }
  272. if( _empty_(_array_key($transaction, 'transactionDate', '')) ||
  273. _empty_(_array_key($transaction, 'transactionIdentifier', '')) ||
  274. _empty_(_array_key($transaction, 'transactionState', '')) ||
  275. _empty_(_array_key($transaction, 'transactionReceipt', ''))
  276. ){
  277. return $this->fail(200001);
  278. }
  279. if($restore === 0){
  280. Cache::put('transactionIdentifier:'._array_key($transaction, 'transactionIdentifier', ''), json_encode(['orderId'=>$orderId, 'user_id'=>$user_id, 'productid'=>$productId]), 3600 * 24);
  281. }else{
  282. if(Cache::has('transactionIdentifier:'._array_key($transaction, 'transactionIdentifier', ''))){
  283. $cache_data = Cache::get('transactionIdentifier:'._array_key($transaction, 'transactionIdentifier', '') );
  284. if($cache_data){
  285. $cache_data = json_decode($cache_data, true);
  286. $orderId = $cache_data['orderId'];
  287. $productId = $cache_data['productid'];
  288. $user_id = $cache_data['user_id'];
  289. }
  290. }
  291. if(_empty_($orderId) || _empty_($productId)){
  292. return $this->fail(200001);
  293. }
  294. }
  295. $is_shop_order = false;
  296. $order = null;
  297. $notify_data = [
  298. ];
  299. if(strpos($productId, 'apple_pay_product_recharge') === 0){
  300. $productid_num = (int)(str_replace('apple_pay_product_recharge_', '', $productId));
  301. $order = WxOrder::find($orderId);
  302. $notify_data['type'] = 3;
  303. $notify_data['num'] = $productid_num;
  304. }else if(strpos($productId, 'apple_pay_product_vip') === 0){
  305. $product_price = (int)(str_replace('apple_pay_product_vip_', '', $productId));
  306. $order = WxOrder::find($orderId);
  307. $notify_data['type'] = 1;
  308. $notify_data['price'] = $product_price;
  309. }
  310. if(_empty_($order)){
  311. return $this->fail(200004, [], '没有与此orderId对应的订单');
  312. }
  313. // 已经处理过的订单
  314. if($is_shop_order){
  315. if($order->pay_status != 1){
  316. return $this->fail(200010, [], '该订单已经通知过');
  317. }
  318. }else{
  319. if($order->order_state != 0){
  320. return $this->fail(200010, [], '该订单已经通知过');
  321. }
  322. }
  323. $is_sand_box = false;
  324. $applePay = new ApplePay(_array_key($transaction, 'transactionReceipt', ''), Settings::get('apple_pay_password', ''));
  325. if ($applePay->verifyReceipt($is_sand_box)) {
  326. $result = $applePay->query($productId, function ($tradeNo, $returnData) use ($order, $is_shop_order, $notify_data) {
  327. // _logger_(__file__, __line__, $returnData);
  328. if($is_shop_order){
  329. }else{
  330. if($notify_data['type'] == 1){
  331. return $this->_notify($order->order_number, $tradeNo, current_time(), 0, 3);
  332. }else if($notify_data['type'] == 3){
  333. return $this->_notify($order->order_number, $tradeNo, current_time(), 0, 3);
  334. }
  335. }
  336. });
  337. if ($result) {
  338. return $this->success();
  339. } else {
  340. return $this->fail(200004, [], $applePay->getError());
  341. }
  342. } else {
  343. return $this->fail(200004, [], $applePay->getError());
  344. }
  345. }
  346. public function alipay_app(){
  347. $config = json_decode(Cache::get('app_pay_config'), true);
  348. if(_empty_($config)){
  349. CheckBatchCahceDataLossJob::dispatch();
  350. }
  351. Pay::config($config);
  352. $result = Pay::alipay()->callback();
  353. // 否则当支付处理
  354. if($result->alipay_trade_app_pay_response){
  355. $result = $result->alipay_trade_app_pay_response;
  356. }
  357. if($result->trade_status == 'TRADE_SUCCESS' || $result->code == 10000){
  358. $out_trade_no = $result->out_trade_no;
  359. $transaction_id = $result->trade_no;
  360. $payment_time = $result->timestamp;//支付回调支付时间
  361. $payed = $result->total_amount;
  362. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 1)){
  363. return Pay::alipay()->success();
  364. }
  365. }
  366. }
  367. public function alipay_wap(){
  368. $config = json_decode(Cache::get('app_pay_config'), true);
  369. if(_empty_($config)){
  370. CheckBatchCahceDataLossJob::dispatch();
  371. }
  372. Pay::config($config);
  373. $result = Pay::alipay()->callback();
  374. _logger_(__file__, __line__, $result);
  375. if($result->alipay_trade_wap_pay_response){
  376. $result = $result->alipay_trade_wap_pay_response;
  377. }
  378. if($result->trade_status == 'TRADE_SUCCESS' || $result->code == 10000){
  379. $out_trade_no = $result->out_trade_no;
  380. $transaction_id = $result->trade_no;
  381. $payment_time = $result->timestamp;//支付回调支付时间
  382. $payed = $result->total_amount;
  383. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 1)){
  384. return Pay::alipay()->success();
  385. }
  386. }
  387. }
  388. public function yi_app(Request $request){
  389. if(_empty_($request->out_trade_no) || _empty_($request->sign)){
  390. return '';
  391. }
  392. $epay_config = json_decode(Cache::get('yi_pay_config'), true);
  393. if(_empty_($epay_config)){
  394. CheckBatchCahceDataLossJob::dispatch();
  395. }
  396. $epay = new EPay();
  397. $epay->key($epay_config['app_secret']);
  398. //签名验证
  399. $data = $request->input();
  400. if(!$epay->signVerify($data)){
  401. exit();
  402. }
  403. // 交易状态## 交易成功
  404. if($data['trade_status']=='TRADE_SUCCESS'){
  405. $out_trade_no=$data['out_trade_no'];//网站自己生成的订单号
  406. $transaction_id=$data['trade_no'];//第三方平台产生的订单号
  407. $total_fee=$data['money'];//金额
  408. $payment_time = date('Y-m-d H:i:s', time());
  409. $payed=(float)$total_fee;
  410. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 2)){
  411. echo "success";
  412. }
  413. }
  414. return '';
  415. }
  416. /** 微信支付v2版本通知url
  417. * @return string
  418. */
  419. public function index()
  420. {
  421. $xml = file_get_contents('php://input');
  422. libxml_disable_entity_loader(true);
  423. $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
  424. $valArr = json_decode(json_encode($xmlstring), true);
  425. $out_trade_no = $valArr['out_trade_no'];//支付回调订单号
  426. $transaction_id = $valArr['transaction_id'];//支付回调支付号
  427. $time_end = $valArr['time_end'];//支付回调支付时间
  428. $payment_time = date('Y-m-d H:i:s', strtotime($time_end));
  429. $total_fee = $valArr['total_fee'];//支付回调支付金额
  430. $payed = $total_fee / 100;
  431. if (_empty_($out_trade_no)) {
  432. die('错误');
  433. }
  434. _logger_(__file__, __line__, $out_trade_no.'开始处理微信小程序支付通知');
  435. if($this->_notify($out_trade_no, $transaction_id, $payment_time, $payed, 0)){
  436. return $this->successXml();
  437. }
  438. }
  439. private function successXml()
  440. {
  441. $arr = ['return_code' => 'SUCCESS', 'return_msg' => 'OK'];
  442. $xml = "<xml>";
  443. foreach ($arr as $key => $val) {
  444. if (is_array($val)) {
  445. $xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">";
  446. } else {
  447. $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
  448. }
  449. }
  450. $xml .= "</xml>";
  451. return $xml;
  452. }
  453. }