WechatPay.php 19 KB

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