WechatPay.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <?php namespace App\Http\Controllers\Api;
  2. use App\Models\Custom;
  3. use App\Models\Orders;
  4. use App\Facades\Servers\Logs\Log;
  5. use App\Models\OrdersProduct;
  6. use App\Models\Regiment;
  7. use App\Models\RegimentActive;
  8. use App\Models\RegimentRecord;
  9. use App\Models\CustomScore;
  10. use EasyWeChat\Factory;
  11. use Illuminate\Http\Request;
  12. use App\Servers\WechatPay\Payment;
  13. use App\Facades\Servers\WechatMini\Mini;
  14. use Illuminate\Support\Facades\DB;
  15. use WeChatPay\Formatter;
  16. use WeChatPay\Crypto\AesGcm;
  17. use WeChatPay\Crypto\Rsa;
  18. /**
  19. * 微信支付接口
  20. *
  21. * @author JUN
  22. *
  23. * */
  24. class WechatPay extends Api{
  25. /**
  26. * 小程序微信支付下单 /api/wechat_pay/pay
  27. *
  28. * */
  29. public function pay(Custom $Custom)
  30. {
  31. // 检查登录
  32. $uid = $this->checkLogin();
  33. $code = request('code','');
  34. $orderId = request('order_id','');
  35. $orderInfo = Orders::query()->where('id','=',$orderId)->first()->toArray();
  36. if (empty($orderInfo))
  37. return json_send(['code'=>'error','msg'=>'订单不存在','data'=>['id'=>null]]);
  38. if ($orderInfo['custom_uid'] != $uid)
  39. return json_send(['code'=>'error','msg'=>'无权操作','data'=>['id'=>null]]);
  40. if ($orderInfo['status'] != 0)
  41. return json_send(['code'=>'error','msg'=>'订单已支付或取消','data'=>['id'=>null]]);
  42. //获取openid
  43. $result = Mini::jscode2session($code);
  44. if (!$result['openid']) return json_send(['code'=>'error','msg'=>'获取openid失败','data'=>$result['error']]);
  45. $openid = $result['openid'];
  46. $description = '开邻智数-订单编号'.$orderInfo['snowflake_id'];
  47. $payment = new Payment();
  48. return $payment->pay(['out_trade_no' => $orderInfo['snowflake_id'],'openid' => $openid,'description' => $description,'total_price' => $orderInfo['pay_total']]);
  49. }
  50. /**
  51. * 小程序微信支付回调 /api/wechat_pay/notify
  52. *
  53. * */
  54. public function notify(Custom $Custom,CustomScore $CustomScore)
  55. {
  56. $content = file_get_contents("php://input");
  57. if (!empty($content)) {
  58. //直接json字符串
  59. $params = $content;
  60. } elseif (!empty($_POST)) {
  61. //直接POST数据
  62. $params = $_POST;
  63. } else {
  64. $params = [];
  65. }
  66. $post_data = $params;
  67. Log::log('notify_wechat_pay', 'post_data:' . $post_data);
  68. //获取headers参数
  69. $headers = request()->header();
  70. Log::log('notify_wechat_pay', '微信支付回调返回headers参数:' . json_encode($headers));
  71. $inWechatpaySignature = $headers['wechatpay-signature'][0];
  72. $inWechatpayTimestamp = $headers['wechatpay-timestamp'][0];
  73. $inWechatpaySerial = $headers['wechatpay-serial'][0];
  74. $inWechatpayNonce = $headers['wechatpay-nonce'][0];
  75. $inBody = $post_data;
  76. Log::log('notify_wechat_pay', 'wechatpay-timestamp:' . $inWechatpayTimestamp);
  77. $apiv3Key = Config('wechatpay.APIV3');// 在商户平台上设置的APIv3密钥
  78. // 根据通知的平台证书序列号,查询本地平台证书文件,
  79. $platformCertificateFilePath = Config('wechatpay.platformCertificate');
  80. $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
  81. try {
  82. // 检查通知时间偏移量,允许5分钟之内的偏移
  83. $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
  84. Log::log('notify_wechat_pay', '时间偏移量:' . $timeOffsetStatus);
  85. $verifiedStatus = Rsa::verify(
  86. // 构造验签名串
  87. Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
  88. $inWechatpaySignature,
  89. $platformPublicKeyInstance
  90. );
  91. }catch (\Exception $e){
  92. Log::log('notify_wechat_pay', '错误getMessage:' . $e->getMessage());
  93. }
  94. Log::log('notify_wechat_pay', '验签:' . $verifiedStatus.'验签inWechatpayTimestamp:' . $inWechatpayTimestamp.'验签verifiedStatus:' . $inWechatpayNonce.'验签timeOffsetStatus:' . $timeOffsetStatus);
  95. if ($timeOffsetStatus && $verifiedStatus) {
  96. // 转换通知的JSON文本消息为PHP Array数组
  97. $inBodyArray = (array)json_decode($inBody, true);
  98. // 使用PHP7的数据解构语法,从Array中解构并赋值变量
  99. ['resource' => [
  100. 'ciphertext' => $ciphertext,
  101. 'nonce' => $nonce,
  102. 'associated_data' => $aad
  103. ]] = $inBodyArray;
  104. // 加密文本消息解密
  105. $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
  106. // 把解密后的文本转换为PHP Array数组
  107. $inBodyResourceArray = (array)json_decode($inBodyResource, true);
  108. Log::log('notify_wechat_pay', '打印解密后的结果:' . json_encode($inBodyResourceArray));
  109. Log::log('notify_wechat_pay', '参数:' . $inBodyResourceArray['trade_state'] . '订单号' . $inBodyResourceArray['out_trade_no'] . '微信支付号' . $inBodyResourceArray['transaction_id']);
  110. if ($inBodyResourceArray['trade_state'] == "SUCCESS") {
  111. Log::log('notify_wechat_pay', '通知订单');
  112. $orderInfo = Orders::query()->where('snowflake_id','=',$inBodyResourceArray['out_trade_no'])->first()->toArray();
  113. $status = 2;
  114. if ($orderInfo['regiment_id'] > 0){
  115. $status = 10;
  116. }
  117. //更新订单支付状态
  118. $orderData = [
  119. 'pay_time' => time(),
  120. 'transaction_id'=>$inBodyResourceArray['transaction_id'],
  121. 'status'=>$status,
  122. ];
  123. // 组合数据,写入订单表,子表
  124. DB::beginTransaction();
  125. try {
  126. $res = Orders::query()->where('snowflake_id','=',$inBodyResourceArray['out_trade_no'])->update($orderData);
  127. Log::log('notify_wechat_pay', '更新订单,snowflake_id:'.$inBodyResourceArray['out_trade_no'].';'. json_encode($res));
  128. if (!$res) {
  129. // 回退数据
  130. DB::rollBack();
  131. Log::log('notify_wechat_pay', '更新订单失败' . json_encode($res));
  132. return json_send(['code'=>'FAIL']);
  133. }
  134. //更新子订单
  135. $res = OrdersProduct::query()->where('order_id','=',$orderInfo['id'])->update(['status'=>$status]);
  136. Log::log('notify_wechat_pay', '更新子订单,order_id:'.$orderInfo['id'].';'. json_encode($res));
  137. if (!$res) {
  138. Log::log('notify_wechat_pay', '更新子订单失败:'.$orderInfo['id'].':'.$status . json_encode($res));
  139. // 回退数据
  140. DB::rollBack();
  141. return json_send(['code'=>'FAIL']);
  142. }
  143. //更新团信息
  144. if ($orderInfo['regiment_id']){
  145. //查询团信息
  146. $regimentInfo = Regiment::query()->where('id','=',$orderInfo['regiment_id'])->first()->toArray();
  147. if (!$regimentInfo) {
  148. Log::log('notify_wechat_pay', '查询团信息失败' . json_encode($regimentInfo));
  149. // 回退数据
  150. DB::rollBack();
  151. return json_send(['code'=>'FAIL']);
  152. }
  153. //查询团活动
  154. $regimentActiveInfo = RegimentActive::query()->where('id','=',$regimentInfo['active_id'])->first();
  155. if (!$regimentActiveInfo) {
  156. Log::log('notify_wechat_pay', '查询团活动失败' . json_encode($regimentInfo));
  157. // 回退数据
  158. DB::rollBack();
  159. return json_send(['code'=>'FAIL']);
  160. }
  161. //查询团记录
  162. $regimentRecordInfo = RegimentRecord::query()->where('order_id','=',$orderInfo['id'])->first();
  163. if (!$regimentRecordInfo) {
  164. Log::log('notify_wechat_pay', '查询团记录失败' . json_encode($regimentInfo));
  165. // 回退数据
  166. DB::rollBack();
  167. return json_send(['code'=>'FAIL']);
  168. }
  169. $regimentId = $orderInfo['regiment_id'];
  170. // 拼团人数加1
  171. $inc = Regiment::query()->where('id','=',$regimentId)->increment('people_number',1);
  172. Log::log('notify_wechat_pay', '拼团人数加1,regiment_id:'.$regimentId.';'. json_encode($inc));
  173. if( !$inc ) {
  174. Log::log('notify_wechat_pay', '更新团人数加1失败' . json_encode($regimentInfo));
  175. // 回退数据
  176. DB::rollBack();
  177. return json_send(['code'=>'FAIL']);
  178. }
  179. //团满
  180. if ((($regimentInfo['people_number'] + 1) == $regimentActiveInfo['number']) && $regimentActiveInfo['exceed_people'] == 1) {
  181. $res = regiment::query()->where('id','=',$regimentId)->update(['status'=>3]);
  182. Log::log('notify_wechat_pay', '图满 团状态,regiment_id:'.$regimentId.';'. json_encode($res));
  183. if( !$res ) {
  184. Log::log('notify_wechat_pay', '更新团订单失败1' . json_encode($regimentInfo));
  185. // 回退数据
  186. DB::rollBack();
  187. return json_send(['code'=>'FAIL']);
  188. }
  189. //修改团记录状态
  190. $res = RegimentRecord::query()->where('order_id','=',$orderInfo['id'])->update(['status'=>3]);
  191. if( !$res ) {
  192. Log::log('notify_wechat_pay', '更新团记录状态失败' . json_encode($regimentInfo));
  193. // 回退数据
  194. DB::rollBack();
  195. return json_send(['code'=>'FAIL']);
  196. }
  197. //修改订单
  198. $orderRes = Orders::query()->where('regiment_id','=',$regimentId)->update(['status'=>'2']);
  199. if( !$orderRes ) {
  200. Log::log('notify_wechat_pay', '更新订单失败' . json_encode($regimentInfo));
  201. // 回退数据
  202. DB::rollBack();
  203. return json_send(['code'=>'FAIL']);
  204. }
  205. //赠送积分
  206. $orderList = Orders::query()->where('regiment_id','=',$regimentId)->get();
  207. foreach ($orderList as $key => $value) {
  208. if( $value['order_score'] > 0 ) $CustomScore->trade($orderInfo['custom_uid'],$value['id'],$value['order_score'],5,1);
  209. }
  210. }else{
  211. if ($regimentInfo['status'] === 4) {
  212. $res = regiment::query()->where('id','=',$regimentId)->update(['status'=>0]);
  213. Log::log('notify_wechat_pay', '图满 团状态,regiment_id:'.$regimentId.';'. json_encode($res));
  214. if( !$res ) {
  215. Log::log('notify_wechat_pay', '更新团订单失败1' . json_encode($regimentInfo));
  216. // 回退数据
  217. //DB::rollBack();
  218. DB::connection('company')->rollBack();
  219. return json_send(['code'=>'FAIL']);
  220. }
  221. }
  222. //修改团记录状态
  223. $res = RegimentRecord::query()->where('order_id','=',$orderInfo['id'])->update(['status'=>0]);
  224. if( !$res ) {
  225. Log::log('notify_wechat_pay', '更新团记录状态失败' . json_encode($regimentInfo));
  226. // 回退数据
  227. //DB::rollBack();
  228. DB::connection('company')->rollBack();
  229. return json_send(['code'=>'FAIL']);
  230. }
  231. }
  232. }
  233. // 提交数据
  234. DB::commit();
  235. }catch (\Exception $e){
  236. Log::log('notify_wechat_pay', '更新订单失败' . json_encode($e));
  237. return json_send(['code'=>'FAIL']);
  238. }
  239. $orderInfo = Orders::query()->where('snowflake_id','=',$inBodyResourceArray['out_trade_no'])->first()->toArray();
  240. Log::log('notify_wechat_pay', '支付回调完成 通知返回' . json_encode($orderInfo));
  241. return json_send(['code'=>'SUCCESS']);
  242. }
  243. return json_send(['code'=>'SUCCESS']);
  244. }else{
  245. return json_send(['code'=>'FAIL']);
  246. }
  247. }
  248. /**
  249. * 小程序微信支付 退款回调 /api/wechat_pay/refund_notify
  250. *
  251. * */
  252. public function refund_notify(Custom $Custom,CustomScore $CustomScore)
  253. {
  254. $content = file_get_contents("php://input");
  255. if (!empty($content)) {
  256. //直接json字符串
  257. $params = $content;
  258. } elseif (!empty($_POST)) {
  259. //直接POST数据
  260. $params = $_POST;
  261. } else {
  262. $params = [];
  263. }
  264. $post_data = $params;
  265. Log::log('notify_refund_wechat_pay', 'post_data:' . $post_data);
  266. //获取headers参数
  267. $headers = request()->header();
  268. Log::log('notify_refund_wechat_pay', '微信支付回调返回headers参数:' . json_encode($headers));
  269. $inWechatpaySignature = $headers['wechatpay-signature'][0];
  270. $inWechatpayTimestamp = $headers['wechatpay-timestamp'][0];
  271. $inWechatpaySerial = $headers['wechatpay-serial'][0];
  272. $inWechatpayNonce = $headers['wechatpay-nonce'][0];
  273. $inBody = $post_data;
  274. Log::log('notify_refund_wechat_pay', 'wechatpay-timestamp:' . $inWechatpayTimestamp);
  275. $apiv3Key = Config('wechatpay.APIV3');// 在商户平台上设置的APIv3密钥
  276. // 根据通知的平台证书序列号,查询本地平台证书文件,
  277. $platformCertificateFilePath = Config('wechatpay.platformCertificate');
  278. $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
  279. try {
  280. // 检查通知时间偏移量,允许5分钟之内的偏移
  281. $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
  282. Log::log('notify_wechat_pay', '时间偏移量:' . $timeOffsetStatus);
  283. $verifiedStatus = Rsa::verify(
  284. // 构造验签名串
  285. Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
  286. $inWechatpaySignature,
  287. $platformPublicKeyInstance
  288. );
  289. }catch (\Exception $e){
  290. Log::log('notify_refund_wechat_pay', '错误getMessage:' . $e->getMessage());
  291. }
  292. Log::log('notify_refund_wechat_pay', '验签:' . $verifiedStatus.'验签inWechatpayTimestamp:' . $inWechatpayTimestamp.'验签verifiedStatus:' . $inWechatpayNonce.'验签timeOffsetStatus:' . $timeOffsetStatus);
  293. if ($timeOffsetStatus && $verifiedStatus) {
  294. // 转换通知的JSON文本消息为PHP Array数组
  295. $inBodyArray = (array)json_decode($inBody, true);
  296. // 使用PHP7的数据解构语法,从Array中解构并赋值变量
  297. ['resource' => [
  298. 'ciphertext' => $ciphertext,
  299. 'nonce' => $nonce,
  300. 'associated_data' => $aad
  301. ]] = $inBodyArray;
  302. // 加密文本消息解密
  303. $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
  304. // 把解密后的文本转换为PHP Array数组
  305. $inBodyResourceArray = (array)json_decode($inBodyResource, true);
  306. Log::log('notify_refund_wechat_pay', '打印解密后的结果:' . json_encode($inBodyResourceArray));
  307. Log::log('notify_refund_wechat_pay', '参数:' . $inBodyResourceArray['refund_status'] . '订单号' . $inBodyResourceArray['out_trade_no'] . '微信支付号' . $inBodyResourceArray['transaction_id']);
  308. if ($inBodyResourceArray['refund_status'] == "SUCCESS") {
  309. Log::log('notify_refund_wechat_pay', '通知订单');
  310. $orderInfo = Orders::query()->where('snowflake_id','=',$inBodyResourceArray['out_trade_no'])->first()->toArray();
  311. $status = 6;
  312. //更新订单支付状态
  313. $orderData = [
  314. 'status'=>$status,
  315. ];
  316. // 组合数据,写入订单表,子表
  317. DB::beginTransaction();
  318. try {
  319. $res = Orders::query()->where('snowflake_id','=',$inBodyResourceArray['out_trade_no'])->update($orderData);
  320. Log::log('notify_refund_wechat_pay', '更新订单,snowflake_id:'.$inBodyResourceArray['out_trade_no'].';'. json_encode($res));
  321. if (!$res) {
  322. // 回退数据
  323. DB::rollBack();
  324. Log::log('notify_refund_wechat_pay', '更新订单失败' . json_encode($res));
  325. return json_send(['code'=>'FAIL']);
  326. }
  327. //更新子订单
  328. $res = OrdersProduct::query()->where('order_id','=',$orderInfo['id'])->update(['status'=>$status]);
  329. Log::log('notify_refund_wechat_pay', '更新子订单,order_id:'.$orderInfo['id'].';'. json_encode($res));
  330. if (!$res) {
  331. Log::log('notify_refund_wechat_pay', '更新子订单失败:'.$orderInfo['id'].':'.$status . json_encode($res));
  332. // 回退数据
  333. DB::rollBack();
  334. return json_send(['code'=>'FAIL']);
  335. }
  336. // 提交数据
  337. DB::commit();
  338. }catch (\Exception $e){
  339. Log::log('notify_refund_wechat_pay', '更新订单失败' . json_encode($e));
  340. return json_send(['code'=>'FAIL']);
  341. }
  342. $orderInfo = Orders::query()->where('snowflake_id','=',$inBodyResourceArray['out_trade_no'])->first()->toArray();
  343. Log::log('notify_refund_wechat_pay', '支付回调完成 通知返回' . json_encode($orderInfo));
  344. return json_send(['code'=>'SUCCESS']);
  345. }
  346. return json_send(['code'=>'SUCCESS']);
  347. }else{
  348. return json_send(['code'=>'FAIL']);
  349. }
  350. }
  351. }