Notify.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <?php
  2. namespace app\api\controller;
  3. use think\Controller;
  4. use think\Db;
  5. /**
  6. * 订单支付回调
  7. */
  8. class Notify extends Controller
  9. {
  10. protected $noNeedLogin = ['*'];
  11. protected $noNeedRight = ['*'];
  12. public function checkNotify($args = []){
  13. $secret = config('hitpay.salt');
  14. $input_hmac = $args['hmac'];
  15. unset($args['hmac']);
  16. //hitpay/client/generateSignatureArray
  17. $hmacSource = [];
  18. foreach ($args as $key => $val) {
  19. $hmacSource[$key] = "{$key}{$val}";
  20. }
  21. ksort($hmacSource);
  22. $sig = implode("", array_values($hmacSource));
  23. $new_hmac = hash_hmac('sha256', $sig, $secret);
  24. //判断相等
  25. if($new_hmac == $input_hmac){
  26. return true;
  27. }else{
  28. return false;
  29. }
  30. }
  31. //主动获取一次
  32. private function getPaymentStatus($payment_request_id){
  33. $apiKey = config('hitpay.apikey');
  34. $hitPayClient = new \HitPay\Client($apiKey, true);
  35. $data = $hitPayClient->getPaymentStatus($payment_request_id);
  36. return $data->status;
  37. }
  38. //充值金币 异步回调对外方法
  39. public function recharge_notify_base(){
  40. //日志
  41. $paytype = 'hitpay';
  42. $notify_file = $this->notify_log_start($paytype);
  43. //接参
  44. $field = ['payment_id','payment_request_id','phone','amount','currency','status','reference_number','hmac'];
  45. $notify_data = request_post_hub($field);
  46. //验签
  47. $checkNotify = $this->checkNotify($notify_data);
  48. if ($checkNotify !== true) {
  49. echo '签名错误';
  50. exit;
  51. }
  52. //检查支付完成 completed / failed
  53. if($notify_data['status'] != 'completed'){
  54. $now_status = $this->getPaymentStatus($notify_data['payment_request_id']);
  55. if($now_status != 'completed'){
  56. echo '没有支付完成';
  57. exit;
  58. }
  59. }
  60. //验证,拿订单号等信息
  61. $out_trade_no = $notify_data['reference_number'];
  62. $payment_request_id = $notify_data['payment_request_id'];
  63. $payment_id = $notify_data['payment_id'];
  64. //订单查询
  65. $map = [
  66. 'out_trade_no' => $out_trade_no,
  67. 'payment_request_id' => $payment_request_id,
  68. ];
  69. $info = Db::name('pay_order')->where($map)->find();
  70. if(empty($info)){
  71. echo 'success';
  72. exit;
  73. }
  74. if($info['order_status'] != 0)
  75. {
  76. echo 'success';
  77. exit;
  78. }
  79. //你可以在此编写订单逻辑
  80. $rs = $this->recharge_notify_do($out_trade_no,$payment_request_id,$payment_id);
  81. if($rs === false){
  82. //不论结果都应返回success
  83. echo 'success';
  84. exit;
  85. }else{
  86. //不论结果都应返回success
  87. echo 'success';
  88. exit;
  89. }
  90. //默认
  91. echo 'success';
  92. exit;
  93. }
  94. public function recharge_notify_base_test(){
  95. //验证,拿订单号等信息
  96. $out_trade_no = input('out_trade_no','');
  97. //订单查询
  98. $map = [
  99. 'out_trade_no' => $out_trade_no,
  100. ];
  101. $info = Db::name('pay_order')->where($map)->find();
  102. if(empty($info)){
  103. echo '订单为空';
  104. exit;
  105. }
  106. if($info['order_status'] != 0)
  107. {
  108. echo '处理过了';
  109. exit;
  110. }
  111. //你可以在此编写订单逻辑
  112. $rs = $this->recharge_notify_do($out_trade_no,'',time());
  113. if($rs === false){
  114. //不论结果都应返回success
  115. echo '错了';
  116. exit;
  117. }else{
  118. //不论结果都应返回success
  119. echo '没错';
  120. exit;
  121. }
  122. //默认
  123. echo '默认成功';
  124. exit;
  125. }
  126. //充值金币 逻辑
  127. private function recharge_notify_do($out_trade_no,$payment_request_id,$payment_id){
  128. $time = time();
  129. Db::startTrans();
  130. $orderInfo = Db::name('pay_order')->where(['out_trade_no' => $out_trade_no])->lock(true)->find();
  131. if (empty($orderInfo)) {
  132. Db::rollback();
  133. return false;
  134. }
  135. if($orderInfo['order_status'] != 0){
  136. Db::rollback();
  137. return false;
  138. }
  139. //逻辑开始
  140. //试课预约
  141. if($orderInfo['table_name'] == 'trylesson_order'){
  142. $update = [
  143. 'order_status' => 10,
  144. 'paytime' => $time,
  145. 'updatetime' => $time,
  146. ];
  147. $rs = Db::name('trylesson_order')->where('id',$orderInfo['table_id'])->update($update);
  148. if($rs === false){
  149. Db::rollback();
  150. return false;
  151. }
  152. }
  153. //售课预约
  154. if($orderInfo['table_name'] == 'lesson_order'){
  155. $update = [
  156. 'order_status' => 10,
  157. 'paytime' => $time,
  158. 'updatetime' => $time,
  159. ];
  160. $rs = Db::name('lesson_order')->where('id',$orderInfo['table_id'])->update($update);
  161. if($rs === false){
  162. Db::rollback();
  163. return false;
  164. }
  165. //更新已预约人数
  166. $lesson_order = Db::name('lesson_order')->where('id',$orderInfo['table_id'])->find();
  167. $pay_number = Db::name('lesson_order')->where('slot_id',$lesson_order['slot_id'])->where('order_status',10)->sum('usernumber');
  168. $rs_slot = Db::name('lesson_slot')->where('id',$lesson_order['slot_id'])->update(['bookednum' => $pay_number]);
  169. if($rs_slot === false){
  170. Db::rollback();
  171. return false;
  172. }
  173. //预约课程
  174. $lesson = Db::name('lesson')->where('id',$lesson_order['lesson_id'])->find();
  175. //赠送积分
  176. if($orderInfo['order_amount'] > 0){
  177. $wallet_rs = model('wallet')->lockChangeAccountRemain($orderInfo['user_id'],'score',$orderInfo['order_amount'],3,'线上预约课程:'.$lesson['name'],'lesson_order',$orderInfo['table_id'],'Booking courses online:'.$lesson['name_en']);
  178. if($wallet_rs['status'] === false){
  179. Db::rollback();
  180. return false;
  181. }
  182. }
  183. }
  184. //售课预约买套餐
  185. if($orderInfo['table_name'] == 'package_order'){
  186. //配套主订单
  187. $package_order = Db::name('package_order')->where('id',$orderInfo['table_id'])->find();
  188. //修改套餐订单支付状态
  189. $update = [
  190. 'order_status' => 1,
  191. 'paytime' => $time,
  192. 'updatetime' => $time,
  193. ];
  194. //小配套自动激活
  195. if($package_order['remain'] < 5){
  196. $update['use_status'] = 1;
  197. $update['starttime'] = $time;
  198. $update['endtime'] = $time + ($package_order['days'] * 86400); //主配套与赠品的时间保持一样就可以
  199. }
  200. $lesson_package = Db::name('lesson_package')->where('id',$package_order['package_id'])->find();
  201. //初次购买,主订单增加赠送小时数
  202. $check_first = Db::name('package_order')->where('user_id',$package_order['user_id'])->where('package_id',$package_order['package_id'])->where('order_status',1)->where('is_gift',0)->find();
  203. if(empty($check_first)){
  204. $sessions_first = $lesson_package['sessions_first'];
  205. if($sessions_first > 0){
  206. $first_update = [
  207. 'sessions'=>$package_order['sessions'] + $sessions_first,
  208. 'remain' =>$package_order['remain'] + $sessions_first,
  209. ];
  210. $rs_first = Db::name('package_order')->where('id',$orderInfo['table_id'])->update($first_update);
  211. if($rs_first === false){
  212. Db::rollback();
  213. return false;
  214. }
  215. }
  216. }
  217. //修改套餐订单支付状态
  218. $rs = Db::name('package_order')->where('order_no',$orderInfo['out_trade_no'])->update($update); //这里不用id,是因为另有赠品单,两个一起更新
  219. if($rs === false){
  220. Db::rollback();
  221. return false;
  222. }
  223. //大于等于5小时的
  224. if($package_order['remain'] >= 5){
  225. //赠送积分
  226. if($orderInfo['order_amount'] > 0){
  227. $wallet_rs = model('wallet')->lockChangeAccountRemain($orderInfo['user_id'],'score',$orderInfo['order_amount'],2,'线上购买配套:' . $lesson_package['name'],'package_order',$orderInfo['table_id'],'Buy package online:'.$lesson_package['name_en']);
  228. if($wallet_rs['status'] === false){
  229. Db::rollback();
  230. return false;
  231. }
  232. }
  233. //新会员变成旧会员
  234. $oldstatus = Db::name('user')->where('id',$orderInfo['user_id'])->value('oldstatus');
  235. if($oldstatus == 0){
  236. $user_rs = Db::name('user')->where('id',$orderInfo['user_id'])->update(['oldstatus'=>1]);
  237. if($user_rs === false){
  238. Db::rollback();
  239. return false;
  240. }
  241. }
  242. }
  243. //小于五小时的,因为已激活。修改预约单状态,减掉相应课时
  244. $args = json_decode($orderInfo['args'],true);
  245. if($package_order['remain'] < 5 && isset($args['lesson_order_id']) && !empty($args['lesson_order_id'])){
  246. // $package_order = Db::name('package_order')->where('order_no',$orderInfo['out_trade_no'])->where('is_gift',0)->find();
  247. $lesson_order = Db::name('lesson_order')->where('id',$args['lesson_order_id'])->find();
  248. //课时能够支撑报名人数
  249. if($package_order['remain'] >= $lesson_order['usernumber_hours']){
  250. //扣除一节
  251. $update = [
  252. 'remain' => bcsub($package_order['remain'],$lesson_order['usernumber_hours'],1),
  253. 'updatetime' => time(),
  254. ];
  255. $rs1 = Db::name('package_order')->where('id',$package_order['id'])->update($update);
  256. if($rs1 === false){
  257. Db::rollback();
  258. return false;
  259. }
  260. //修改预约单状态
  261. $update = [
  262. 'order_status' => 10,
  263. 'paytime' => $time,
  264. 'updatetime' => $time,
  265. 'package_order_id' => $package_order['id'],
  266. 'paytype' => 1, //从购买套餐中3,改为 课程套餐1
  267. ];
  268. $update['package_remark'] = ($package_order['sessions'] - $package_order['remain']) . '-' . ($package_order['sessions'] - $package_order['remain'] + $lesson_order['usernumber_hours']) .'/'. $package_order['sessions'];
  269. $rs = Db::name('lesson_order')->where('id',$args['lesson_order_id'])->update($update);
  270. if($rs === false){
  271. Db::rollback();
  272. return false;
  273. }
  274. //更新已预约人数
  275. $pay_number = Db::name('lesson_order')->where('slot_id',$lesson_order['slot_id'])->where('order_status',10)->sum('usernumber');
  276. $rs_slot = Db::name('lesson_slot')->where('id',$lesson_order['slot_id'])->update(['bookednum' => $pay_number]);
  277. if($rs_slot === false){
  278. Db::rollback();
  279. return false;
  280. }
  281. }else{
  282. //新买的课时不足以支撑这次的报名人数,不处理
  283. }
  284. }
  285. }
  286. //逻辑结束
  287. //状态
  288. $ros = Db::name('pay_order')->where(['out_trade_no' => $out_trade_no])->update(['order_status'=>1,'notifytime'=>$time,'payment_id'=>$payment_id]);
  289. if($ros === false) {
  290. Db::rollback();
  291. return false;
  292. }
  293. //默认提交
  294. Db::commit();
  295. return true;
  296. }
  297. //异步日志
  298. private function notify_log_start($paytype = 'wechat'){
  299. //记录支付回调数据
  300. ignore_user_abort(); // run script in background
  301. set_time_limit(30);
  302. // 日志文件 start
  303. $log_base_dir = '../paylog/'.$paytype.'/';
  304. if (!is_dir($log_base_dir))
  305. {
  306. mkdir($log_base_dir, 0770, true);
  307. @chmod($log_base_dir, 0770);
  308. }
  309. $notify_file = $log_base_dir.'notify.txt';
  310. if(!file_exists($notify_file)) {
  311. @touch($notify_file);
  312. @chmod($notify_file, 0770);
  313. }
  314. if(filesize($notify_file)>5242880)//大于5M自动切换
  315. {
  316. rename($notify_file, $log_base_dir.'notify_'.date('Y_m_d_H_i_s').'.txt');
  317. }
  318. if(!file_exists($notify_file)) {
  319. @touch($notify_file);
  320. @chmod($notify_file, 0770);
  321. }
  322. // 日志文件 end
  323. //开始写入
  324. $xml = file_get_contents("php://input");
  325. file_put_contents($notify_file, "\r\n\r\n".date('Y-m-d H:i:s')." [notify][入口接收php://input流原始数据] \n".$xml, FILE_APPEND);
  326. ini_set('display_errors','On');
  327. return $notify_file;
  328. }
  329. }