OrderService.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. <?php
  2. namespace addons\Alitopay\common\services;
  3. use addons\Alitopay\common\jdk\alipay\request\AlipayDataBillEreceiptApplyRequest;
  4. use addons\Alitopay\common\jdk\alipay\request\AlipayDataBillEreceiptQueryRequest;
  5. use addons\Alitopay\common\jdk\alipay\request\AlipayFundTransCommonQueryRequest;
  6. use addons\Alitopay\common\logic\MiddleApi;
  7. use addons\Alitopay\common\models\Apply;
  8. use addons\Alitopay\common\models\Order;
  9. use common\components\export\CsvTool;
  10. use common\helpers\CurlHelper;
  11. use common\helpers\LockHelper;
  12. use common\helpers\UploadHelper;
  13. use common\models\common\Attachment;
  14. use common\models\common\MiddleMch;
  15. use common\models\user\UserWithdrawLog;
  16. use common\models\user\UserWithdrawLog as Withdraw;
  17. use Omnipay\Omnipay;
  18. use yii\base\BaseObject;
  19. use yii\helpers\Json;
  20. class OrderService extends BaseObject
  21. {
  22. public $error;
  23. public function error($msg)
  24. {
  25. $this->error = $msg;
  26. return false;
  27. }
  28. /**
  29. * 提现打款
  30. *
  31. * @param array $params
  32. * @return array [boolean, string]
  33. */
  34. public function withdrawToPay($params)
  35. {
  36. $mall_id = $params['mall_id'] ?? '';
  37. $order_ids = $params['ids'] ?? '';
  38. /**
  39. * @var Apply
  40. */
  41. $apply = Apply::find()->where(['mall_id' => $mall_id])->one();
  42. if (empty($apply)) {
  43. return [false, "支付宝代付配置未设置"];
  44. }
  45. if ($apply->auth_status != 2) {
  46. return [false, "支付宝代付未授权"];
  47. }
  48. $list = Withdraw::find()
  49. ->where([
  50. 'id' => $order_ids,
  51. 'mall_id' => $mall_id,
  52. 'status' => Withdraw::STATUS_AGREE,
  53. 'type' => "alipay",
  54. ])
  55. ->asArray()
  56. ->all();
  57. if (empty($list)) {
  58. return [false, "没有符合条件的提现记录"];
  59. }
  60. $uniqid = uniqid();
  61. $lock_key = "alitopay_order_lock_" . $mall_id;
  62. if (!LockHelper::lock($lock_key, $uniqid)) {
  63. return [false, "操作频繁请稍后重试"];
  64. };
  65. try {
  66. $order_insert = $trans_list = [];
  67. foreach ($list as $cash) {
  68. // 收款对象
  69. $ali_user = Json::decode($cash['extra']);
  70. if (empty($ali_user['name']) || empty($ali_user['mobile'])) {
  71. return [false, "订单{$cash['order_no']},支付宝名称或者支付宝账号为空"];
  72. }
  73. if ($cash['actual_num'] < 1) {
  74. return [false, "订单{$ali_user['name']},打款金额必须大于1元"];
  75. }
  76. // $ali_user['name'] = '陆福佳';
  77. // $ali_user['mobile'] = '15820232749';
  78. $one['mall_id'] = $apply->mall_id;
  79. $one['order_sn'] = time() . rand(1000, 9999);
  80. $one['type'] = Order::TYPE_WITHDRAWAL;
  81. $one['money'] = $cash['actual_num'];
  82. $one['name'] = $ali_user['name'];
  83. $one['account'] = $ali_user['mobile'];
  84. $one['source_id'] = $cash['id'];
  85. $one['source_sn'] = $cash['order_no'];
  86. $one['created_at'] = time();
  87. $one['updated_at'] = time();
  88. $order_insert[] = $one;
  89. }
  90. $db = \Yii::$app->db->beginTransaction();
  91. $res = \Yii::$app->db
  92. ->createCommand()
  93. ->batchInsert(Order::tableName(), array_keys($order_insert[0]), $order_insert)
  94. ->execute();
  95. if (!$res) {
  96. LockHelper::uLock($lock_key, $uniqid);
  97. $db->rollBack();
  98. return [false, "创建代付订单失败"];
  99. }
  100. // 修改提现记录,打款中
  101. $res = UserWithdrawLog::updateAll(['status' => 5, 'updated_at' => time()], ['id' => $order_ids]);
  102. if (!$res) {
  103. LockHelper::uLock($lock_key, $uniqid);
  104. $db->rollBack();
  105. return [false, "修改提现记录失败"];
  106. }
  107. // 发起代付请求
  108. try {
  109. $res = $this->ali_to_pay($apply, $order_insert);
  110. if ($res == false) {
  111. LockHelper::uLock($lock_key, $uniqid);
  112. $db->rollBack();
  113. return [false, $this->error];
  114. } else {
  115. LockHelper::uLock($lock_key, $uniqid);
  116. $db->commit();
  117. }
  118. return [true, $res];
  119. } catch (\Exception$e) {
  120. \Yii::error($e);
  121. LockHelper::uLock($lock_key, $uniqid);
  122. $db->rollBack();
  123. return [false, $this->error($e->getMessage())];
  124. }
  125. } catch (\Exception$e) {
  126. \Yii::error($e);
  127. isset($db) && $db->rollBack();
  128. LockHelper::uLock($lock_key, $uniqid);
  129. return [false, $e->getMessage()];
  130. }
  131. }
  132. public function orderToPay($mall_id, $ids)
  133. {
  134. }
  135. public function withdrawChannel($param)
  136. {
  137. $mall_id = $param['mall_id'] ?? 0;
  138. $data = Apply::findOne(['mall_id' => $mall_id]);
  139. $is_install = 0;
  140. if (!empty($data) && $data['auth_status'] == 2) {
  141. $is_install = 1;
  142. }
  143. return [
  144. "alitopay" => [
  145. 'is_install' => $is_install,
  146. ],
  147. ];
  148. }
  149. public function orderQuery($id)
  150. {
  151. $order = Order::findOne($id);
  152. if ($order->status != 2) {
  153. return $this->error("订单状态不是打款中...");
  154. }
  155. $mch = MiddleMch::getMch($order->mall_id);
  156. try {
  157. $api = new MiddleApi($mch);
  158. $res = $api->query($order->batch_sn);
  159. if (isset($res['order_list'])) {
  160. $this->orderNotifyHandle($res['order_list']);
  161. return true;
  162. }
  163. return $this->error("查询失败");
  164. } catch (\Exception$e) {
  165. return $this->error($e->getMessage());
  166. }
  167. }
  168. /**
  169. * 支付宝代付
  170. *
  171. * @param Apply $apply
  172. * @param array $orders
  173. * @return string|false
  174. */
  175. private function ali_to_pay(Apply $apply, $orders)
  176. {
  177. $order_sn = [];
  178. foreach ($orders as $order) {
  179. /** 创建支付宝数据 start **/
  180. $data['out_biz_no'] = $order['order_sn'];
  181. $data['trans_amount'] = $order['money'];
  182. $data['order_title'] = '收益';
  183. $data['payee_info'] = [
  184. 'identity' => $order['account'],
  185. 'identity_type' => 'ALIPAY_LOGON_ID',
  186. 'name' => $order['name'],
  187. ];
  188. $trans_list[] = $data;
  189. $order_sn[] = $order['order_sn'];
  190. /** 创建支付宝数据 end **/
  191. }
  192. $out_batch_no = 'TX' . date("Ymd") . $apply->mall_id . rand(1000, 9999);
  193. Order::updateAll(["batch_sn" => $out_batch_no], ['mall_id' => $apply->mall_id, 'order_sn' => $order_sn]);
  194. $title = '收益提现';
  195. $api_data = [
  196. 'alipay_app_id' => $apply->alipay_app_id,
  197. 'out_batch_no' => $out_batch_no,
  198. 'order_title' => $title,
  199. 'trans_order_list' => Json::encode($trans_list),
  200. ];
  201. // 发起代付请求
  202. try {
  203. $mch = MiddleMch::getMch($apply->mall_id);
  204. $api = new MiddleApi($mch);
  205. $res = $api->pay($api_data);
  206. if (!isset($res['form_html'])) {
  207. return $this->error("代付订单 form_html 未定义");
  208. }
  209. Order::updateAll(["status" => 2, 'updated_at' => time()], ['mall_id' => $apply->mall_id, 'order_sn' => $order_sn]);
  210. return $res['form_html'];
  211. } catch (\Exception$e) {
  212. \Yii::error($e);
  213. return $this->error($e->getMessage());
  214. }
  215. }
  216. public function orderNotifyHandle($order_list)
  217. {
  218. foreach ($order_list as $item) {
  219. $order = Order::find()->where(['order_sn' => $item['out_biz_no'], 'status' => 2])->one();
  220. if (empty($order)) {
  221. continue;
  222. }
  223. switch ($item['status']) {
  224. case "success":
  225. // 成功
  226. $res = $this->_success($order, $item);
  227. break;
  228. case "wait":
  229. // 打款中,不做处理
  230. break;
  231. case "fail":
  232. $res = $this->_fail($order, $item);
  233. // 失败
  234. break;
  235. case "disuse":
  236. // 取消,也作为失败
  237. break;
  238. }
  239. }
  240. return true;
  241. }
  242. private function _success(Order $order, $response)
  243. {
  244. $res = Order::updateAll(['status' => 3, 'msg' => '成功', 'updated_at' => time()], ['id' => $order->id]);
  245. if (!$res) {
  246. return $this->error("修改订单状态success失败");
  247. }
  248. if ($order->type == 2) {
  249. // 手动打款的,直接返回成功
  250. return true;
  251. }
  252. // 提现打款,要修改提现记录
  253. $cash = UserWithdrawLog::findOne($order->source_id);
  254. UserWithdrawLog::remit_success($cash);
  255. // 提现成功...
  256. return true;
  257. }
  258. private function _fail(Order $order, $response)
  259. {
  260. $msg = $response['error_msg'] ?? "未知错误";
  261. $res = Order::updateAll(['status' => 4, 'msg' => $msg, 'updated_at' => time()], ['id' => $order->id]);
  262. if (!$res) {
  263. return $this->error("修改订单状态success失败");
  264. }
  265. if ($order->type == 2) {
  266. // 手动打款的,直接返回成功
  267. return true;
  268. }
  269. // 提现打款,要修改提现记录
  270. $cash = UserWithdrawLog::findOne($order->source_id);
  271. UserWithdrawLog::remit_fail($cash, $msg);
  272. // 失败...
  273. return true;
  274. }
  275. public function list($mall_id, $param)
  276. {
  277. $name = $param['name'] ?? '';
  278. $batch_sn = $param['batch_sn'] ?? '';
  279. $account = $param['account'] ?? '';
  280. $source_sn = $param['source_sn'] ?? '';
  281. $limit = $param['limit'] ?? 10;
  282. $page = $param['page'] ?? 1;
  283. $type = $param['type'] ?? '';
  284. $status = $param['status'] ?? '';
  285. $where[] = ["=", "mall_id", $mall_id];
  286. $name && $where[] = ["=", "name", $name];
  287. $batch_sn && $where[] = ["=", "batch_sn", $batch_sn];
  288. $account && $where[] = ["=", "account", $account];
  289. $source_sn && $where[] = ["=", "source_sn", $source_sn];
  290. $type && $where[] = ["=", "type", $type];
  291. $status && $where[] = ["=", "status", $status];
  292. $params = array('where' => $where, 'order' => 'created_at desc', 'limit' => $limit);
  293. $list = Order::listPage($params, false, true);
  294. return $list;
  295. // $pagination = new Pagination(['totalCount' => $model->count(), 'pageSize' => $limit]);
  296. // $model->limit($pagination->limit)->offset($pagination->offset);
  297. // $list = $model->asArray()
  298. // ->orderBy('created_at desc')
  299. // ->all();
  300. // $data['list'] = $list;
  301. // $data['page_count'] = $pagination->pageCount;
  302. // $data['current_page'] = $page + 1;
  303. // return $data;
  304. }
  305. /**
  306. * 返回前端提现方式
  307. *
  308. * @param array $params
  309. * @return array
  310. */
  311. public static function getWithdrawWay($params)
  312. {
  313. $mall_id = $params['mall_id'];
  314. // mall_id为空 后台显示提现方式
  315. if (empty($mall_id)) return ['alipay' => '支付宝代付'];
  316. $apply = (new ApplyService())->getApply($mall_id);
  317. if ($apply && $apply['auth_status'] == 2) {
  318. return ['alipay' => '支付宝代付'];
  319. }
  320. return [];
  321. }
  322. public function ereceiptApply($mall_id, Order $order)
  323. {
  324. if (empty($order)) throw new \Exception('订单不存在');
  325. $alipay = new AlipayService($mall_id);
  326. $request = new AlipayDataBillEreceiptApplyRequest();
  327. $params = [
  328. 'type' => 'FUND_DETAIL',
  329. 'key' => $order->order_id
  330. ];
  331. $request->setBizContent(json_encode($params));
  332. $result = $alipay->getAopClientMall()->execute($request);
  333. $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
  334. $resultCode = $result->$responseNode->code;
  335. if (!empty($resultCode) && $resultCode == 10000) {
  336. $order->file_id = $result->$responseNode->file_id;
  337. $order->save();
  338. return $order->file_id;
  339. } else {
  340. throw new \Exception($result->$responseNode->msg);
  341. }
  342. }
  343. public function ereceiptQuery($mall_id, Order $order)
  344. {
  345. if (empty($order)) throw new \Exception('订单不存在');
  346. $alipay = new AlipayService($mall_id);
  347. $request = new AlipayDataBillEreceiptQueryRequest();
  348. $params = [
  349. 'file_id' => $order->file_id,
  350. ];
  351. $request->setBizContent(json_encode($params));
  352. $result = $alipay->getAopClientMall()->execute($request);
  353. $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
  354. $resultCode = $result->$responseNode->code;
  355. $relative_filename = 'alipay/' . $mall_id;
  356. if (!empty($resultCode) && $resultCode == 10000) {
  357. $file_name = $order->order_id . '_' . time();
  358. $absolute_dir = \Yii::getAlias('@attachment') . '/alipay/' . $mall_id . '/';
  359. $res = CurlHelper::downImgRar($result->$responseNode->download_url, $file_name, 'pdf', $absolute_dir);
  360. $obj = new CsvTool();
  361. $url = $obj->upload($mall_id, $relative_filename . '/' . $file_name . '.pdf');
  362. $order->download_url = $url;
  363. $order->save();
  364. unlink($res);
  365. return $url;
  366. } else {
  367. throw new \Exception($result->$responseNode->msg);
  368. }
  369. }
  370. public function fundTransCommonQuery($mall_id, $id)
  371. {
  372. try {
  373. $order = Order::findOne(['mall_id' => $mall_id, 'id' => $id]);
  374. if (empty($order)) throw new \Exception('订单不存在');
  375. $alipay = new AlipayService($mall_id);
  376. $request = new AlipayFundTransCommonQueryRequest();
  377. $params = [
  378. 'out_biz_no' => $order->order_sn,
  379. 'biz_scene' => 'DIRECT_TRANSFER',
  380. 'product_code' => 'TRANS_ACCOUNT_NO_PWD',
  381. ];
  382. $request->setBizContent(json_encode($params));
  383. $result = $alipay->getAopClientMall()->execute($request);
  384. $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
  385. $resultCode = $result->$responseNode->code;
  386. if (!empty($resultCode) && $resultCode == 10000) {
  387. $order->order_id = $result->$responseNode->pay_fund_order_id;
  388. $order->save();
  389. $this->ereceiptApply($mall_id, $order);
  390. sleep(1);
  391. $this->ereceiptQuery($mall_id, $order);
  392. return $result->$responseNode->pay_fund_order_id;
  393. } else {
  394. return $this->error($result->$responseNode->msg);
  395. }
  396. } catch (\Exception $e) {
  397. return $this->error($e->getMessage());
  398. }
  399. }
  400. }