PayService.php 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675
  1. <?php
  2. namespace services\common;
  3. use addons\AlipayMiniHuabei\common\enums\AlipayMiniHuabeiEnum;
  4. use addons\CardCoupons\common\models\CardCouponsScoreGive;
  5. use addons\RandFee\common\service\RandFeeService;
  6. use addons\SandPay\common\enums\SandPayEnums;
  7. use common\components\Payment;
  8. use common\components\payment\AliPay;
  9. use common\components\Service;
  10. use common\enums\AddonsEnum;
  11. use common\enums\AliPayPayTypeEnum;
  12. use common\enums\HuijuBillTypeEnum;
  13. use common\enums\JoinPayTypeEnum;
  14. use common\enums\PayReqSourceEnum;
  15. use common\enums\StatusEnum;
  16. use common\enums\UserWalletEnum;
  17. use common\globals\GlobalInvoke;
  18. use common\helpers\StringHelper;
  19. use common\models\common\Addons;
  20. use common\models\common\CapitalLog;
  21. use common\models\common\Log;
  22. use common\models\common\PaymentRefund;
  23. use common\models\common\PaymentTradeLog;
  24. use common\models\common\PaymentTransfer;
  25. use common\models\common\PlatformSetting;
  26. use common\models\mall\MallAddons;
  27. use common\models\order\Order;
  28. use common\models\user\User;
  29. use common\models\user\UserWallet;
  30. use EasyWeChat\Factory;
  31. use Exception;
  32. use Yii;
  33. use common\models\common\PaymentTrade;
  34. use common\models\common\PaymentTradeOrder;
  35. use common\enums\WechatPayTypeEnum;
  36. use common\enums\PayTypeEnum;
  37. use common\enums\SettingEnum;
  38. use common\helpers\Url;
  39. use common\models\common\MallSetting;
  40. use yii\helpers\Json;
  41. use common\models\common\PaymentTradeGroupOrder;
  42. /**
  43. * 支付服务类
  44. * Class PayService
  45. * @package services\common
  46. */
  47. class PayService extends Service
  48. {
  49. /**
  50. * 当前支持的支付方式
  51. * @param $mall_id
  52. * @param string $app_platform
  53. * @param string $req_source 服务请求来源,1:商品订单;2:余额充值;3:佣金提现;4:余额提现; 198:金币充值;
  54. * @param array $extra 额外信息,可能有pay_money,支付金额
  55. * @return array
  56. */
  57. public function getEnablePayment($mall_id=0, $app_platform='h5', $req_source = 1, $extra = [])
  58. {
  59. if ($mall_id) {
  60. $config = Yii::$app->services->setting->mall->get($mall_id, 'pay', true);
  61. } else {
  62. //系统配置
  63. $config = Yii::$app->services->setting->platform->get('pay', true);
  64. }
  65. $enable_pay_type = [];
  66. foreach ($config as $key => $value) {
  67. $payment_mode = json_decode($value, true);
  68. if (!$payment_mode['status']) {
  69. continue;
  70. }
  71. $enable_pay_type[] = $key;
  72. }
  73. $all_payment = PayTypeEnum::allTypeConf($mall_id);
  74. $enable_payment = [];
  75. foreach($enable_pay_type as $pay_type)
  76. {
  77. $payment_info = $all_payment[$pay_type] ?? [];
  78. if(empty($payment_info)) continue;
  79. $trade_type = 'default';
  80. switch ($payment_info['pay_type']) {
  81. case PayTypeEnum::WECHAT:
  82. $trade_type = WechatPayTypeEnum::getTypeByPlatform($app_platform);
  83. break;
  84. case PayTypeEnum::ALI:
  85. $trade_type = AliPayPayTypeEnum::getTypeByPlatform($app_platform);
  86. break;
  87. case PayTypeEnum::JOINPAY:
  88. $trade_type = JoinPayTypeEnum::getTypeByPlatform($app_platform);
  89. break;
  90. case PayTypeEnum::HUIJU_BILL:
  91. $trade_type = HuijuBillTypeEnum::getTypeByPlatform($app_platform);
  92. break;
  93. case PayTypeEnum::SANDPAY_FAST_PAY:
  94. $trade_type = SandPayEnums::getTypeByPlatform($app_platform);
  95. break;
  96. case PayTypeEnum::SCORE:
  97. $trade_type = $pay_type;
  98. break;
  99. case PayTypeEnum::MIXED:
  100. $trade_type = $pay_type;
  101. break;
  102. }
  103. if (!empty($trade_type)) {
  104. $payment_info['action'] = PayTypeEnum::action($payment_info['pay_type']);
  105. $payment_info['trade_type'] = $trade_type;
  106. $payment_mode = json_decode($config[$pay_type], true);
  107. $enable_payment[$pay_type] = $payment_info;
  108. }
  109. }
  110. // 如果是APP
  111. if ($app_platform == User::PLATFORM_APP) {
  112. $groups = SettingEnum::getMallType('mall_basic');
  113. $data = MallSetting::getConfByGroup($mall_id, $groups, true);
  114. $seting = !empty($data['pay']['wechat']['value'])
  115. ? json_decode($data['pay']['wechat']['value'], true) : [];
  116. $close_app_pay = !empty($seting['close_app_pay']) ? $seting['close_app_pay'] : false;
  117. // 后台关闭APP支付
  118. if ($close_app_pay) unset($enable_payment['wechat']);
  119. }
  120. $params['enable_payment']=$enable_payment;
  121. $params['mall_id']=$mall_id;
  122. $params['app_platform'] = $app_platform;
  123. $params['req_source'] = $req_source;
  124. $params['pay_money'] = $extra['pay_money'] ?? 0;
  125. $params['user_id'] = $extra['user_id'] ?? 0;
  126. $return = GlobalInvoke::exec(GlobalInvoke::INVOKE_GET_PAYMENT_LIST,$mall_id,$params);
  127. if(!empty($return)) {
  128. foreach ($return as $key => $item) { // 判断是否有需要绑定父支付方式的,比如支付宝花呗需要绑定到支付宝支付
  129. if (!empty($item['bind_to'])) {
  130. $enable_payment[$item['bind_to']]['children'][] = $item;
  131. unset($return[$key]);
  132. }
  133. }
  134. if (!empty($return)) {
  135. $enable_payment = array_merge($enable_payment,$return);
  136. }
  137. }
  138. // source = 5 收银台。 962 总平台没有开启积分支付的情况下,可以单独设置门店支持积分支付,但是目前仅支持购买门店商品,不支持收银台订单,希望优化
  139. if($req_source == 6 || $req_source == 5){
  140. $payment = \Yii::$app->services->setting->addons->get($mall_id,AddonsEnum::ADDONS_NAME_STORE, 'payment');
  141. if($req_source == 6 && !empty($payment['payment_is_enable'])){
  142. $enable_payment_type = $payment['payment_type'] ? json_decode($payment['payment_type'],true) : [];
  143. foreach($enable_payment as $key => $pay_type){
  144. if(!in_array($pay_type['pay_type'],$enable_payment_type)){
  145. unset($enable_payment[$key]);
  146. }
  147. }
  148. }
  149. if(!empty($payment['score_pay_setting'])){
  150. $pay_type_arr = array_column($enable_payment,'pay_type');
  151. $pay_type = PayTypeEnum::action(PayTypeEnum::SCORE);
  152. if(!in_array(PayTypeEnum::SCORE,$pay_type_arr) && !empty($payment['score_pay'])){
  153. $payment_info = $all_payment[$pay_type];
  154. $payment_info['action'] = $pay_type;
  155. $payment_info['trade_type'] = $pay_type;
  156. $enable_payment[$pay_type] = $payment_info;
  157. }elseif(in_array(PayTypeEnum::SCORE,$pay_type_arr) && empty($payment['score_pay'])){
  158. unset($enable_payment[$pay_type]);
  159. }
  160. }
  161. }
  162. if($req_source == PayReqSourceEnum::SCORE_GAME_GOLD_RECHARGE || $req_source == PayReqSourceEnum::SCORE_GAME_PROJECT_PAY){
  163. //只支持第三方支付
  164. $filer_list = array_keys(PayTypeEnum::mallParty());
  165. foreach ($enable_payment as $key => $item){
  166. if(in_array($item['pay_type'],$filer_list)){
  167. unset($enable_payment[$key]);
  168. }
  169. }
  170. }
  171. $payment_list = array_values($enable_payment);
  172. $params['enable_payment'] = $payment_list;
  173. $params['user_id'] = $extra['user_id'] ?? 0; // 需要传user_id
  174. $filter_result = GlobalInvoke::exec(GlobalInvoke::INVOKE_FILTER_PAYMENT_LIST, $mall_id, $params);
  175. !empty($filter_result) && $payment_list = $filter_result;
  176. return $payment_list;
  177. }
  178. /**
  179. * 支付金额为0的支付
  180. * @param $mall_id
  181. * @return array
  182. */
  183. public function getEnablePayMentByZero($mall_id){
  184. $pay_type_list=[];
  185. $config = Yii::$app->services->setting->mall->get($mall_id, 'pay', true);
  186. $balance_name = \Yii::$app->services->setting->mall->get($mall_id, 'balance.balance_name');
  187. $name = '余额支付';
  188. $name = str_replace('余额',$balance_name,$name);
  189. $payment_mode = json_decode($config['balance'], true);
  190. if(!empty($payment_mode['status'])) {
  191. $protocol = ($_SERVER['SERVER_PORT'] == 443 ? 'https' : 'http') . '://';
  192. $host = $protocol . $_SERVER['HTTP_HOST'];
  193. $pay_type_list[] = [
  194. 'action' => "balance",
  195. 'img' => "{$host}/resources/img/payment/balance.png",
  196. 'name' => $name,
  197. 'pay_type' => PayTypeEnum::BALANCE,
  198. 'trade_type' => "default",
  199. 'is_wallet' => 1
  200. ];
  201. }
  202. return $pay_type_list;
  203. }
  204. /**
  205. * 微信支付
  206. * @param PaymentTrade $trade 交易流水表记录
  207. * @param string $tradeType 微信支付类型
  208. * @param array $params 支付参数
  209. * @return bool|array
  210. * @throws
  211. */
  212. public function wechat(PaymentTrade $trade, $tradeType, $params = [])
  213. {
  214. // 小程序支付
  215. if ($tradeType == WechatPayTypeEnum::MINI_PROGRAM) {
  216. return $this->wechatMp($trade, $params);
  217. }
  218. // 生成订单
  219. $order = [
  220. 'body' => $trade->title, // 内容
  221. 'out_trade_no' => $trade->out_trade_no, // 订单号
  222. 'total_fee' => $trade->getOnlinePayFee() * 100,
  223. 'notify_url' => Url::toApi(['notify/' . PayTypeEnum::action(PayTypeEnum::WECHAT)], TRUE), // 回调地址
  224. 'detail' => $trade->title,
  225. ];
  226. // 判断如果是js支付
  227. $tradeType == WechatPayTypeEnum::JS && $order['openid'] = $params['openid'];
  228. // 判断如果是刷卡支付
  229. //$tradeType == WechatPayTypeEnum::POS && $order['auth_code'] = $params['auth_code'];
  230. //微信配置
  231. $config = $this->getWechatConfig($trade->mall_id);
  232. $result = Yii::$app->payment->wechat($config)->$tradeType($order);
  233. if (empty($result)) {
  234. $debug = Yii::$app->payment->wechat($config)->$tradeType($order, true);
  235. //Yii::$app->services->actionLog->create('wechatPayError', json_encode($debug));
  236. return $debug;
  237. }
  238. return $result;
  239. }
  240. /**
  241. * 微信小程序支付
  242. * @param $trade
  243. * @param array $params
  244. * @return mixed
  245. * @throws
  246. */
  247. public function wechatMp(PaymentTrade $trade, $params = [])
  248. {
  249. //微信配置
  250. $config = $this->getWechatConfig($trade->mall_id);
  251. //微信小程序配置
  252. Yii::$app->params['wechatConfig'] = [
  253. 'app_id' => $config['app_id'] ?? '',
  254. 'secret' => $config['secret'] ?? '',
  255. 'token' => $config['token'] ?? '',
  256. ];
  257. Yii::$app->params['wechatPaymentConfig'] = [
  258. 'app_id' => $config['app_id'] ?? '',
  259. 'secret' => $config['secret'] ?? '',
  260. 'mch_id' => $config['wechat_mch_id'] ?? '',
  261. 'key' => $config['wechat_pay_secret'] ?? '',
  262. 'cert_path' => dirname(Yii::$app->basePath).$config['wechat_cert_pem_file'],
  263. 'key_path' => dirname(Yii::$app->basePath).$config['wechat_key_pem_file'],
  264. 'sandbox' => false,
  265. ];
  266. \Yii::$app->params['wechatMiniProgramConfig'] = [
  267. 'app_id' => $config['mini_program_app_id'] ?? '',
  268. 'secret' => $config['mini_program_secret'] ?? '',
  269. 'mch_id' => $config['wechat_mch_id'] ?? '',
  270. 'key' => $config['wechat_pay_secret'],
  271. 'cert_path' => dirname(Yii::$app->basePath).$config['wechat_cert_pem_file'],
  272. 'key_path' => dirname(Yii::$app->basePath).$config['wechat_key_pem_file'],
  273. ];
  274. $orderData = [
  275. 'trade_type' => 'JSAPI',
  276. 'body' => $trade->title,
  277. 'detail' => $trade->title,
  278. 'notify_url' => Url::toApi(['notify/wechat-mp'], TRUE), // 支付结果通知网址
  279. 'out_trade_no' => $trade->out_trade_no, // 支付
  280. 'total_fee' => $trade->getOnlinePayFee() * 100,
  281. 'openid' => $params['openid'] ?? '', // trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识,
  282. ];
  283. $payment = Factory::payment(Yii::$app->params['wechatMiniProgramConfig']);
  284. // $payment = Yii::$app->wechat->payment;
  285. $result = $payment->order->unify($orderData);
  286. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
  287. return $payment->jssdk->sdkConfig($result['prepay_id']);
  288. }
  289. // Yii::$app->services->actionLog->create('wechatMpPayError', json_encode($result));
  290. return $result;
  291. }
  292. /**
  293. * 支付宝支付
  294. * @param PaymentTrade $trade
  295. * @param string $tradeType 交易类型
  296. * @param array $params
  297. * @return array
  298. * @throws
  299. */
  300. public function alipay(PaymentTrade $trade, $tradeType, $params = [])
  301. {
  302. // 生成订单
  303. $order = [
  304. 'out_trade_no' => $trade->out_trade_no,
  305. 'total_amount' => $trade->getOnlinePayFee(),
  306. 'subject' => $trade->title,
  307. ];
  308. $pay_type = PayTypeEnum::action(PayTypeEnum::ALI);
  309. if ($tradeType == AliPayPayTypeEnum::JS) {
  310. $order['buyer_id'] = $params['openid'];
  311. $order['seller_id'] = $config['mch_id'] ?? '';
  312. // 这里需要取订单详情
  313. $order['goods_detail'] = $params['goods_detail'] ?? [];
  314. // 校验是否有分期
  315. if (!empty($params['period']) && MallAddons::isInstall($trade->mall_id, AddonsEnum::ADDONS_NAME_ALIPAY_MINI_HUABEI)) {
  316. $huabei_config = Yii::$app->services->setting->addons->get($trade->mall_id, AlipayMiniHuabeiEnum::ADDONS_NAME, 'basic');
  317. $huabei_config['is_enable'] = $huabei_config['is_enable'] ?? StatusEnum::DISABLED;
  318. $huabei_config['periods'] = !empty($huabei_config['periods']) ? Json::decode($huabei_config['periods']) : [];
  319. if ($huabei_config['is_enable'] == StatusEnum::ENABLED && in_array($params['period'], $huabei_config['periods'])) { // 如果是启用状态且当前期数可使用
  320. $order['extend_params'] = [
  321. 'hb_fq_num' => $params['period'],
  322. 'hb_fq_seller_percent' => $huabei_config['is_seller_fee'] == StatusEnum::ENABLED ? 100 : 0,
  323. ];
  324. }
  325. }
  326. $config = $this->getAlipayConfig($trade);
  327. } else {
  328. $config = $this->getConfig($trade->mall_id, $pay_type);
  329. }
  330. $notify_url = isset($params['notify_url']) ? $params['notify_url'] : Url::toApi(['notify/' . $pay_type], true);
  331. $return_url = isset($params['return_url']) ? $params['return_url'] : PlatformSetting::conf("system.h5_url",[],true);
  332. // 配置
  333. $config['notify_url'] = $notify_url; //支付通知回调地址
  334. if($tradeType!='app'){
  335. $config['return_url'] = $return_url; // 买家付款成功跳转地址
  336. }
  337. $config['sandbox'] = false;
  338. $pay = Yii::$app->payment->alipay($config);
  339. return [
  340. 'config' => $pay->$tradeType($order),
  341. ];
  342. }
  343. /**
  344. * 获取支付宝小程序配置信息
  345. * @param PaymentTrade $trade
  346. * @return array|mixed
  347. */
  348. public function getAlipayConfig(PaymentTrade $trade)
  349. {
  350. $config = Yii::$app->services->setting->mall->get($trade->mall_id, "alipay_mini");
  351. // 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
  352. $base_path = dirname(Yii::$app->basePath);
  353. if ($config['is_use_cert'] == StatusEnum::ENABLED) { // 如果启用证书模式
  354. $config['alipay_public_cert'] = dirname(Yii::$app->basePath).'/'.$config['alipayCertPath'];
  355. $config['alipay_root_cert'] = dirname(Yii::$app->basePath).'/'.$config['alipayRootCertPath'];
  356. $config['app_cert'] = dirname(Yii::$app->basePath).'/'.$config['merchantCertPath'];
  357. } else {
  358. //注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
  359. $config['alipay_public_key'] = file_get_contents("$base_path/{$config['alipayCertPath']}");
  360. }
  361. $config['alipay_app_id'] = $config['app_id'];
  362. $config['alipay_private_key'] = $config['merchantPrivateKey'];
  363. return $config;
  364. }
  365. /**
  366. * 汇聚聚合支付
  367. * @param PaymentTrade $trade
  368. * @param $tradeType
  369. * @param array $params
  370. * @return array|mixed
  371. * @throws Exception
  372. */
  373. public function joinpay(PaymentTrade $trade, $tradeType, $params = [])
  374. {
  375. $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY);
  376. $order = [
  377. 'title' => $trade->title,
  378. 'order_no' => $trade->out_trade_no,
  379. 'amount' => $trade->getOnlinePayFee(),
  380. 'mall_id' => $trade->mall_id,
  381. 'notify_url' => Url::toApi(['notify/' . $pay_type], TRUE),
  382. 'type' => $tradeType,
  383. 'open_id' => $params['openid'] ?? ''
  384. ];
  385. $config = $this->getConfig($trade->mall_id, $pay_type);
  386. $pay = Yii::$app->payment->joinpay($config);
  387. return $pay->pay($order);
  388. }
  389. /**
  390. * 汇聚快捷支付
  391. * @param PaymentTrade $trade
  392. * @param $tradeType
  393. * @param array $params
  394. * @return bool
  395. * @throws Exception
  396. */
  397. public function joinpay_fast(PaymentTrade $trade, $tradeType, $params = [])
  398. {
  399. $order = [
  400. 'order_no' => $trade->out_trade_no,
  401. 'sms_code' => $params['sms_code'],
  402. 'created_at' => $trade->created_at
  403. ];
  404. $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY_FAST_PAY);
  405. $config = $this->getConfig($trade->mall_id, $pay_type);
  406. $pay = Yii::$app->payment->joinpayFastPay($config);
  407. return $pay->pay($order);
  408. }
  409. /**
  410. * 杉德快捷支付
  411. * @param PaymentTrade $trade
  412. * @param $tradeType
  413. * @param array $params
  414. * @return bool
  415. * @throws Exception
  416. */
  417. public function sandpay_fast(PaymentTrade $trade, $tradeType, $params = [])
  418. {
  419. $order = [
  420. 'order_no' => $trade->out_trade_no,
  421. 'sms_code' => $params['sms_code'],
  422. 'created_at' => $trade->created_at
  423. ];
  424. $config = $this->getConfig($trade->mall_id, SandPayEnums::SAND_PAY_FAST);
  425. $pay = Yii::$app->payment->sandpayFastPay($config);
  426. return $pay->pay($order);
  427. }
  428. /**
  429. * 汇聚分账支付
  430. *
  431. * @param PaymentTrade $trade
  432. * @param string $tradeType 支付平台类型 [WEIXIN_GZH, WEIXIN_XCX, ...]
  433. * @param array $params
  434. * @return array
  435. * @throws Exception
  436. */
  437. public function huiju_bill(PaymentTrade $trade, $tradeType, $params = [])
  438. {
  439. $pay_type = PayTypeEnum::action(PayTypeEnum::HUIJU_BILL);
  440. $config = $this->getConfig($trade->mall_id, $pay_type);
  441. $pay = Yii::$app->payment->huijuBill($config);
  442. // 判断支付类型
  443. if (!in_array($tradeType, HuijuBillTypeEnum::getConstants())) {
  444. throw new Exception('未定义该支付类型');
  445. }
  446. $order = [
  447. 'trade_id' => $trade->id,
  448. 'title' => $trade->title,
  449. 'order_no' => $trade->out_trade_no,
  450. 'amount' => $trade->pay_fee,
  451. 'mall_id' => $trade->mall_id,
  452. 'notify_url' => Url::toApi(['notify/' . $pay_type], TRUE),
  453. 'type' => $tradeType,
  454. 'open_id' => $params['openid'] ?? ''
  455. ];
  456. return $pay->pay($order);
  457. }
  458. const BALANCE_DESC_TYPES = [
  459. 0 => '支付',
  460. 101 => '共享平台-保证金支付',
  461. 102 => '共享平台-尾款支付'
  462. ];
  463. /**
  464. * 余额支付
  465. * @param PaymentTrade $trade
  466. * @param $tradeType
  467. * @param array $params
  468. * @throws
  469. * @return
  470. */
  471. public function balance(PaymentTrade $trade, $tradeType, $params = [])
  472. {
  473. //校验支付密码
  474. $balance_pay_setting = Yii::$app->services->setting->mall->get($trade->mall_id, 'pay.balance');
  475. $balance_pay_setting = json_decode($balance_pay_setting,'true');
  476. if(!empty($balance_pay_setting['pay_password_status']) && $trade->pay_fee>0){
  477. $user = User::findOne(['id'=>$trade->user_id]);
  478. if(empty($user->transaction_password)){
  479. throw new Exception('您未设置支付密码');
  480. }
  481. if(empty($params['transaction_password'])){
  482. throw new Exception('请输入交易密码');
  483. }
  484. if (!\Yii::$app->getSecurity()->validatePassword(trim($params['transaction_password']), $user->transaction_password)) {
  485. throw new Exception('支付密码错误');
  486. }
  487. }
  488. $t = \Yii::$app->db->beginTransaction();
  489. try{
  490. //判断是否有卡券插件
  491. if (MallAddons::isInstall($trade->mall_id, AddonsEnum::ADDONS_NAME_CARD_COUPONS)) {
  492. $trade_id = $trade->id;
  493. $tradeOrder = PaymentTradeOrder::findOne(['trade_id'=>$trade_id]);
  494. //$findOrder = Order::findOne(['out_trade_no'=>$trade->out_trade_no]);
  495. $giveData = [
  496. 'mall_id' => $trade->mall_id,
  497. 'user_id' => $trade->user_id,
  498. 'pay_fee' => $trade->pay_fee,
  499. 'source_table' => 'transaction',
  500. 'source_table_id' => $trade->id,
  501. 'order_no' => $tradeOrder->order_no,
  502. ];
  503. //卡券抵扣
  504. $cardMoney = CardCouponsScoreGive::setOrderPay($giveData);
  505. $trade->pay_fee = $cardMoney;
  506. }
  507. //2023-04-18
  508. $desc_prefix = self::BALANCE_DESC_TYPES[$params['balance_desc_type']] ?? self::BALANCE_DESC_TYPES[0];
  509. $order_no = PaymentTradeOrder::find()->where(['trade_id' => $trade->id])->select('order_no')->scalar();
  510. $fee = bcmul($trade->getWalletPayFee(), 1, 2);
  511. $handle_wallet = [
  512. 'message_id' => 0,
  513. 'mall_id' => $trade->mall_id,
  514. 'user_id' => $trade->user_id,
  515. 'wallet_type' => UserWalletService::WALLET_TYPE_BALANCE,
  516. 'money' => -$trade->getWalletPayFee(),
  517. 'change_type' => CapitalLog::CHANGE_TYPE_SUB,
  518. 'desc' => "订单【{$order_no}】{$desc_prefix} {$fee} 元",
  519. 'source_table' => 'transaction',
  520. 'source_table_id' => $trade->id,
  521. 'from_type' => UserWalletEnum::CONSUME,
  522. 'capital_type' => UserWalletEnum::CONSUMPTION_ORDER,
  523. 'order_no' => $order_no,
  524. ];
  525. if ($params['model_type'] == 'Area') {
  526. $handle_wallet['from_type'] = UserWalletEnum::FROM_AREA;
  527. $handle_wallet['capital_type'] = UserWalletEnum::AREA_APPLY_CONSUME;
  528. $handle_wallet['desc'] = '购买区代申请资格,支付'.$trade->pay_fee.'元';
  529. }
  530. $result = UserWalletService::handle(0,$handle_wallet);
  531. if(!$result){
  532. $error_msg = UserWalletService::$static_error ?? '余额支付失败';
  533. throw new Exception($error_msg);
  534. }
  535. $t->commit();
  536. }catch(\Exception $e){
  537. $t->rollBack();
  538. throw new \Exception($e->getMessage());
  539. }
  540. // 组合支付不进行修改支付流水
  541. $trade->setWalletPayType(PayTypeEnum::BALANCE);
  542. !$trade->isMixedPay() && $trade->is_pay = 1;
  543. !$trade->isMixedPay() && $trade->pay_time = time();
  544. if (!$trade->save()) {
  545. throw new Exception('支付流水修改失败');
  546. }
  547. $orders = $this->changePaymentTradeOrder($trade, $trade->pay_type, $tradeType);
  548. $orders && !$trade->isMixedPay() && Yii::$app->services->pay->notify($orders);
  549. // 剩余支付金额
  550. return true;
  551. }
  552. /**
  553. * 积分支付
  554. * @param PaymentTrade $trade
  555. * @param $tradeType
  556. * @param array $params
  557. * @throws
  558. * @return
  559. */
  560. public function score(PaymentTrade $trade, $tradeType, $params = [])
  561. {
  562. //校验支付密码
  563. $score_pay_setting = Yii::$app->services->setting->mall->get($trade->mall_id, 'pay.score');
  564. $score_setting = Yii::$app->services->setting->mall->get($trade->mall_id, 'score');
  565. $score_name = $score_setting['score_name'] ?: '积分';
  566. $score_balance_ratio = $score_setting['score_balance_ratio'] ?: 1;// 积分兑换人民币比例
  567. $score_pay_setting = json_decode($score_pay_setting,'true');
  568. if(!empty($params['source']) && $params['source'] == 6){
  569. $store_payment = \Yii::$app->services->setting->addons->get($trade->mall_id,AddonsEnum::ADDONS_NAME_STORE, 'payment');
  570. if(!empty($store_payment['score_pay_password_status'])){
  571. $score_pay_setting['pay_password_status'] = 1;
  572. }
  573. }
  574. // 门店积分支付设置
  575. $store_score_payment = Yii::$app->services->setting->addons->get($trade->mall_id, 'Store', 'payment');
  576. // 是否安装门店插件
  577. $is_store = Addons::isInstall(AddonsEnum::ADDONS_NAME_STORE);
  578. $is_store_score = $is_store && !empty($store_score_payment['score_pay_password_status']) && !empty($params['is_store_pay']);
  579. if((!empty($score_pay_setting['pay_password_status']) || $is_store_score) && $trade->pay_fee>0){
  580. $user = User::findOne(['id'=>$trade->user_id]);
  581. if(empty($user->transaction_password)){
  582. throw new Exception('您未设置支付密码');
  583. }
  584. if(empty($params['transaction_password'])){
  585. throw new Exception('请输入交易密码');
  586. }
  587. if (!\Yii::$app->getSecurity()->validatePassword(trim($params['transaction_password']), $user->transaction_password)) {
  588. throw new Exception('支付密码错误');
  589. }
  590. }
  591. // 检测积分跟人民币比例是否为1:1
  592. if ($score_balance_ratio != 1) throw new Exception($score_name.'与人民币比例必须为1:1');
  593. // 检测积分余额是否足够
  594. $wallet = UserWallet::find()->where(['user_id' => $trade->user_id, 'status' => StatusEnum::ENABLED])->asArray()->one();
  595. if ($wallet['score'] < $trade->getWalletPayFee()) throw new Exception($score_name . '不足');
  596. // 获取订单编号
  597. $order_no = PaymentTradeOrder::find()->where(['trade_id' => $trade->id])->select('order_no')->scalar();
  598. $fee = bcmul($trade->getWalletPayFee(), 1, 2);
  599. $handle_wallet = [
  600. 'message_id' => 0,
  601. 'mall_id' => $trade->mall_id,
  602. 'user_id' => $trade->user_id,
  603. 'wallet_type' => UserWalletService::WALLET_TYPE_SCORE,
  604. 'money' => -$trade->getWalletPayFee(),
  605. 'desc' => "订单号【{$order_no}】支付 {$fee} {$score_name}",
  606. 'source_table' => 'transaction',
  607. 'source_table_id' => $trade->id,
  608. 'from_type' => UserWalletEnum::CONSUME,
  609. 'order_no' => $order_no,
  610. 'capital_type' => UserWalletEnum::CONSUMPTION_ORDER,
  611. ];
  612. $result = UserWalletService::handle(0,$handle_wallet);
  613. if(!$result){
  614. $error_msg = UserWalletService::$static_error ?? $score_name.'支付失败';
  615. throw new Exception($error_msg);
  616. }
  617. // 组合支付不进行修改支付流水
  618. $trade->setWalletPayType(PayTypeEnum::SCORE);
  619. !$trade->isMixedPay() && $trade->is_pay = 1;
  620. !$trade->isMixedPay() && $trade->pay_time = time();
  621. if (!$trade->save()) {
  622. throw new Exception('支付流水修改失败');
  623. }
  624. $orders = $this->changePaymentTradeOrder($trade, $trade->pay_type, $tradeType);
  625. $orders && !$trade->isMixedPay() && Yii::$app->services->pay->notify($orders);
  626. return true;
  627. }
  628. /**
  629. * 消费卡支付
  630. * @param PaymentTrade $trade
  631. * @param $tradeType
  632. * @param array $params
  633. * @throws
  634. * @return
  635. */
  636. public function consumer_card(PaymentTrade $trade, $tradeType, $params = [])
  637. {
  638. $t = \Yii::$app->db->beginTransaction();
  639. try{
  640. //2023-04-18
  641. $order_no = PaymentTradeOrder::find()->where(['trade_id' => $trade->id])->select('order_no')->scalar();
  642. $fee = bcmul($trade->getWalletPayFee(), 1, 2);
  643. //扣除消费卡余额
  644. $insert_wallet = [
  645. 'mall_id' => $trade->mall_id,
  646. 'user_id' => $trade->user_id,
  647. 'wallet_type' => \addons\ScoreGame\common\enums\ScoreGameEnum::WALLET_TYPE_CONSUMER_CARD,
  648. 'change_type' => CapitalLog::CHANGE_TYPE_SUB,
  649. 'change_num' => -$trade->getWalletPayFee(),
  650. 'source_table' => 'transaction',
  651. 'source_table_id' => $trade->id,
  652. 'from_type' => \addons\ScoreGame\common\enums\ScoreGameEnum::SCORE_CAPITAL_TYPE_CONSUME,
  653. 'capital_type' => \addons\ScoreGame\common\enums\ScoreGameEnum::SCORE_CAPITAL_TYPE_CONSUME,
  654. 'order_no' => $order_no,
  655. 'desc' => "订单号【{$order_no}】支付 {$fee}",
  656. 'created_at' => time(),
  657. 'updated_at' => time(),
  658. ];
  659. $res = \addons\ScoreGame\common\logic\WalletLogic::handle($insert_wallet);
  660. if(!$res){
  661. $error_msg = '扣除消费卡余额失败';
  662. throw new Exception($error_msg);
  663. }
  664. $t->commit();
  665. }catch(\Exception $e){
  666. $t->rollBack();
  667. throw new \Exception($e->getMessage());
  668. }
  669. // 组合支付不进行修改支付流水
  670. $trade->setWalletPayType(PayTypeEnum::BALANCE);
  671. !$trade->isMixedPay() && $trade->is_pay = 1;
  672. !$trade->isMixedPay() && $trade->pay_time = time();
  673. if (!$trade->save()) {
  674. throw new Exception('支付流水修改失败');
  675. }
  676. $orders = $this->changePaymentTradeOrder($trade, $trade->pay_type, $tradeType);
  677. $orders && !$trade->isMixedPay() && Yii::$app->services->pay->notify($orders);
  678. // 剩余支付金额
  679. return true;
  680. }
  681. /**
  682. * @desc:退款
  683. * @Author: hua
  684. * @Date: 2021/12/20
  685. * @Time: 9:03
  686. * @Copyright: copyright (c) 2021 广东七件事集团
  687. * @param $totalFee//支付金额
  688. * @param $refundFee//退款金额
  689. * @param $out_trade_no//系统订单支付编号
  690. * @param null $order//订单对象
  691. * @param string $desc//描述
  692. * @param string $from_type//退款来源
  693. * @param string $capital_type//退款类型
  694. * @return bool
  695. * @throws Exception
  696. */
  697. public function refund($totalFee, $refundFee,$out_trade_no,$order, $desc='',$from_type='',$capital_type='', $pay_type = null)
  698. {
  699. try {
  700. $payment_trade = PaymentTrade::findOne(['out_trade_no' => $out_trade_no]);
  701. $totalFee = $payment_trade->getOnlinePayFee();
  702. $pay_type = isset($pay_type) ? $pay_type : $payment_trade->pay_type;
  703. $payment_refund = new PaymentRefund();
  704. $payment_refund->user_id =$order->user_id;
  705. $payment_refund->mall_id =$order->mall_id;
  706. $payment_refund->order_no = StringHelper::generateOrderNo('tr');
  707. $payment_refund->out_trade_no = $out_trade_no;
  708. $payment_refund->amount = $refundFee;
  709. $payment_refund->pay_type = $pay_type;
  710. $payment_refund->trade_order_no = $order->order_no;
  711. $payment_refund->save();
  712. switch ($pay_type) {
  713. case PayTypeEnum::BALANCE:
  714. $res = Yii::$app->payment->balance->refund($payment_refund,$order::tableName(),$order['id'],$desc,$from_type,$capital_type);
  715. if($res==false) throw new \Exception('余额退款失败');
  716. break;
  717. case PayTypeEnum::SCORE:
  718. $res = Yii::$app->payment->score->refund($payment_refund,$order['order_no'],$order::tableName(),$order['id'],$desc,$from_type,$capital_type);
  719. if($res==false) throw new \Exception('积分支付退款失败');
  720. break;
  721. case PayTypeEnum::WECHAT:
  722. //$res = Yii::$app->payment->wechat->refunds($payment_refund,$totalFee);
  723. $wechat_refund = [
  724. 'out_trade_no' => $out_trade_no,
  725. 'transaction_id' => '',
  726. 'out_refund_no'=>$payment_refund->order_no,
  727. 'total_fee' => bcmul($totalFee ,100,0),
  728. 'refund_fee' => bcmul($refundFee,100,0),
  729. ];
  730. //$config = Yii::$app->services->pay->getConfig($order->mall_id,'wechat');
  731. $config = $this->getWechatConfig($order->mall_id);
  732. //微信支付配置获取
  733. $res = Yii::$app->payment->wechat($config)->refund($wechat_refund);
  734. if ($res['return_code'] != 'SUCCESS' || $res['result_code'] != 'SUCCESS') {
  735. $exception = $res['err_code_des'] ?? $res['return_msg'];
  736. throw new Exception($exception);
  737. }
  738. $this->refundNotify($payment_refund);
  739. break;
  740. case PayTypeEnum::JOINPAY:
  741. $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY);
  742. $config = $this->getConfig($payment_refund->mall_id, $pay_type);
  743. $refund_class = Yii::$app->payment->joinpay($config);
  744. $res = $refund_class->refund(['out_refund_no' => $out_trade_no,'refund_order_no'=>$payment_refund->order_no,'refund_amount' => $refundFee,'refund_reason' => $desc]);
  745. if($res==false) throw new \Exception($refund_class->getError());
  746. break;
  747. case PayTypeEnum::JOINPAY_FAST_PAY:
  748. $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY_FAST_PAY);
  749. $config = $this->getConfig($payment_refund->mall_id, $pay_type);
  750. $refund_class = Yii::$app->payment->joinpayFastPay($config);
  751. $res = $refund_class->refund(['out_trade_no' => $out_trade_no,'refund_order_no'=>$payment_refund->order_no,'refund_amount' => $refundFee,'refund_reason' => $desc,'trade_created_at' => $payment_trade->created_at]);
  752. if($res==false) throw new \Exception($refund_class->getError());
  753. break;
  754. case PayTypeEnum::SANDPAY_FAST_PAY:
  755. $pay_type = \addons\SandPay\common\enums\SandPayEnums::action(\addons\SandPay\common\enums\SandPayEnums::SANDPAY_FAST_PAY);
  756. $config = $this->getConfig($payment_refund->mall_id, $pay_type);
  757. $refund_class = Yii::$app->payment->sandpayFastPay($config);
  758. $res = $refund_class->refund(['out_trade_no' => $out_trade_no,'refund_order_no'=>
  759. $payment_refund->order_no,'refund_amount' => $refundFee,'refund_reason' => $desc,'trade_created_at' => $payment_trade->created_at]);
  760. if(!$res) throw new \Exception($refund_class->getError());
  761. break;
  762. case PayTypeEnum::ALI:
  763. switch ($payment_trade->trade_type) {
  764. case AliPay::JS:
  765. $config = $this->getAlipayConfig($payment_trade);
  766. break;
  767. default:
  768. $config = \Yii::$app->services->pay->getConfig($payment_refund->mall_id,PayTypeEnum::action(PayTypeEnum::ALI));
  769. }
  770. $refund_class = Yii::$app->payment->alipay($config);
  771. $res = $refund_class->refund(['out_trade_no' => $out_trade_no,'out_request_no'=>$payment_refund->order_no,'refund_amount' => $refundFee,'refund_reason' => $desc,'trade_no' => $payment_trade->third_trade_no]);
  772. if($res['is_success'] && !empty($res['fund_change'])){
  773. $this->refundNotify($payment_refund);
  774. }
  775. if($res == false) throw new \Exception('支付宝退款失败');
  776. break;
  777. default:
  778. throw new \Exception('暂时不支持该支付方式退款');
  779. }
  780. return true;
  781. } catch (Exception $e) {
  782. throw new \Exception($e->getMessage());
  783. }
  784. }
  785. public function queryByOutTradeNo($mall_id,$out_trade_no){
  786. $config = $this->getWechatConfig($mall_id);
  787. $res = Yii::$app->payment->wechat($config)->queryByOutTradeNo($out_trade_no);
  788. var_dump($res);
  789. }
  790. public function queryRefund($mall_id,$out_trade_no){
  791. $config = $this->getWechatConfig($mall_id);
  792. $res = Yii::$app->payment->wechat($config)->queryRefund($out_trade_no);
  793. var_dump($res);
  794. }
  795. /**
  796. * 支付回调
  797. * @param PaymentTrade $trade
  798. * @param int $pay_type 支付方式
  799. * @param string $trade_type 交易类型
  800. * @return bool
  801. * @throws Exception
  802. */
  803. public function notifyOld(PaymentTrade $trade,$pay_type,$trade_type = '')
  804. {
  805. $orders = PaymentTradeOrder::findAll(['trade_id' => $trade->id]);
  806. foreach ($orders as $order_info) {
  807. $order_info->is_pay = 1;
  808. $order_info->pay_type = $pay_type;
  809. if (!$order_info->save()) {
  810. throw new Exception($order_info->getErrorMessage());
  811. }
  812. $notify_class = $order_info->notify_class;
  813. if (!class_exists($notify_class)) {
  814. continue;
  815. }
  816. $notify = new $notify_class();
  817. //todo
  818. $notify->notify($order_info->toArray());
  819. }
  820. //修改支付流水调用记录
  821. /** @var PaymentTradeLog $log */
  822. $log = PaymentTradeLog::getLog($trade->id,$pay_type,$trade_type);
  823. if($log){
  824. $log->notify_result = 1;
  825. $log->save();
  826. }
  827. return true;
  828. }
  829. public function notify($orders)
  830. {
  831. foreach ($orders as $order_info) {
  832. $notify_class = $order_info->notify_class;
  833. if (!class_exists($notify_class)) {
  834. continue;
  835. }
  836. $notify = new $notify_class();
  837. //todo
  838. $notify->notify($order_info->toArray());
  839. }
  840. return true;
  841. }
  842. /**
  843. * 修改并获取支付订单信息
  844. *
  845. * @param PaymentTrade $trade
  846. * @param integer $pay_type
  847. * @param string $trade_type
  848. * @return array
  849. */
  850. public function changePaymentTradeOrder(PaymentTrade $trade, int $pay_type, $trade_type = '')
  851. {
  852. $orders = PaymentTradeOrder::findAll(['trade_id' => $trade->id]);
  853. foreach ($orders as $order_info) {
  854. $trade->is_pay && $order_info->is_pay = 1;
  855. $order_info->pay_type = $trade->isMixedPay() ? $trade->pay_type : $pay_type;
  856. if (!$order_info->save()) {
  857. throw new Exception($order_info->getErrorMessage());
  858. }
  859. }
  860. // 判断是否为钱包支付
  861. $trade->isWalletPay() && $trade->setWalletPaySuccess();
  862. // 判断是否为线上支付
  863. $trade->isOnlinePay() && $trade->setOnlinePaySuccess();
  864. //修改支付流水调用记录
  865. /**
  866. * @var PaymentTradeLog $log
  867. */
  868. $log = PaymentTradeLog::getLog($trade->id, $trade->pay_type, $trade_type);
  869. if ($log) {
  870. $log->notify_result = 1;
  871. $log->save();
  872. }
  873. return $orders;
  874. }
  875. /**
  876. * 创建订单流水
  877. * @param int $mall_id
  878. * @param int $user_id
  879. * @param array $orders
  880. * @throws
  881. * @return int
  882. */
  883. public function createTrade($mall_id, $user_id, $orders)
  884. {
  885. $trans = Yii::$app->db->beginTransaction();
  886. try {
  887. if (empty($orders) || !is_array($orders)) {
  888. throw new Exception('订单信息有误');
  889. }
  890. $order_amount = 0;
  891. $title = '';
  892. $order_nos = '';
  893. foreach ($orders as $order_info) {
  894. $order_amount = bcadd($order_amount, $order_info['amount'], 2);
  895. $title .= str_replace(';', '', $order_info['title']) . ';';
  896. $is_rand_fee = isset($order_info['order_type']) ? true : false;
  897. $order_nos.= $order_info['order_no'] . ';';
  898. }
  899. //判断支付金额 不能小于0
  900. if($order_amount < 0){
  901. throw new Exception('支付金额有误');
  902. }
  903. // 全局调用
  904. $models = [];
  905. if ($mall_id && $is_rand_fee) {
  906. $res = GlobalInvoke::exec(GlobalInvoke::RAND_FEE, $mall_id, compact('orders', 'mall_id', 'user_id'));
  907. if (!empty($res)) list($rand_fee, $models, $rand_rule) = $res;
  908. }
  909. $rand_fee = !empty($rand_fee) ? $rand_fee : 0;
  910. $pay_fee = bcsub($order_amount, $rand_fee, 2);
  911. if($pay_fee < 0) $pay_fee = 0;
  912. $trade = [
  913. 'mall_id' => $mall_id,
  914. 'user_id' => $user_id,
  915. 'out_trade_no' => PaymentTrade::createOutTradeNo(),
  916. 'pay_fee' => $pay_fee,
  917. 'total_fee' => $order_amount,
  918. 'rand_fee' => $rand_fee,
  919. 'title' => mb_convert_encoding(mb_substr($title, 0, 32),'UTF-8'),
  920. //'notify_url' => '',
  921. 'return_url' => $orders[0]['return_url'] ?? '',
  922. ];
  923. //正则过滤除了汉字字母数字的其它字符
  924. $trade['title'] = preg_replace('#[^\x{4e00}-\x{9fa5}A-Za-z0-9]#u','',$trade['title']);
  925. $paymentTrade = new PaymentTrade();
  926. $paymentTrade->attributes = $trade;
  927. if (!$paymentTrade->save()) {
  928. throw new Exception($paymentTrade->getErrorMessage());
  929. }
  930. $trade_id = $paymentTrade->id;
  931. \Yii::$app->custom->logs(('trade_id:'.$trade_id.':order_nos:'.$order_nos));
  932. $rand_fee_arr = [];
  933. $total_amount = array_sum(array_column($orders,'amount'));
  934. $temp_rand_fee = 0;
  935. $order_type = '';
  936. foreach ($orders as $k=>$order_info) {
  937. $order_rand_fee = 0;
  938. if($rand_fee){
  939. if(!isset($orders[$k+1])){
  940. $order_rand_fee = bcsub($rand_fee,$temp_rand_fee,2);
  941. }else{
  942. $rate = bcdiv($order_info['amount'],$total_amount,2);
  943. $order_rand_fee = bcmul($rate,$rand_fee,2);
  944. $temp_rand_fee = bcadd($order_rand_fee,$temp_rand_fee,2);
  945. }
  946. $rand_fee_arr[$order_info['order_no']] = $order_rand_fee;
  947. $order_type = $order_info['order_type'];
  948. }
  949. $model = new PaymentTradeOrder();
  950. $model->trade_id = $trade_id;
  951. $model->order_no = $order_info['order_no'];
  952. $model->order_type = $order_info['order_type']; //订单类型
  953. $model->pay_fee = $paymentTrade->pay_fee;
  954. $model->total_fee = $order_info['amount'];
  955. $model->is_pay = 0;
  956. $model->title = $order_info['title'];
  957. $model->notify_class = $order_info['notify_class'] ?? '';
  958. $model->rand_fee = $order_rand_fee;
  959. if (!$model->save()) {
  960. throw new Exception($model->getErrorMessage());
  961. }
  962. \Yii::$app->custom->logs('trade_id:'.$trade_id.':out_trade_no:'.$paymentTrade->out_trade_no.':order_no:'.$order_info['order_no']);
  963. }
  964. if(!empty($rand_fee_arr)){
  965. if($order_type=='order'){
  966. Order::updateOrderPayMoney($rand_fee_arr);
  967. }
  968. }
  969. if (!empty($models)) {
  970. foreach ($models as $model) {
  971. $model->trade_id = $trade_id;
  972. $model->save();
  973. }
  974. }
  975. $trans->commit();
  976. return array_merge([
  977. 'trade_id' => $trade_id,
  978. 'trade_no' => $paymentTrade->out_trade_no,
  979. ], isset($rand_rule) ? compact('rand_rule') : [] ); // 如果定义随机减规则 合并
  980. } catch (Exception $e) {
  981. $trans->rollBack();
  982. Yii::error($e->getMessage());
  983. return false;
  984. }
  985. }
  986. public function getByTradeId($id)
  987. {
  988. return PaymentTrade::findOne(['id' => $id]);
  989. }
  990. public function getByTradeNo($out_trade_no)
  991. {
  992. return PaymentTrade::findOne(['out_trade_no' => $out_trade_no]);
  993. }
  994. /**
  995. * 获取商城支付配置
  996. * @param $mall_id
  997. * @param $pay_type
  998. * @return array|mixed
  999. * @throws
  1000. */
  1001. public function getConfig($mall_id , $pay_type)
  1002. {
  1003. if ($mall_id) {
  1004. $config = Yii::$app->services->setting->mall->get($mall_id, 'pay.' . $pay_type, true);
  1005. } else {
  1006. //获取系统配置
  1007. $config = Yii::$app->services->setting->platform->get($pay_type,true);
  1008. }
  1009. if (empty($config)) {
  1010. throw new Exception('查询不到对应的支付配置');
  1011. }
  1012. return $config && is_string($config) ? json_decode($config, true) : $config;
  1013. }
  1014. /**
  1015. * @param $mall_id
  1016. * @return array|false|null|string
  1017. * @throws Exception
  1018. */
  1019. public function getWechatConfig($mall_id = 0)
  1020. {
  1021. $payment_conf = [];
  1022. if ($mall_id) {
  1023. $payment_conf = Yii::$app->services->setting->mall->get($mall_id, 'pay.wechat', true);
  1024. if (empty($payment_conf)) {
  1025. throw new Exception('查询不到微信支付配置');
  1026. }
  1027. $payment_conf = json_decode($payment_conf,true);
  1028. //获取公众号配置
  1029. $wechat_conf = Yii::$app->services->setting->mall->get($mall_id, 'wechat', true);
  1030. if($wechat_conf){
  1031. $payment_conf = array_merge($payment_conf,$wechat_conf);
  1032. }
  1033. //获取小程序配置
  1034. $mini_program = Yii::$app->services->setting->mall->get($mall_id, 'mini_program', true);
  1035. if($mini_program){
  1036. $payment_conf['mini_program_app_id'] = $mini_program['app_id'];
  1037. $payment_conf['mini_program_secret'] = $mini_program['secret'];
  1038. }
  1039. }else{
  1040. //获取平台配置
  1041. $payment_conf = Yii::$app->services->setting->get('wechat', true);
  1042. if (empty($payment_conf)) {
  1043. throw new Exception('查询不到微信支付配置');
  1044. }
  1045. if (!is_array($payment_conf)) $payment_conf = json_decode($payment_conf,true);
  1046. //获取公众号配置
  1047. // $wechat_conf = Yii::$app->services->setting->get('wechat_public', true);
  1048. // if($wechat_conf){
  1049. // $payment_conf = array_merge($payment_conf,$wechat_conf);
  1050. // }
  1051. //获取小程序配置
  1052. $mini_program = Yii::$app->services->setting->get('mini_program', true);
  1053. if($mini_program){
  1054. $payment_conf['mini_program_app_id'] = $mini_program['app_id'];
  1055. $payment_conf['mini_program_secret'] = $mini_program['secret'];
  1056. }
  1057. }
  1058. return $payment_conf;
  1059. }
  1060. /**
  1061. * todo 需要调用公共调用
  1062. * 校验交易类型
  1063. * @param string $pay_type 支付方式
  1064. * @param string $trade_type 交易类型
  1065. * @throws
  1066. * @return bool
  1067. */
  1068. public function verifyTradeType($pay_type,$trade_type)
  1069. {
  1070. switch ($pay_type) {
  1071. case PayTypeEnum::WECHAT :
  1072. if (!in_array($trade_type, WechatPayTypeEnum::getKeys())) {
  1073. throw new Exception('微信交易类型不符');
  1074. }
  1075. break;
  1076. case PayTypeEnum::ALI :
  1077. if (!in_array($trade_type, ['pc', 'app', 'f2f', 'wap','js'])) {
  1078. throw new Exception('支付宝交易类型不符');
  1079. }
  1080. break;
  1081. case PayTypeEnum::UNION :
  1082. if (!in_array($trade_type, ['app', 'html'])) {
  1083. throw new Exception('银联交易类型不符');
  1084. }
  1085. break;
  1086. // 汇聚支付
  1087. // case PayTypeEnum::JOINPAY :
  1088. // if (!in_array($trade_type, JoinPayTypeEnum::getKeys())) {
  1089. // throw new Exception('汇聚交易类型不符');
  1090. // }
  1091. break;
  1092. }
  1093. return true;
  1094. }
  1095. /**
  1096. * @throws Exception
  1097. */
  1098. public function transfer($paymentTransfer): bool
  1099. {
  1100. $where = [
  1101. 'order_no' => $paymentTransfer['orderNo'],
  1102. 'user_id' => $paymentTransfer['user_id'],
  1103. 'mall_id' => $paymentTransfer['mall_id']
  1104. ];
  1105. $model = PaymentTransfer::getOne([$where]);
  1106. if (!$model) {
  1107. $model = new PaymentTransfer();
  1108. $model->transfer_order_no = $paymentTransfer->orderNo;
  1109. $model->mall_id = $paymentTransfer->cashInfo->mall_id;
  1110. $model->user_id = $paymentTransfer->cashInfo->user_id;
  1111. $model->title = $paymentTransfer->title;
  1112. $model->amount = $paymentTransfer->amount;
  1113. $model->pay_type = $paymentTransfer->transferType;
  1114. $model->is_pay = 0;
  1115. $model->withdrawal_source = $paymentTransfer->withdrawal_source;
  1116. $model->withdrawal_source_id = $paymentTransfer->withdrawal_source_id;
  1117. $model->order_no = 'HM' . substr(md5($paymentTransfer->orderNo), 2,29);
  1118. $model->created_at = time();
  1119. $model->cashInfo = $paymentTransfer->cashInfo;
  1120. }
  1121. if ($model->is_pay == 1) {
  1122. throw new Exception('该订单号已经打款,请勿重复操作');
  1123. }
  1124. $t = \Yii::$app->db->beginTransaction();
  1125. try {
  1126. if($model->pay_type == PayTypeEnum::WECHAT){
  1127. $config = Yii::$app->services->pay->getConfig($model->mall_id,'wechat');
  1128. //判断微信开通的功能:企业打款到零钱(老版本),商家转账到零钱(v3 版本)
  1129. if(isset($config['wechat_transfer']) && $config['wechat_transfer'] == WechatPayTypeEnum::WECHAT_TRANSFER_V3){
  1130. // $result = Yii::$app->payment->wechat($config)->v3_transfer_batches($model, $paymentTransfer->user);
  1131. //$result = Yii::$app->payment->wechat($config)->v3_transfer_bills($model, $paymentTransfer->user);
  1132. // 如果没有选择版本,或者版本选择1,那就用旧版本
  1133. if(!isset($config['wechat_transfer_version']) || $config['wechat_transfer_version'] == 1){
  1134. $result = Yii::$app->payment->wechat($config)->v3_transfer_batches($model, $paymentTransfer->user);
  1135. }else{ // 新版本
  1136. $result = Yii::$app->payment->wechat($config)->v3_transfer_bills($model, $paymentTransfer->user);
  1137. }
  1138. \Yii::error('---+++' . json_encode($result));
  1139. }else{
  1140. $result = Yii::$app->payment->wechat($config)->transfer($model, $paymentTransfer->user);
  1141. }
  1142. }else{
  1143. $class = $this->transferClass($model->pay_type);
  1144. $result = $class->transfer($model, $paymentTransfer->user);
  1145. }
  1146. if ($result) {
  1147. $t->commit();
  1148. return true;
  1149. } else {
  1150. throw new Exception();
  1151. }
  1152. } catch (Exception $e) {
  1153. $t->rollBack();
  1154. Yii::error($e->getMessage());
  1155. throw new Exception($e->getMessage());
  1156. return false;
  1157. }
  1158. }
  1159. /**
  1160. * @throws Exception
  1161. */
  1162. private function transferClass($type)
  1163. {
  1164. switch ($type) {
  1165. case PayTypeEnum::WECHAT:
  1166. $class = 'common\\components\\payment\\WechatPay';
  1167. break;
  1168. case PayTypeEnum::ALI:
  1169. $class = 'common\\components\\payment\\AliPay';
  1170. break;
  1171. case PayTypeEnum::JOINPAY;
  1172. //汇聚代付:
  1173. $class = 'common\\components\\payment\\AlternativePay';
  1174. break;
  1175. default:
  1176. throw new Exception('无效的方式');
  1177. }
  1178. if (!class_exists($class)) {
  1179. throw new Exception('未安装相关平台的插件或未知的客户端平台,平台标识`');
  1180. }
  1181. return new $class();
  1182. }
  1183. /**
  1184. * 退款回调
  1185. * @param PaymentRefund $payment_refund
  1186. * @return bool
  1187. */
  1188. public function refundNotify($payment_refund)
  1189. {
  1190. if (!$payment_refund || $payment_refund->is_pay){
  1191. return true;
  1192. }
  1193. //修改状态
  1194. $payment_refund->is_pay = 1;
  1195. if($payment_refund->save()){
  1196. $trade_order = PaymentTradeOrder::findOne(['order_no' => $payment_refund->trade_order_no,'is_pay'=>1]);
  1197. if($trade_order){
  1198. $trade_order->refund_fee = bcadd($trade_order->refund_fee,$payment_refund->amount,2);
  1199. $trade_order->save();
  1200. }
  1201. }
  1202. return true;
  1203. }
  1204. /**
  1205. * 获取系统已开启支付方式
  1206. * @param $mall_id
  1207. * @return array
  1208. */
  1209. public function getEnablePaymentType($mall_id=0)
  1210. {
  1211. if ($mall_id) {
  1212. $config = Yii::$app->services->setting->mall->get($mall_id, 'pay', true);
  1213. } else {
  1214. //系统配置
  1215. $config = Yii::$app->services->setting->platform->get('pay', true);
  1216. }
  1217. $enable_pay_type = [];
  1218. foreach ($config as $key => $value) {
  1219. $payment_mode = json_decode($value, true);
  1220. if (!$payment_mode['status']) {
  1221. continue;
  1222. }
  1223. $enable_pay_type[] = $key;
  1224. }
  1225. $all_payment = PayTypeEnum::allTypeConf($mall_id);
  1226. $enable_payment = [];
  1227. foreach($enable_pay_type as $pay_type)
  1228. {
  1229. $payment_info = $all_payment[$pay_type] ?? [];
  1230. if(empty($payment_info)) continue;
  1231. $enable_payment[] = [
  1232. "name" => $payment_info['name'],
  1233. "pay_type" => $payment_info['pay_type'],
  1234. ];
  1235. }
  1236. $params['mall_id']=$mall_id;
  1237. $return = GlobalInvoke::exec(GlobalInvoke::INVOKE_GET_PAYMENT_TYPE,$mall_id,$params);
  1238. if(!empty($return)) {
  1239. if (!empty($return)) {
  1240. $enable_payment = array_merge($enable_payment,$return);
  1241. }
  1242. }
  1243. return $enable_payment;
  1244. }
  1245. /**
  1246. * 创建订单流水
  1247. * @param int $mall_id
  1248. * @param int $user_id
  1249. * @param array $orders
  1250. * @throws
  1251. * @return int
  1252. */
  1253. public function createGroupTrade($mall_id, $user_id, $orders, $parent_trade_id,$pay_type = 0)
  1254. {
  1255. $trans = Yii::$app->db->beginTransaction();
  1256. try {
  1257. if (empty($orders) || !is_array($orders)) {
  1258. throw new Exception('订单信息有误');
  1259. }
  1260. $exist = PaymentTrade::findOne($parent_trade_id);
  1261. if(empty($exist)){
  1262. throw new Exception('支付数据有误');
  1263. }
  1264. if($exist->is_pay == 1){
  1265. throw new Exception('订单已支付');
  1266. }
  1267. $exist->is_pay_group = 1;
  1268. $exist->pay_type = $pay_type;
  1269. if(!$exist->save()){
  1270. throw new Exception($exist->getErrorMessage());
  1271. }
  1272. $order_amount = 0;
  1273. $title = '';
  1274. foreach ($orders as $order_info) {
  1275. $order_amount = bcadd($order_amount, $order_info['amount'], 2);
  1276. $title .= str_replace(';', '', $order_info['title']) . ';';
  1277. $is_rand_fee = isset($order_info['order_type']) ? true : false;
  1278. }
  1279. // 全局调用
  1280. $models = [];
  1281. if ($mall_id && $is_rand_fee) {
  1282. $res = GlobalInvoke::exec(GlobalInvoke::RAND_FEE, $mall_id, compact('orders', 'mall_id', 'user_id'));
  1283. if (!empty($res)) list($rand_fee, $models, $rand_rule) = $res;
  1284. }
  1285. $rand_fee = !empty($rand_fee) ? $rand_fee : 0;
  1286. $trade = [
  1287. 'mall_id' => $mall_id,
  1288. 'user_id' => $user_id,
  1289. 'out_trade_no' => PaymentTrade::createOutTradeNo(),
  1290. 'pay_fee' => bcsub($order_amount, $rand_fee, 2),
  1291. 'total_fee' => $order_amount,
  1292. 'rand_fee' => $rand_fee,
  1293. 'title' => mb_convert_encoding(mb_substr($title, 0, 32),'UTF-8'),
  1294. //'notify_url' => '',
  1295. 'return_url' => $orders[0]['return_url'] ?? '',
  1296. 'is_pay_group' => 1,
  1297. 'parent_trade_id' => $parent_trade_id,
  1298. ];
  1299. //正则过滤除了汉字字母数字的其它字符
  1300. $trade['title'] = preg_replace('#[^\x{4e00}-\x{9fa5}A-Za-z0-9]#u','',$trade['title']);
  1301. $paymentTrade = new PaymentTrade();
  1302. $paymentTrade->attributes = $trade;
  1303. if (!$paymentTrade->save()) {
  1304. throw new Exception($paymentTrade->getErrorMessage());
  1305. }
  1306. $trade_id = $paymentTrade->id;
  1307. $rand_fee_arr = [];
  1308. $total_amount = array_sum(array_column($orders,'amount'));
  1309. $temp_rand_fee = 0;
  1310. $order_type = '';
  1311. foreach ($orders as $k=>$order_info) {
  1312. $order_rand_fee = 0;
  1313. if($rand_fee){
  1314. if(!isset($orders[$k+1])){
  1315. $order_rand_fee = bcsub($rand_fee,$temp_rand_fee,2);
  1316. }else{
  1317. $rate = bcdiv($order_info['amount'],$total_amount,2);
  1318. $order_rand_fee = bcmul($rate,$rand_fee,2);
  1319. $temp_rand_fee = bcadd($order_rand_fee,$temp_rand_fee,2);
  1320. }
  1321. $rand_fee_arr[$order_info['order_no']] = $order_rand_fee;
  1322. $order_type = $order_info['order_type'];
  1323. }
  1324. $model = new PaymentTradeGroupOrder();
  1325. $model->trade_id = $trade_id;
  1326. $model->order_no = $order_info['order_no'];
  1327. $model->order_type = $order_info['order_type']; //订单类型
  1328. $model->pay_fee = $paymentTrade->pay_fee;
  1329. $model->total_fee = $order_info['amount'];
  1330. $model->is_pay = 0;
  1331. $model->title = $order_info['title'];
  1332. $model->notify_class = $order_info['notify_class'] ?? '';
  1333. $model->rand_fee = $order_rand_fee;
  1334. if (!$model->save()) {
  1335. throw new Exception($model->getErrorMessage());
  1336. }
  1337. }
  1338. if(!empty($rand_fee_arr)){
  1339. if($order_type=='order'){
  1340. Order::updateOrderPayMoney($rand_fee_arr);
  1341. }
  1342. }
  1343. if (!empty($models)) {
  1344. foreach ($models as $model) {
  1345. $model->trade_id = $trade_id;
  1346. $model->save();
  1347. }
  1348. }
  1349. $trans->commit();
  1350. return array_merge([
  1351. 'trade_id' => $trade_id,
  1352. 'trade_no' => $paymentTrade->out_trade_no,
  1353. ], isset($rand_rule) ? compact('rand_rule') : [] ); // 如果定义随机减规则 合并
  1354. } catch (Exception $e) {
  1355. $trans->rollBack();
  1356. Yii::error($e->getMessage());
  1357. throw new \Exception($e->getMessage());
  1358. }
  1359. }
  1360. /**
  1361. * 退款-不需要传$order
  1362. * @Author:hua
  1363. * @Date:2024-03-08 10:40
  1364. * @Copyright:copyright(c)2024 广东七件事集团
  1365. * @param $refund_fee
  1366. * @param $out_trade_no
  1367. * @param $order_no
  1368. * @param $source_table
  1369. * @param $source_table_id
  1370. * @param string $desc
  1371. * @param string $from_type
  1372. * @param string $capital_type
  1373. * @return bool
  1374. */
  1375. public function commonRefund($refund_fee,$out_trade_no,$order_no,$source_table,$source_table_id, $desc='',$from_type='',$capital_type='')
  1376. {
  1377. try {
  1378. $payment_trade = PaymentTrade::findOne(['out_trade_no' => $out_trade_no]);
  1379. $user_id = $payment_trade->user_id;
  1380. $mall_id = $payment_trade->mall_id;
  1381. $total_fee = $payment_trade->pay_fee;
  1382. $payment_refund = new PaymentRefund();
  1383. $payment_refund->user_id = $user_id;
  1384. $payment_refund->mall_id = $mall_id;
  1385. $payment_refund->order_no = StringHelper::generateOrderNo('tr');
  1386. $payment_refund->out_trade_no = $out_trade_no;
  1387. $payment_refund->amount = $refund_fee;
  1388. $payment_refund->pay_type =$payment_trade->pay_type;
  1389. $payment_refund->trade_order_no = $order_no;
  1390. $payment_refund->save();
  1391. switch ($payment_refund->pay_type){
  1392. case PayTypeEnum::BALANCE:
  1393. $res = Yii::$app->payment->balance->refund($payment_refund,$source_table,$source_table_id,$desc,$from_type,$capital_type);
  1394. if($res==false) throw new \Exception('余额退款失败');
  1395. break;
  1396. case PayTypeEnum::SCORE:
  1397. $res = Yii::$app->payment->score->refund($payment_refund,$order_no,$source_table,$source_table_id,$desc,$from_type,$capital_type);
  1398. if($res==false) throw new \Exception('积分支付退款失败');
  1399. break;
  1400. case PayTypeEnum::WECHAT:
  1401. $wechat_refund = [
  1402. 'out_trade_no' => $out_trade_no,
  1403. 'transaction_id' => '',
  1404. 'out_refund_no'=>$payment_refund->order_no,
  1405. 'total_fee' => bcmul($total_fee ,100,0),
  1406. 'refund_fee' => bcmul($refund_fee,100,0),
  1407. ];
  1408. $config = $this->getWechatConfig($payment_trade->mall_id);
  1409. //微信支付配置获取
  1410. $res = Yii::$app->payment->wechat($config)->refund($wechat_refund);
  1411. if ($res['return_code'] != 'SUCCESS' || $res['result_code'] != 'SUCCESS') {
  1412. $exception = $res['err_code_des'] ?? $res['return_msg'];
  1413. throw new Exception($exception);
  1414. }
  1415. $this->refundNotify($payment_refund);
  1416. break;
  1417. case PayTypeEnum::JOINPAY:
  1418. $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY);
  1419. $config = $this->getConfig($mall_id, $pay_type);
  1420. $refund_class = Yii::$app->payment->joinpay($config);
  1421. $res = $refund_class->refund(['out_refund_no' => $out_trade_no,'refund_order_no'=>$payment_refund->order_no,'refund_amount' => $refund_fee,'refund_reason' => $desc]);
  1422. if($res==false) throw new \Exception($refund_class->getError());
  1423. break;
  1424. case PayTypeEnum::JOINPAY_FAST_PAY:
  1425. $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY_FAST_PAY);
  1426. $config = $this->getConfig($mall_id, $pay_type);
  1427. $refund_class = Yii::$app->payment->joinpayFastPay($config);
  1428. $res = $refund_class->refund(['out_trade_no' => $out_trade_no,'refund_order_no'=>$payment_refund->order_no,'refund_amount' => $refund_fee,'refund_reason' => $desc,'trade_created_at' => $payment_trade->created_at]);
  1429. if($res==false) throw new \Exception($refund_class->getError());
  1430. break;
  1431. case PayTypeEnum::ALI:
  1432. switch ($payment_trade->trade_type) {
  1433. case AliPay::JS:
  1434. $config = $this->getAlipayConfig($payment_trade);
  1435. break;
  1436. default:
  1437. $config = \Yii::$app->services->pay->getConfig($mall_id,PayTypeEnum::action(PayTypeEnum::ALI));
  1438. }
  1439. $refund_class = Yii::$app->payment->alipay($config);
  1440. $res = $refund_class->refund(['out_trade_no' => $out_trade_no,'out_request_no'=>$payment_refund->order_no,'refund_amount' => $refund_fee,'refund_reason' => $desc,'trade_no' => $payment_trade->third_trade_no]);
  1441. if($res['is_success'] && !empty($res['fund_change'])){
  1442. $this->refundNotify($payment_refund);
  1443. }
  1444. if($res == false) throw new \Exception('支付宝退款失败');
  1445. break;
  1446. case PayTypeEnum::OFFLINE:
  1447. break;
  1448. default:
  1449. $res = GlobalInvoke::exec(GlobalInvoke::INVOKE_ORDER_REFUND, $mall_id, [
  1450. 'mall_id' => $mall_id,
  1451. 'order_no' => $order_no,
  1452. 'refund_order_no' => $order_no,
  1453. 'payment_trade_id' => $payment_trade->id,
  1454. 'refund_price' => $refund_fee,
  1455. 'pay_type' => $payment_refund->pay_type,
  1456. ]);
  1457. list($boolean, $msg) = $res;
  1458. if ($boolean === false) {
  1459. throw new \Exception($msg);
  1460. }
  1461. }
  1462. return true;
  1463. } catch (Exception $e) {
  1464. throw new \Exception($e->getMessage().$e->getFile().$e->getLine());
  1465. }
  1466. }
  1467. /**
  1468. * 订单支付验证
  1469. * @param $trade_id
  1470. * @return bool
  1471. */
  1472. public function getPayVerify($trade_id): bool
  1473. {
  1474. try {
  1475. $paymentTradeInfo = PaymentTrade::findOne(['id' => $trade_id]);
  1476. if (empty($paymentTradeInfo)) {
  1477. Yii::$app->custom->logs('支付订单不存在:', $trade_id);
  1478. return false;
  1479. }
  1480. if ($paymentTradeInfo->is_pay == PayTypeEnum::ON_LINE) {
  1481. return false;
  1482. }
  1483. } catch (Exception $e) {
  1484. return false;
  1485. }
  1486. return true;
  1487. }
  1488. }