PaymentService.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php
  2. namespace app\common\service;
  3. use app\common\model\PaymentModel;
  4. use app\common\model\ScoreLogModel;
  5. use app\common\model\ServicesOrderModel;
  6. use app\common\model\ShopGoodsModel;
  7. use app\common\model\ShopOrderGoodsModel as OrderGoods;
  8. use app\common\model\ShopOrderModel;
  9. use app\common\model\ThirdpayBackModel;
  10. use app\common\model\UserModel;
  11. use think\facade\Db;
  12. use utils\RedisCache;
  13. /**
  14. * 支付服务 by wes
  15. * Class PaymentService
  16. * @package app\common\service
  17. */
  18. class PaymentService
  19. {
  20. protected static $instance = null;
  21. protected $cacheTime = 7200;
  22. protected $payWayArr = [
  23. 1=>'wxpay',
  24. 2=>'alipay',
  25. 3=>'balancePay',
  26. 14=>'huifuPay',
  27. 15=>'sqzpay',
  28. 16=>'sqzAliPay',
  29. 17=>'sqzWxPay',
  30. 18=>'sqzYljk',
  31. 19=>'usdtPay',
  32. 20=>'yswkPay',
  33. 22=>'dkpay',
  34. 56=>'ysftyPay'
  35. ];
  36. protected $model = null;
  37. public function __construct()
  38. {
  39. $this->model = new PaymentModel();
  40. }
  41. /**
  42. * 静态化入口
  43. * @return static|null
  44. */
  45. public static function make()
  46. {
  47. if(!self::$instance){
  48. self::$instance = new static();
  49. }
  50. return self::$instance;
  51. }
  52. /**
  53. * 获取支付方式标识
  54. * @param $payType 支付类型
  55. * @return string
  56. */
  57. public function getPayCode($payType)
  58. {
  59. return isset($this->payWayArr[$payType])? $this->payWayArr[$payType] : 'defpay';
  60. }
  61. /**
  62. * 按状态获取支付单数量
  63. * @param $uid
  64. * @param $orderType
  65. * @param $state
  66. * @param int $time 时间/小时,默认2小时
  67. * @return array|mixed
  68. * @throws \think\db\exception\DataNotFoundException
  69. * @throws \think\db\exception\DbException
  70. * @throws \think\db\exception\ModelNotFoundException
  71. */
  72. public function getPaymentCountByState($uid, $orderSn='', $orderType, $state, $time = 2)
  73. {
  74. $cacheKey = "caches:paymentCall:u{$uid}_ot{$orderType}_s{$state}_{$time}_{$orderSn}";
  75. $data = RedisCache::get($cacheKey);
  76. if($data){
  77. return $data;
  78. }
  79. $where = [];
  80. if($orderType){
  81. $where['order_type'] = $orderType;
  82. }
  83. if($state){
  84. $where['state'] = $state;
  85. }
  86. $data = $this->model->where($where)->where(function ($query) use($time,$orderSn){
  87. if($time>0){
  88. $query->where('creat_at','>=', time() - $time * 3600);
  89. }
  90. if($orderSn){
  91. $query->where('remarks',$orderSn);
  92. }
  93. })->count('id');
  94. if($data){
  95. RedisCache::set($cacheKey, $data, rand(3,5));
  96. }
  97. return $data;
  98. }
  99. /**
  100. * 验证订单是否已支付
  101. * @param $uid
  102. * @param $orderSn
  103. * @return array|mixed
  104. * @throws \think\db\exception\DataNotFoundException
  105. * @throws \think\db\exception\DbException
  106. * @throws \think\db\exception\ModelNotFoundException
  107. */
  108. public function checkPaymentState($uid, $orderSn)
  109. {
  110. $cacheKey = "caches:paymentState:u{$uid}_sn{$orderSn}";
  111. $data = RedisCache::get($cacheKey);
  112. if($data){
  113. return $data;
  114. }
  115. $data = $this->model->where(['state'=> 6])->where(function ($query) use($orderSn){
  116. if($orderSn){
  117. $query->where('remarks', $orderSn);
  118. }
  119. })->value('id');
  120. if($data){
  121. RedisCache::set($cacheKey, $data, rand(2,3));
  122. }
  123. return $data;
  124. }
  125. /**
  126. * 获取支付信息(有缓存)
  127. * @param $outTradeNo
  128. * @param int $state
  129. * @param string $field
  130. * @param false $cache
  131. * @return array|mixed
  132. */
  133. public function getCacheInfo($outTradeNo, $state=7, $field='', $cache = false)
  134. {
  135. $cacheKey = "caches:payment:info:otn{$outTradeNo}_{$state}".($field? '_'.md5($field):'');
  136. $data = RedisCache::get($cacheKey);
  137. if($data && $cache){
  138. return $data;
  139. }
  140. $where = ['out_trade_no'=> $outTradeNo];
  141. if($state){
  142. $where['state'] = $state;
  143. }
  144. $field = $field? $field : 'id,out_trade_no,uid,total_fee,state,trade_type,out_trade_no1,hy_token_id,syl_sureorderid,hy_bill_no,is_retreat,pay_way,order_type,sid,remarks,trade_no';
  145. $data = $this->model->where($where)
  146. ->field($field)
  147. ->order('creat_at desc')
  148. ->findOrEmpty();
  149. if($data && $cache){
  150. RedisCache::set($cacheKey, $data, rand(5,10));
  151. }
  152. return $data;
  153. }
  154. /**
  155. * 支付回调处理
  156. * @param $outTradeNo
  157. * @param $payMoney
  158. * @param $payType
  159. * @param $content
  160. * @throws \think\Exception
  161. * @throws \think\db\exception\DataNotFoundException
  162. * @throws \think\db\exception\DbException
  163. * @throws \think\db\exception\ModelNotFoundException
  164. */
  165. public function payBack($outTradeNo, $payMoney, $payType, $content)
  166. {
  167. $nowTime = date('Y-m-d H:i:s');
  168. $params = $content? json_decode($content, true):[];
  169. $payCode = PaymentService::make()->getPayCode($payType);
  170. $cacheKey = "caches:payNotify:{$payCode}:otn_{$outTradeNo}:";
  171. RedisCache::set($cacheKey.'catch', ['params'=>$params,'date'=> $nowTime], $this->cacheTime);
  172. // 验证支付请求信息
  173. $payInfo = PaymentService::make()->getCacheInfo($outTradeNo, 0);
  174. $payId = isset($payInfo['id'])? $payInfo['id']:0;
  175. $payUid = isset($payInfo['uid'])? $payInfo['uid']:0;
  176. $payState = isset($payInfo['state'])? intval($payInfo['state']):0;
  177. $totalFee = isset($payInfo['total_fee'])? floatval($payInfo['total_fee']):0;
  178. $orderSn = isset($payInfo['remarks'])? trim($payInfo['remarks']):'';
  179. $orderType = isset($payInfo['order_type'])? intval($payInfo['order_type']) : 0;
  180. if (!$payInfo || $payUid<=0 || empty($orderSn) || $payState!= 7) {
  181. $error = empty($payInfo) || !$payId || empty($orderSn)? "单号{$orderSn}支付信息不存在或参数错误":"单号{$orderSn}已经回调支付";
  182. $logData = ['params'=>$params,'payInfo'=>$payInfo,'msg'=> $error,'date'=> $nowTime];
  183. RedisCache::set($cacheKey.'error', $logData, $this->cacheTime);
  184. sr_throw($error);
  185. }
  186. // 验证支付金额
  187. if($payMoney < $totalFee || $totalFee<=0){
  188. $logData = ['params'=>$params,'payInfo'=>$payInfo,'msg'=> "单号{$orderSn}支付金额错误",'date'=> $nowTime];
  189. RedisCache::set($cacheKey.'error', $logData, $this->cacheTime);
  190. sr_throw("单号{$orderSn}支付金额错误");
  191. }
  192. // 更新支付状态
  193. if(!PaymentModel::where('id', $payId)->update(['state' => 6, 'pay_at' => $nowTime])){
  194. $logData = ['params'=>$params,'payInfo'=>$payInfo,'msg'=> "单号{$orderSn}更新支付状态失败",'date'=> $nowTime];
  195. RedisCache::set($cacheKey.'_error', $logData, $this->cacheTime);
  196. sr_throw("单号{$orderSn}更新支付状态失败");
  197. }
  198. // 第三方回调数据
  199. $data = [
  200. 'out_order_no' => $outTradeNo,
  201. 'content' => $content,
  202. 'create_time' => sr_getcurtime(time()),
  203. 'type' => $payType,
  204. 'uid' => $payUid,
  205. 'money' => $payMoney
  206. ];
  207. if(!ThirdpayBackModel::insertGetid($data)){
  208. $logData = ['params'=>$params,'payInfo'=>$payInfo,'third'=>$data,'msg'=> "单号{$orderSn}处理第三方回调数据错误",'date'=> $nowTime];
  209. RedisCache::set($cacheKey.'_error', $logData, $this->cacheTime);
  210. sr_throw("单号{$orderSn}处理第三方回调数据错误");
  211. }
  212. /* TODO 服务商订单处理 */
  213. if ($orderType == 6) {
  214. // 获取验证订单信息
  215. $orderInfo = ServiceOrderService::make()->getInfoBySn($orderSn, $payUid);
  216. $orderId = isset($orderInfo['order_id'])? $orderInfo['order_id'] : 0;
  217. $orderMoney = isset($orderInfo['payment'])? floatval($orderInfo['payment']) : 0;
  218. $orderStatus = isset($orderInfo['status'])? intval($orderInfo['status']) : 0;
  219. if(empty($orderInfo) || $orderId<=0 || $orderMoney<=0){
  220. $logData = ['params'=>$params,'payInfo'=>$payInfo,'order'=>$orderInfo,'msg'=> "服务商订单{$orderSn}不存在或参数错误",'date'=> $nowTime];
  221. RedisCache::set($cacheKey.'_error', $logData, $this->cacheTime);
  222. sr_throw("服务商订单{$orderSn}不存在或参数错误");
  223. }
  224. // 验证订单状态
  225. if($orderStatus != 1){
  226. $logData = ['params'=>$params,'payInfo'=>$payInfo,'order'=>$orderInfo,'msg'=> "服务商订单{$orderSn}订单已处理",'date'=> $nowTime];
  227. RedisCache::set($cacheKey.'_error', $logData, $this->cacheTime);
  228. sr_throw("服务商订单{$orderSn}订单已处理");
  229. }
  230. // 更新订单状态
  231. if(!ServicesOrderModel::where('order_id', $orderId)->update(['status' => 2, 'pay_type' => $payType, 'updated_time' => $nowTime])){
  232. $logData = ['params'=>$params,'payInfo'=>$payInfo,'order'=>$orderInfo,'msg'=> "服务商订单{$orderSn}更新状态错误",'date'=> $nowTime];
  233. RedisCache::set($cacheKey.'_error', $logData, $this->cacheTime);
  234. sr_throw("服务商订单{$orderSn}更新状态错误");
  235. }
  236. // 更新用户服务商有效期
  237. $date = sr_getcurtime(time(), 'Y-m-d');
  238. $expireDay = date('Y-m-d', strtotime("$date +1 month"));
  239. if(!UserModel::where('id', $payUid)->update(['store_type' => 1,'store_expire_time' => $expireDay,'update_time'=>time()])){
  240. $logData = ['params'=>$params,'payInfo'=>$payInfo,'order'=>$orderInfo,'storeExpire'=>$expireDay,'msg'=> "服务商订单{$orderSn}更新用户服务商有效期错误",'date'=> $nowTime];
  241. RedisCache::set($cacheKey.'_error', $logData, $this->cacheTime);
  242. sr_throw("服务商订单{$orderSn}更新用户服务商有效期错误");
  243. }
  244. $logData = ['params'=>$params,'payInfo'=>$payInfo,'order'=>$orderInfo,'storeExpire'=>$expireDay,'msg'=> "服务商订单{$orderSn}支付回调处理成功",'date'=> $nowTime];
  245. RedisCache::set($cacheKey."success_{$orderSn}", $logData, $this->cacheTime);
  246. }
  247. /** TODO 商城订单处理 **/
  248. elseif ($orderType == 4) {
  249. // 获取验证订单信息
  250. $orderInfo = ShopOrderService::make()->getInfoBySn($orderSn, $payUid);
  251. $orderId = isset($orderInfo['order_id'])? $orderInfo['order_id'] : 0;
  252. $orderMoney = isset($orderInfo['payment'])? floatval($orderInfo['payment']) : 0;
  253. $orderStatus = isset($orderInfo['status'])? intval($orderInfo['status']) : -1;
  254. if(empty($orderInfo) || $orderId<=0 || $orderMoney<=0){
  255. $logData = ['params'=>$params,'payInfo'=>$payInfo,'order'=>$orderInfo,'msg'=> "商城订单{$orderSn}不存在或参数错误",'date'=> $nowTime];
  256. RedisCache::set($cacheKey."error_{$orderSn}", $logData, $this->cacheTime);
  257. sr_throw("商城订单{$orderSn}不存在或参数错误");
  258. }
  259. // 验证订单状态
  260. if($orderStatus != 0){
  261. $logData = ['params'=>$params,'payInfo'=>$payInfo,'order'=>$orderInfo,'msg'=> "商城订单{$orderSn}订单已处理",'date'=> $nowTime];
  262. RedisCache::set($cacheKey."error_{$orderSn}", $logData, $this->cacheTime);
  263. sr_throw("商城订单{$orderSn}订单已处理");
  264. }
  265. // 更新订单状态
  266. if(!ShopOrderModel::where('order_id', $orderId)->update(['status' => 1, 'pay_type' => $payType, 'updated_time' => $nowTime])){
  267. $logData = ['params'=>$params,'payInfo'=>$payInfo,'order'=>$orderInfo,'msg'=> "服务商订单{$orderSn}更新状态错误",'date'=> $nowTime];
  268. RedisCache::set($cacheKey.'_error', $logData, $this->cacheTime);
  269. sr_throw("服务商订单{$orderSn}更新状态错误");
  270. }
  271. // 增加订单商品销量
  272. $updateSale = ShopOrderModel::alias('a')
  273. ->leftJoin('shop_order_goods og', 'og.order_id=a.order_id')
  274. ->leftJoin('shop_goods g', 'g.goods_id=og.goods_id')
  275. ->where(['a.order_id' => $orderId, 'a.user_id' => $payUid])
  276. ->update([
  277. 'g.sales_volume' => Db::raw('g.sales_volume + og.num'),
  278. 'g.real_sales_volume' => Db::raw('g.real_sales_volume + og.num'),
  279. ]);
  280. if (!$updateSale) {
  281. Db::rollback();
  282. $logData = ['params' => $params, 'orderInfo' => $orderInfo, 'payInfo' => $payInfo, 'error' => "商城订单{$orderSn}更新商品销量失败", 'date' => $date];
  283. RedisCache::set($cacheKey . "error_{$orderSn}", $logData, 7200);
  284. sr_throw("商城订单{$orderSn}更新商品销量失败");
  285. }
  286. // 更新用户交易额(消费)
  287. $userInfo = UserService::make()->getCacheInfo($payUid,'id,mobile,score,money,path', false);
  288. $upperPath = isset($userInfo['path'])? $userInfo['path'] : '';
  289. if($userInfo && !UserModel::where('id', $payUid)->inc('total_income', $totalFee)->inc('total_team_income',$totalFee)->update()){
  290. $logData = ['params'=>$params,'payInfo'=>$payInfo,'user'=>$userInfo,'order'=>$orderInfo,'msg'=> "商城订单{$orderSn}更新用户交易数据错误",'date'=> $nowTime];
  291. RedisCache::set($cacheKey."error_{$orderSn}", $logData, $this->cacheTime);
  292. sr_throw("商城订单{$orderSn}更新用户交易数据错误");
  293. }
  294. // 更新团队交易额数据
  295. if($upperPath && !UserModel::whereIn('id', explode(',', $upperPath))->inc('total_team_income',$totalFee)->update()){
  296. $logData = ['params'=>$params,'payInfo'=>$payInfo,'user'=>$userInfo,'order'=>$orderInfo,'msg'=> "商城订单{$orderSn}更新用户上级交易数据错误",'date'=> $nowTime];
  297. RedisCache::set($cacheKey."error_{$orderSn}", $logData, $this->cacheTime);
  298. sr_throw("商城订单{$orderSn}更新用户上级交易数据错误");
  299. }
  300. // 赠送积分处理
  301. $rebateScore = isset($orderInfo['rebate_score']) ? floatval($orderInfo['rebate_score']) : 0;
  302. if ($rebateScore > 0 && $userInfo) {
  303. // 更新用户账户积分
  304. if (!UserModel::where('id', $payUid)->inc('score', $rebateScore)->update()) {
  305. $logData = ['params'=>$params,'payInfo'=>$payInfo,'user'=>$userInfo,'order'=>$orderInfo,'msg'=> "商城订单{$orderSn}赠送积分处理错误",'date'=> $nowTime];
  306. RedisCache::set($cacheKey."error_{$orderSn}", $logData, $this->cacheTime);
  307. sr_throw("商城订单{$orderSn}赠送积分处理错误");
  308. }
  309. // 处理积分流水明细
  310. $userScore = isset($userInfo['score']) ? floatval($userInfo['score']) : 0;
  311. $data = [
  312. 'uid' => $payUid,
  313. 'type' => 3,
  314. 'score' => $rebateScore,
  315. 'create_at' => sr_getcurtime(time()),
  316. 'state' => 12,
  317. 'before_score' => $userScore,
  318. 'after_score' => floatval($userScore + $rebateScore),
  319. 'from_id' => $payId,
  320. 'uid2' => 0
  321. ];
  322. if (!ScoreLogModel::insertGetId($data)) {
  323. $logData = ['params'=>$params,'payInfo'=>$payInfo,'user'=>$userInfo,'order'=>$orderInfo,'msg'=> "商城订单{$orderSn}赠送积分明细处理错误",'date'=> $nowTime];
  324. RedisCache::set($cacheKey."error_{$orderSn}", $logData, $this->cacheTime);
  325. sr_throw("商城订单{$orderSn}赠送积分明细处理错误");
  326. }
  327. }
  328. $logData = ['params'=>$params,'payInfo'=>$payInfo,'user'=>$userInfo,'order'=>$orderInfo,'msg'=> "商城订单{$orderSn}回调处理成功",'date'=> $nowTime];
  329. RedisCache::set($cacheKey."success_{$orderSn}", $logData, $this->cacheTime);
  330. }
  331. return true;
  332. }
  333. }