SplitBillService.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <?php
  2. namespace addons\YunfastPay\common\services;
  3. use addons\Store\common\services\MerchantService;
  4. use addons\YunfastPay\common\models\PaymentOrder;
  5. use addons\YunfastPay\common\models\SplitbillItem;
  6. use addons\YunfastPay\common\models\SplitbillLog;
  7. use addons\YunfastPay\common\services\trans\nocash\SplitBillTrans;
  8. use addons\YunfastPay\common\services\trans\TransAbstract;
  9. use common\enums\RabbitMqEnum;
  10. use Exception;
  11. use Yii;
  12. /**
  13. * 分账
  14. * @package addons\YunfastPay\common\services
  15. */
  16. class SplitBillService extends BaseService
  17. {
  18. /**
  19. * @var int
  20. */
  21. protected $storeId;
  22. /**
  23. * @var SettingService
  24. */
  25. protected $settingService;
  26. /**
  27. * 分账子项数据
  28. *
  29. * @var array
  30. */
  31. protected $itemLog;
  32. /**
  33. * @var YunPayService
  34. */
  35. protected $yunPayService;
  36. /**
  37. * @var PaymentOrder
  38. */
  39. protected $payment;
  40. /**
  41. * @var string
  42. */
  43. protected $orderNo;
  44. /**
  45. * 银典收取的手续费
  46. *
  47. * @var float
  48. */
  49. protected $fee = 0;
  50. /**
  51. * 扣除手续费比例
  52. */
  53. protected const FEE = 0.005;
  54. public function init()
  55. {
  56. $this->settingService = new SettingService(['mall_id' => $this->mall_id]);
  57. $this->yunPayService = new YunPayService(['mall_id' => $this->mall_id]);
  58. }
  59. /**
  60. * 进行分账
  61. *
  62. * @param array $data
  63. * [
  64. * order_no => 订单号
  65. * amount_received => 分配金额
  66. * user_id => 用户ID
  67. * store_id => 店铺ID
  68. * mall_id => 商城ID
  69. * type => 订单类型
  70. * ]
  71. * @return void
  72. */
  73. public function divide(array $data)
  74. {
  75. try {
  76. $this->getPaymentOrder($data['order_no']);
  77. // 设置手续费
  78. $this->setYunfastFeeAmount();
  79. // 获取分账数据
  80. $this->yunPayService->setStoreId($data['store_id']);
  81. $trans = $this->getSplitBillTrans($data['store_id'], (float)$data['amount_received']);
  82. // 是否已经分账
  83. if ($this->payment->isSplitBill()) {
  84. return true;
  85. }
  86. $res = $this->yunPayService->sendPayRq($trans);
  87. $id = $this->addSplitBillLog($res);
  88. } catch (Exception $e) {
  89. Yii::error($e->getMessage());
  90. return false;
  91. }
  92. return true;
  93. }
  94. /**
  95. * 添加分账记录
  96. *
  97. * @return int|false
  98. */
  99. public function addSplitBillLog(array $res)
  100. {
  101. $req_data = $this->yunPayService->getReqData();
  102. $t = Yii::$app->db->beginTransaction();
  103. try {
  104. // 设置分账
  105. if ($res['resCode'] == 'SUCCESS') {
  106. $this->payment->splitBill();
  107. }
  108. // 添加分账记录
  109. $log = SplitbillLog::addLog([
  110. 'mall_id' => $this->mall_id,
  111. 'order_no' => $this->getOrderNo(),
  112. 'payment_id' => $this->payment->id,
  113. 'req_data' => $req_data,
  114. 'res_data' => $res,
  115. 'status' => $res['resCode'] == 'SUCCESS' ? 1 : 0,
  116. ]);
  117. // 分账子项记录
  118. foreach ($this->itemLog as $item) {
  119. $model = SplitbillItem::addLog([
  120. 'mall_id' => $this->mall_id,
  121. 'log_id' => $log->id,
  122. 'store_id' => $item['store_id'],
  123. 'amount' => $item['item10'],
  124. 'role' => $item['item2'],
  125. 'mcht_cd' => $item['item3'],
  126. 'fee_party' => $item['item4'],
  127. ]);
  128. $this->storeFastAgent($model);
  129. }
  130. $t->commit();
  131. return $log->id;
  132. } catch (Exception $e) {
  133. $t->rollBack();
  134. Yii::error('添加分账记录 ' . $e->getLine() . $e->getMessage());
  135. }
  136. return false;
  137. }
  138. /**
  139. * 商家快速到账
  140. *
  141. * @param SplitbillItem $item
  142. * @return void
  143. */
  144. private function storeFastAgent($item)
  145. {
  146. // 添加快速到账任务
  147. if (!empty($item) && ($item->store_id > 0)) {
  148. $data = [
  149. 'id' => $item->id,
  150. 'mall_id' => $item->mall_id,
  151. ];
  152. Yii::$app->services
  153. ->rabbitMq
  154. ->delay(10, $data, HandleMqService::class, 'handleStoreAgent');
  155. }
  156. }
  157. /**
  158. * 获取分账
  159. *
  160. * @param string $order_no
  161. * @param integer $store_id
  162. * @param float $amount
  163. * @return SplitBillTrans
  164. */
  165. public function getSplitBillTrans(
  166. int $store_id,
  167. float $amount
  168. )
  169. {
  170. $trans = new SplitBillTrans(
  171. $this->settingService->getOrgCd(),
  172. $this->getOrderNo(),
  173. $this->payment->getOglOrdId()
  174. );
  175. // 获取分给门店的费用
  176. $store_amount = $this->getStoreAmount($amount);
  177. $this->setSplitBillItemData($trans, $store_id, $store_amount);
  178. $this->setLeftSplitBillItemData($trans);
  179. return $trans;
  180. }
  181. /**
  182. * 获取分给门店的费用
  183. *
  184. * @return float
  185. */
  186. protected function getStoreAmount(float $amount)
  187. {
  188. // 暂时门店承担
  189. $store_amount = bcsub($amount, $this->fee, 2);
  190. return $store_amount > 0 ? $store_amount : $amount;
  191. }
  192. /**
  193. * 判断是否全部给了门店
  194. *
  195. * @param float $amount
  196. * @return boolean
  197. */
  198. protected function isAllToStore(float $amount)
  199. {
  200. return $amount == $this->payment->amount;
  201. }
  202. /**
  203. * 设置剩下的金额分账给平台
  204. *
  205. * @param SplitBillTrans $trans
  206. * @return void
  207. */
  208. public function setLeftSplitBillItemData(SplitBillTrans $trans)
  209. {
  210. $left = $this->getLeftAmount($trans);
  211. if ($left > 0) {
  212. $this->setSplitBillItemData($trans, 0, $left);
  213. }
  214. }
  215. /**
  216. * 获取支付订单
  217. *
  218. * @param string $order_no
  219. * @return void
  220. */
  221. public function getPaymentOrder($order_no)
  222. {
  223. $this->payment = PaymentOrder::getPaymentByCashierOrderNo($order_no);
  224. if (empty($this->payment)) {
  225. throw new Exception('当前支付方式不支持');
  226. }
  227. }
  228. /**
  229. * 获取店铺分账数据
  230. *
  231. * @return string
  232. */
  233. public function getStoreMchtCd(int $store_id)
  234. {
  235. // 当店铺ID为0 返回平台mchtId
  236. if ($store_id == 0) return $this->settingService->getMchtCd();
  237. $merchant = new MerchantService(['mall_id' => $this->mall_id, 'store_id' => $store_id]);
  238. return $merchant->getMchtCd();
  239. }
  240. /**
  241. * 设置子项分账数据
  242. *
  243. * @param TransAbstract $trans
  244. * @param integer $store_id
  245. * @param float $amount
  246. * @return void
  247. */
  248. public function setSplitBillItemData(
  249. TransAbstract $trans, int $store_id, float $amount
  250. )
  251. {
  252. if ($amount > 0) {
  253. $trans->setSplitBillItem(
  254. $this->getStoreMchtCd($store_id),
  255. $amount
  256. );
  257. $this->itemLog[] = array_merge(
  258. $trans->getSplitBillItem(),
  259. [
  260. 'store_id' => $store_id
  261. ]
  262. );
  263. }
  264. }
  265. /**
  266. * 设置银典收取的手续费
  267. *
  268. * @return void
  269. */
  270. protected function setYunfastFeeAmount()
  271. {
  272. $this->fee = round(bcmul($this->payment->amount, static::FEE, 3), 2);
  273. }
  274. /**
  275. * 获取分账之后剩下的金额(分给平台用)
  276. *
  277. * @return float
  278. */
  279. protected function getLeftAmount(SplitBillTrans $trans)
  280. {
  281. $totalAmount = array_sum(array_column($trans->getSplitBill(), 'item10'));
  282. return bcsub($this->getTotalAmount(), $totalAmount, 3);
  283. }
  284. /**
  285. * 获取一共可以分配的金额
  286. *
  287. * @return float
  288. */
  289. protected function getTotalAmount()
  290. {
  291. return bcsub($this->payment->amount, $this->fee, 2);
  292. }
  293. /**
  294. * 获取一个订单号
  295. *
  296. * @return string
  297. */
  298. protected function getOrderNo()
  299. {
  300. return 'BS' . date('YmdHis') . mt_rand(100, 999);
  301. }
  302. }