MixedPayService.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. <?php
  2. namespace services\common;
  3. use common\enums\PayTypeEnum;
  4. use common\events\addons\PaymentTypeEvent;
  5. use Yii;
  6. use yii\helpers\Json;
  7. use common\models\common\PaymentTrade;
  8. use common\models\user\UserWallet;
  9. use Exception;
  10. /**
  11. * 组合支付服务类
  12. * @package services\common
  13. */
  14. final class MixedPayService extends BaseService
  15. {
  16. /**
  17. * @var array
  18. */
  19. protected $setting;
  20. /**
  21. * 钱包支付方式
  22. */
  23. const WALLET_TYPES = [
  24. PayTypeEnum::BALANCE,
  25. PayTypeEnum::SCORE,
  26. PayTypeEnum::CONSUMER_CARD,
  27. ];
  28. /**
  29. * 目前支持进行组合支付的在线支付方式
  30. */
  31. const ONLINE_TYPES = [
  32. PayTypeEnum::WECHAT,
  33. PayTypeEnum::ALI,
  34. PayTypeEnum::JOINPAY,
  35. PayTypeEnum::HUIFUPAY,
  36. // PayTypeEnum::JOINPAY_FAST_PAY
  37. ];
  38. public function __construct($config)
  39. {
  40. parent::__construct($config);
  41. $this->setting = $this->getPaySetting();
  42. }
  43. /**
  44. * @return void
  45. */
  46. public function setIsPayGroup(PaymentTrade $trade)
  47. {
  48. $trade->is_pay_group = 1;
  49. }
  50. /**
  51. * 当前支付订单钱包是否符合进行组合支付
  52. *
  53. * @param PaymentTrade $trade
  54. * @param array $wallet
  55. * @return boolean
  56. */
  57. public function isConformOfWallteByTrade(PaymentTrade $trade, array $wallet)
  58. {
  59. // 没有订单直接返回
  60. if (empty($trade)) return [];
  61. // 是否开启组合支付
  62. if (!($this->isOpenMixedPay())) return [];
  63. // 判断余额是否足够支付
  64. if ($this->isSufficientOfwallet($trade, $wallet)) return [];
  65. return [
  66. 'balance' => $this->isBalanceEnable(),
  67. 'score' => $this->isScoreEnable(),
  68. ];
  69. }
  70. /**
  71. * 重组支付方式列表,添加余额和积分是否支持组合支付的标识
  72. *
  73. * @param PaymentTrade|null $trade 支付订单
  74. * @param array $list 支付方式列表
  75. * @param array $wallet 钱包
  76. * @param integer $source 来源 1:商品订单;2:余额充值;3:佣金提现;4:余额提现
  77. * @return array
  78. */
  79. public function getPayTypeList($trade, array $list, array $wallet, int $source = 1): array
  80. {
  81. // 给支付方式列表添加钱包支付类型标识
  82. $list = $this->setPayListWalletType($list);
  83. // 来源不是商品订单直接返回
  84. if ($source != 1) return $list;
  85. // 没有订单直接返回
  86. if (empty($trade)) return $list;
  87. // 判断是否已经扣除钱包进行在线支付
  88. if ($trade->isMixedPay() && $trade->getWalletPayItem()->isPay()) {
  89. // return $this->getOnlinePayList($list); // 返回所有在线支付方式
  90. return $this->getCurrOnlinePayList($list, $trade); // 返回当前在线支付方式
  91. }
  92. // 是否开启组合支付
  93. if (!($this->isOpenMixedPay())) return $list;
  94. // 判断余额是否足够支付
  95. if ($this->isSufficientOfwallet($trade, $wallet)) return $list;
  96. return array_map(function($item) use ($trade, $wallet) {
  97. // 判断商品独立设置是否开启组合支付
  98. $isGoodsMixed = true;
  99. if (!empty($trade->limit_payment)) {
  100. $limit_payment = json_decode($trade->limit_payment, true);
  101. if (empty($limit_payment['MIXED'])) $isGoodsMixed = false;
  102. }
  103. $item['is_enable_group'] = 0;
  104. if ((isset($item['pay_type']) && $item['pay_type'] == PayTypeEnum::BALANCE) && $isGoodsMixed && $this->isBalanceEnable() && $this->isPaymentInsufficientByBalance($trade, $wallet)) {
  105. $item['unpaid_money'] = $this->getUnpaidBalance($trade, $wallet);
  106. $item['is_enable_group'] = 1;
  107. }
  108. if (isset($item['pay_type']) && $item['pay_type'] == PayTypeEnum::SCORE && $isGoodsMixed && $this->isScoreEnable() && $this->isPaymentInsufficientByScore($trade, $wallet)) {
  109. $item['unpaid_money'] = $this->getUnpaidScore($trade, $wallet);
  110. $item['is_enable_group'] = 1;
  111. }
  112. //判断消费卡
  113. if (isset($item['pay_type']) && $item['pay_type'] == PayTypeEnum::CONSUMER_CARD && $isGoodsMixed && $item['is_enable_mixed_pay'] == 1 && $this->isPaymentInsufficientByConsumerCard($trade, $wallet)) {
  114. $item['unpaid_money'] = $this->getUnpaidConsumerCard($trade, $wallet);
  115. $item['is_enable_group'] = 1;
  116. }
  117. // 判断是否支持在线支付
  118. if (in_array($item['pay_type'], self::ONLINE_TYPES)) {
  119. $item['is_mixed_online'] = 1;
  120. }
  121. // 不支持「汇付-快捷支付」
  122. if (($item['pay_type'] == PayTypeEnum::HUIFUPAY && ($item['trade_type'] == 'fast_pay' || $item['trade_type'] == 'fastpay'))) {
  123. $item['is_mixed_online'] = 0;
  124. }
  125. return $item;
  126. }, $list);
  127. }
  128. /**
  129. * 给支付方式列表添加钱包支付类型标识
  130. *
  131. * @param array $list
  132. * @return array
  133. */
  134. public function setPayListWalletType(array $list): array
  135. {
  136. return array_map(function($item) {
  137. in_array($item['pay_type'], self::WALLET_TYPES)
  138. ? $item['is_wallet'] = 1
  139. : $item['is_wallet'] = 0;
  140. return $item;
  141. }, $list);
  142. }
  143. /**
  144. * 创建组合支付子订单项
  145. *
  146. * @param PaymentTrade $trade
  147. * @param array $pay_types
  148. * @return boolean
  149. */
  150. public function createTradeItem(PaymentTrade $trade, array $pay_types, array $trade_type)
  151. {
  152. if ($this->isBalancePay($pay_types)) $pay_fee = $this->createBalanceTradeItem($trade);
  153. if ($this->isScorePay($pay_types)) $pay_fee = $this->createScoreTradeItem($trade);
  154. if ($this->isConsumerCardPay($pay_types)) $pay_fee = $this->createConsumerCardTradeItem($trade);
  155. // 未返回钱包支付金额则抛出异常
  156. if (!isset($pay_fee)) throw new Exception('组合支付子订单项创建失败');
  157. // 在线支付方式
  158. $online_pay_type = $this->getOnlinePayType($pay_types);
  159. // 剩余支付金额
  160. $left_fee = bcsub($trade->pay_fee, $pay_fee, 2);
  161. $this->createOnlineTradeItem($trade, $online_pay_type, $left_fee, $trade_type[$online_pay_type] ?? '');
  162. }
  163. /**
  164. * 获取支付
  165. *
  166. * @param array $pay_types
  167. * @param integer $pay_type
  168. * @return string
  169. * @throws Exception
  170. */
  171. public function getTradeType(array $pay_types, int $pay_type)
  172. {
  173. if (isset($pay_types[$pay_type])) {
  174. return $pay_types[$pay_type];
  175. }
  176. throw new Exception('选择交易方式异常');
  177. }
  178. /**
  179. * 获取在线支付方式(余额和积分以外的)
  180. *
  181. * @param array $pay_types
  182. * @return int
  183. * @throws Exception
  184. */
  185. public function getOnlinePayType(array $pay_types): int
  186. {
  187. if (count($left = array_diff($pay_types, self::WALLET_TYPES)) == 1) {
  188. return current($left);
  189. }
  190. throw new Exception('剩余支付方式异常');
  191. }
  192. /**
  193. * 获取钱包支付方式
  194. *
  195. * @param array $pay_types
  196. * @return int
  197. * @throws Exception
  198. */
  199. public function getWalletPayType(array $pay_types): int
  200. {
  201. if (count($right = array_intersect($pay_types, self::WALLET_TYPES)) == 1) {
  202. return current($right);
  203. }
  204. throw new Exception('钱包支付方式异常');
  205. }
  206. /**
  207. * 创建余额支付子订单项并返回扣除的余额
  208. *
  209. * @param PaymentTrade $trade
  210. * @return float
  211. */
  212. public function createBalanceTradeItem(PaymentTrade $trade)
  213. {
  214. $wallet = UserWallet::getUserWallet($trade->user_id);
  215. if ($this->isSufficientOfwallet($trade, $wallet)) {
  216. throw new Exception('余额或者积分充足, 无法进行组合支付');
  217. }
  218. // 余额支付
  219. $balance = $wallet['balance'];
  220. if ($balance == 0) {
  221. throw new Exception('余额不足, 无法进行组合支付');
  222. }
  223. $trade->setWalletPayItem(PayTypeEnum::BALANCE, $balance);
  224. return $balance;
  225. }
  226. /**
  227. * 创建积分支付子订单项并返回扣除的积分
  228. *
  229. * @param PaymentTrade $trade
  230. * @return float
  231. */
  232. public function createScoreTradeItem(PaymentTrade $trade)
  233. {
  234. $wallet = UserWallet::getUserWallet($trade->user_id);
  235. if ($this->isSufficientOfwallet($trade, $wallet)) {
  236. throw new Exception('余额或者积分充足, 无法进行组合支付');
  237. }
  238. // 积分支付
  239. $score = $wallet['total_score'];
  240. if ($score == 0) {
  241. throw new Exception('积分不足, 无法进行组合支付');
  242. }
  243. $trade->setWalletPayItem(PayTypeEnum::SCORE, $score);
  244. return $score;
  245. }
  246. /**
  247. * 创建消费卡支付子订单项并返回扣除的金额
  248. *
  249. * @param PaymentTrade $trade
  250. * @return float
  251. */
  252. public function createConsumerCardTradeItem(PaymentTrade $trade)
  253. {
  254. // $wallet = UserWallet::getUserWallet($trade->user_id);
  255. //获取消费卡金额
  256. $wallet = \addons\ScoreGame\common\models\ScoreGameUserWallet::getUserWalletType($trade->user_id, \addons\ScoreGame\common\enums\ScoreGameEnum::WALLET_TYPE_CONSUMER_CARD, 0);
  257. //判断余额、积分、消费卡余额是否充足
  258. if ($this->isSufficientOfwallet($trade, $wallet) && $wallet['num'] > $trade->pay_fee) {
  259. throw new Exception('余额、积分或消费卡金额充足, 无法进行组合支付');
  260. }
  261. // 消费卡支付
  262. $consumer_card = $wallet['num'] ?? 0;
  263. if ($consumer_card == 0) {
  264. throw new Exception('消费卡金额不足, 无法进行组合支付');
  265. }
  266. $trade->setWalletPayItem(PayTypeEnum::CONSUMER_CARD, $consumer_card);
  267. return $consumer_card;
  268. }
  269. /**
  270. * 创建在线支付子订单项
  271. *
  272. * @param PaymentTrade $trade
  273. * @param integer $pay_type
  274. * @param float $pay_fee
  275. * @param string $trade_type
  276. * @return boolean
  277. */
  278. public function createOnlineTradeItem(PaymentTrade $trade, int $pay_type, float $pay_fee, string $trade_type = '')
  279. {
  280. return $trade->setOnlinePayItem($pay_type, $pay_fee, $trade_type);
  281. }
  282. /**
  283. * @param array $pay_types
  284. * @return boolean
  285. */
  286. public function isBalancePay(array $pay_types): bool
  287. {
  288. return in_array(PayTypeEnum::BALANCE, $pay_types);
  289. }
  290. /**
  291. * @param array $pay_types
  292. * @return boolean
  293. */
  294. public function isScorePay(array $pay_types): bool
  295. {
  296. return in_array(PayTypeEnum::SCORE, $pay_types);
  297. }
  298. /**
  299. * @param array $pay_types
  300. * @return boolean
  301. */
  302. public function isConsumerCardPay(array $pay_types): bool
  303. {
  304. return in_array(PayTypeEnum::CONSUMER_CARD, $pay_types);
  305. }
  306. /**
  307. * 仅返回在线支付方式
  308. *
  309. * @return array
  310. */
  311. public function getOnlinePayList(array $list)
  312. {
  313. return array_filter($list, function ($item) {
  314. return !in_array($item['pay_type'], self::WALLET_TYPES);
  315. });
  316. }
  317. /**
  318. * 仅返回绑定的在线支付方式
  319. *
  320. * @return array
  321. */
  322. public function getCurrOnlinePayList(array $list, PaymentTrade $trade)
  323. {
  324. return array_filter($list, function ($item) use ($trade) {
  325. return in_array($item['pay_type'], [$trade->getOnlinePayItem()->pay_type]);
  326. });
  327. }
  328. /**
  329. * @return array
  330. */
  331. protected function getPaySetting()
  332. {
  333. $config = Yii::$app->services->setting->mall->get($this->mall_id, 'pay', true);
  334. return array_map(function($item) {
  335. return Json::decode($item);
  336. }, $config);
  337. }
  338. /**
  339. * @return boolean
  340. */
  341. protected function isOpenMixedPay()
  342. {
  343. return isset($this->setting['mixed']['status']) && $this->setting['mixed']['status'];
  344. }
  345. /**
  346. * 钱包的积分或者余额是否足够支付
  347. *
  348. * @param PaymentTrade $trade
  349. * @param array $wallet
  350. * @return boolean
  351. */
  352. protected function isSufficientOfwallet(PaymentTrade $trade, array $wallet)
  353. {
  354. if (isset($wallet['consumer_card'])) {
  355. return $wallet['balance'] > $trade->pay_fee && $wallet['total_score'] > $trade->pay_fee && $wallet['consumer_card'] > $trade->pay_fee;
  356. }
  357. return $wallet['balance'] > $trade->pay_fee && $wallet['total_score'] > $trade->pay_fee;
  358. }
  359. /**
  360. * 余额是否开启组合支付
  361. *
  362. * @return boolean
  363. */
  364. protected function isBalanceEnable()
  365. {
  366. return isset($this->setting['balance']['is_enable_group']) && $this->setting['balance']['is_enable_group'];
  367. }
  368. /**
  369. * 积分是否开启组合支付
  370. *
  371. * @return boolean
  372. */
  373. protected function isScoreEnable()
  374. {
  375. return isset($this->setting['score']['is_enable_group']) && $this->setting['score']['is_enable_group'];
  376. }
  377. /**
  378. * 余额支付是否充足
  379. * @param PaymentTrade $trade
  380. * @param array $wallet
  381. * @return boolean
  382. */
  383. protected function isPaymentInsufficientByBalance(PaymentTrade $trade, array $wallet)
  384. {
  385. return $wallet['balance'] > 0 && bcsub($trade->pay_fee, $wallet['balance'], 2) > 0;
  386. }
  387. /**
  388. * 积分支付是否充足
  389. *
  390. * @param PaymentTrade $trade
  391. * @param array $wallet
  392. * @return boolean
  393. */
  394. protected function isPaymentInsufficientByScore(PaymentTrade $trade, array $wallet)
  395. {
  396. return $wallet['total_score'] > 0 && bcsub($trade->pay_fee, $wallet['total_score'], 2) > 0;
  397. }
  398. /**
  399. * 消费卡支付是否充足
  400. *
  401. * @param PaymentTrade $trade
  402. * @param array $wallet
  403. * @return boolean
  404. */
  405. protected function isPaymentInsufficientByConsumerCard(PaymentTrade $trade, array $wallet)
  406. {
  407. return $wallet['consumer_card'] > 0 && bcsub($trade->pay_fee, $wallet['consumer_card'], 2) > 0;
  408. }
  409. /**
  410. * 获取未支付的余额
  411. *
  412. * @param PaymentTrade $trade
  413. * @param array $wallet
  414. * @return float
  415. */
  416. protected function getUnpaidBalance(PaymentTrade $trade, array $wallet)
  417. {
  418. return bcsub($trade->total_fee, $wallet['balance'], 2);
  419. }
  420. /**
  421. * 获取未支付的积分
  422. *
  423. * @param PaymentTrade $trade
  424. * @param array $wallet
  425. * @return float
  426. */
  427. protected function getUnpaidScore(PaymentTrade $trade, array $wallet)
  428. {
  429. return bcsub($trade->total_fee, $wallet['total_score'], 2);
  430. }
  431. /**
  432. * 获取未支付的消费卡
  433. *
  434. * @param PaymentTrade $trade
  435. * @param array $wallet
  436. * @return float
  437. */
  438. protected function getUnpaidConsumerCard(PaymentTrade $trade, array $wallet)
  439. {
  440. return bcsub($trade->total_fee, $wallet['consumer_card'], 2);
  441. }
  442. }