Pay.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <?php
  2. /**
  3. * 玩家充值相关公共业务逻辑类
  4. *
  5. */
  6. namespace app\common\logic;
  7. use app\common\model\MemberCoinInfo;
  8. use think\Cache;
  9. use app\common\library\Mail;
  10. use app\common\model\Members;
  11. use app\common\model\Game;
  12. use app\common\model\PaySetting;
  13. use app\common\model\PayWarningRule;
  14. use think\Db;
  15. use think\Env;
  16. use think\Exception;
  17. class Pay
  18. {
  19. /**
  20. * 设置充值预警的redis信息
  21. *
  22. * @param $member_id int 玩家ID
  23. * @param $channel_id int 渠道ID
  24. * @param $game_id int 游戏ID
  25. * @param $amount 充值金额
  26. *
  27. */
  28. public function payWarning($member_id, $channel_id, $game_id, $amount)
  29. {
  30. //配置信息
  31. $settingInfo = (new PaySetting)->getPaySetting();
  32. //规则列表
  33. $rule_list = (new PayWarningRule)->getPaySetting();
  34. $settingInfo['whitelist_channel'] = empty($settingInfo['whitelist_channel']) ? [] : explode(',', $settingInfo['whitelist_channel']);
  35. //功能状态开启并且有设置预警规则时
  36. if (isset($settingInfo['status']) && $settingInfo['status'] == 1 && !in_array($channel_id, $settingInfo['whitelist_channel']) && count($rule_list) > 0) {
  37. $this->payTimesOverLimit($rule_list, $member_id, $channel_id, $game_id, $amount);
  38. }
  39. }
  40. /**
  41. * 充值预警是否超过限制次数
  42. *
  43. * @param $rule_list array 规则列表
  44. *
  45. */
  46. public function payTimesOverLimit($rule_list, $member_id, $channel_id, $game_id, $amount)
  47. {
  48. foreach ($rule_list as $v) {
  49. $key = 'clearable_pay_warning:channelid:' . $channel_id . 'gameid:' . $game_id . 'member_id:' . $member_id . 'ruleid:' . $v['id'] . 'amount_' . $amount;
  50. //包含6元
  51. if ($v['is_six']) {
  52. if ($v['min_amount'] <= $amount && $amount <= $v['max_amount']) {
  53. $res = $this->setRedisByRule($key, $v['order_count'], $v['warning_time'], $member_id, $game_id);
  54. }
  55. } //不包含6元
  56. else {
  57. if ($amount != 6 && $v['min_amount'] <= $amount && $amount <= $v['max_amount']) {
  58. $res = $this->setRedisByRule($key, $v['order_count'], $v['warning_time'], $member_id, $game_id);
  59. }
  60. }
  61. }
  62. }
  63. /**
  64. * 满足规则时,设置redis缓存计数
  65. *
  66. * @param $key string redis的key
  67. * @param $times int 累加次数
  68. * @param $expire int redis有效期
  69. * @param $member_id int 玩家ID
  70. * @param $game_id int 游戏ID
  71. *
  72. */
  73. public function setRedisByRule($key, $times, $expire, $member_id, $game_id)
  74. {
  75. if (empty($key)) {
  76. return false;
  77. }
  78. $times = (int)$times;
  79. $expire = (int)$expire * 60; //规则中设置的预警时间阈值
  80. $redis = Cache::store('default')->handler();
  81. $value = $redis->incr($key); //充值次数
  82. //超过预警规则的次数时
  83. if ($times <= $value) {
  84. $redis->del($key);
  85. //发送预警邮件
  86. $this->sendWarningMail($member_id, $game_id);
  87. return true; //代表可以发送邮件
  88. } //首次时
  89. else if ('1' == $value) {
  90. $redis->expire($key, $expire);
  91. }
  92. return false;
  93. }
  94. /**
  95. * 发送预警邮件
  96. *
  97. */
  98. public function sendWarningMail($member_id, $game_id)
  99. {
  100. //配置信息
  101. $settingInfo = (new PaySetting)->getPaySetting();
  102. //用户名
  103. $username = (new Members())->where(['id' => $member_id])->value('username');
  104. //游戏名称
  105. $game_name = (new Game())->where(['id' => $game_id])->value('name');
  106. /*
  107. //发送邮件
  108. (new Mail())->sendMail("{$username}账号 在 {$game_name} 充值存在异常,请及时核实,时间:".date('Y-m-d H:i:s'), explode(';',$settingInfo['emails']));
  109. */
  110. $template = $username . " 账号在游戏 " . $game_name . " 充值存在异常,请及时核实,时间:" . date('Y-m-d H:i:s');
  111. $ddurl = Env::get('dingtalk.warning_url');
  112. $result = curlDD($template, $ddurl, true);
  113. }
  114. //----------------------------------------------------------------------------------//
  115. /**
  116. * 计算订单各项支付金额
  117. *
  118. * @param $amount :订单总金额
  119. * @param $coin_amount :专属币余额
  120. * @param $ptb_amount :使用平台币金额
  121. *
  122. * @return $result array
  123. */
  124. public function calPayAmount($amount, $ptb_amount = 0,$discount = 1)
  125. {
  126. $result['real_amount'] = 0; // 实际支付金额
  127. $result['real_ptb'] = 0; // 使用平台币的金额
  128. $result['real_coin'] = 0; // 真实平台币
  129. // 先计算折扣后的应付总额
  130. $discounted_amount = round(bcmul((string)$amount, (string)$discount, 3), 2);
  131. // 确保最小支付 0.01
  132. if($amount != 0 && $discounted_amount < 0.01){
  133. $discounted_amount = 0.01;
  134. }
  135. // 如果专属币大于等于折扣后的总额
  136. if ($ptb_amount > 0 && bccomp((string)$ptb_amount, $discounted_amount, 2) >= 0) {
  137. $result['real_ptb'] = $discounted_amount;
  138. $result['real_amount'] = 0;
  139. $result['pay_amount'] = $result['real_ptb'];
  140. } else {
  141. $diff = bcsub($discounted_amount, $ptb_amount, 2);
  142. $result['real_amount'] = $diff; // 四舍五入到 2 位
  143. $result['real_ptb'] = $ptb_amount;
  144. $result['pay_amount'] = round(bcadd($diff, $ptb_amount, 3), 2);
  145. }
  146. $result['real_coin'] = 0;
  147. return $result;
  148. }
  149. /**
  150. * 专属币相关数据更新处理
  151. *
  152. * @param array $coinInfo 使用的平台币信息
  153. * @param array $payData 消费的订单信息
  154. *
  155. */
  156. public function saveCoinData($coinInfo, $payData)
  157. {
  158. $userCoinInfo = model('MemberZscoin')->field('id,userid,username,game_id,amount,status')->where(['userid' => $coinInfo['userid'], 'game_id' => $coinInfo['game_id']])->find();
  159. if (!empty($userCoinInfo)) {
  160. $coinData = array();
  161. $coinData['amount'] = Db::raw("amount-" . $payData['real_ptb']);
  162. $coinData['update_time'] = time();
  163. $result = model('MemberZscoin')->where(['id' => $userCoinInfo['id'], 'userid' => $userCoinInfo['userid'], 'game_id' => $userCoinInfo['game_id'], 'amount' => ['>=', $payData['real_ptb']]])->update($coinData);
  164. if (!$result) {
  165. throw new Exception("用户游戏平台币账户金额变动失败");
  166. }
  167. $coinDetData = array();
  168. $coinDetData['userid'] = $payData['userid'];
  169. $coinDetData['username'] = $payData['username'];
  170. $coinDetData['game_id'] = $payData['gameid'];
  171. $coinDetData['type'] = 3;
  172. $coinDetData['prev_amount'] = $userCoinInfo['amount'];
  173. $coinDetData['change_amount'] = -$payData['real_ptb'];
  174. $coinDetData['after_amount'] = $coinDetData['prev_amount'] - $payData['real_ptb'];
  175. $coinDetData['out_orderid'] = $payData['orderid'];
  176. $coinDetData['create_time'] = time();
  177. $coinDetData['update_time'] = time();
  178. $insertDetId = model('MemberZscoinDet')->insertGetId($coinDetData);
  179. if (!$insertDetId) {
  180. throw new Exception("添加用户游戏平台币变动明细失败");
  181. }
  182. } else {
  183. throw new Exception("游戏平台币账户余额不足");
  184. }
  185. }
  186. /**
  187. * 计算订单各项支付金额
  188. *
  189. * @param $amount string 订单总金额
  190. * @param $coin_amount double 平台币余额
  191. * @param $discount double 折扣
  192. *
  193. * @return $result array
  194. */
  195. public function calCoinPayAmount($amount, $coin_amount = 0,$discount = 1)
  196. {
  197. $result['real_amount'] = 0; // 实际支付金额
  198. $result['real_ptb'] = 0; // 使用平台币的金额
  199. $result['real_coin'] = 0; // 专属平台币
  200. // 先计算折扣后的应付总额
  201. $discounted_amount = round(bcmul((string)$amount, (string)$discount, 3), 2);
  202. // 确保最小支付 0.01
  203. if($amount != 0 && $discounted_amount < 0.01){
  204. $discounted_amount = 0.01;
  205. }
  206. // 如果平台币大于等于折扣后的金额,则支付平台币,否则使用 平台币+第三方支付
  207. if ($amount > 0 && bccomp((string)$coin_amount, $discounted_amount, 2) >= 0) {
  208. $result['real_ptb'] = 0;
  209. $result['real_coin'] = $discounted_amount;
  210. $result['real_amount'] = 0;
  211. $result['pay_amount'] = $result['real_coin'];
  212. } else {
  213. $diff = bcsub($discounted_amount, $coin_amount, 2);
  214. $result['real_ptb'] = 0;
  215. $result['real_amount'] = $diff;
  216. $result['real_coin'] = $coin_amount;
  217. $result['pay_amount'] = round(bcadd($diff, $coin_amount, 3), 2);
  218. }
  219. // dump($result);
  220. return $result;
  221. }
  222. /**
  223. * 平台币相关数据更新处理
  224. *
  225. * @param array $members 使用的平台币信息
  226. * @param array $payInfo 消费的订单信息
  227. *
  228. */
  229. public function saveCoinPayData($members, $payInfo)
  230. {
  231. $membersModel = new Members();
  232. $amount = priceFormat($members['amount'] - $payInfo['real_coin']);
  233. if($amount<0){
  234. return false;
  235. }
  236. $coinData = [
  237. 'userid' => $payInfo['userid'],
  238. 'orderid' => $payInfo['orderid'],
  239. 'start_amount' => $members['amount'],
  240. 'amount' => '-'.$payInfo['real_coin'],
  241. 'result_amount' => $amount,
  242. 'type' => 2,
  243. 'remarks' => '平台币使用',
  244. 'create_time' => NOW_TIMESTAMP
  245. ];
  246. $memberCoinInfoModel = new MemberCoinInfo();
  247. if (!$memberCoinInfoModel->insertGetId($coinData)) {
  248. return false;
  249. }
  250. if (!$membersModel->where('id', $payInfo['userid'])->update(['amount' => $amount])) {
  251. return false;
  252. }
  253. return true;
  254. }
  255. /**
  256. * 代金券使用状态修改
  257. * @param $payData
  258. * @return bool
  259. */
  260. public function saveCouponData($payData){
  261. if($payData['real_amount']>0){
  262. if(model('common/CouponMember')->where(['id'=>$payData['coupon_member_id'],'is_use'=>1,'examine'=>2,'state'=>1])->update(['is_use'=>2,'use_time'=>time()])){
  263. return true;
  264. }else{
  265. return false;
  266. }
  267. }else{
  268. if(model('common/CouponMember')->where(['id'=>$payData['coupon_member_id'],'is_use'=>1,'examine'=>2,'state'=>1])->update(['is_use'=>3,'use_time'=>time()])){
  269. return true;
  270. }else{
  271. return false;
  272. }
  273. }
  274. }
  275. }