| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675 |
- <?php
- namespace services\common;
- use addons\AlipayMiniHuabei\common\enums\AlipayMiniHuabeiEnum;
- use addons\CardCoupons\common\models\CardCouponsScoreGive;
- use addons\RandFee\common\service\RandFeeService;
- use addons\SandPay\common\enums\SandPayEnums;
- use common\components\Payment;
- use common\components\payment\AliPay;
- use common\components\Service;
- use common\enums\AddonsEnum;
- use common\enums\AliPayPayTypeEnum;
- use common\enums\HuijuBillTypeEnum;
- use common\enums\JoinPayTypeEnum;
- use common\enums\PayReqSourceEnum;
- use common\enums\StatusEnum;
- use common\enums\UserWalletEnum;
- use common\globals\GlobalInvoke;
- use common\helpers\StringHelper;
- use common\models\common\Addons;
- use common\models\common\CapitalLog;
- use common\models\common\Log;
- use common\models\common\PaymentRefund;
- use common\models\common\PaymentTradeLog;
- use common\models\common\PaymentTransfer;
- use common\models\common\PlatformSetting;
- use common\models\mall\MallAddons;
- use common\models\order\Order;
- use common\models\user\User;
- use common\models\user\UserWallet;
- use EasyWeChat\Factory;
- use Exception;
- use Yii;
- use common\models\common\PaymentTrade;
- use common\models\common\PaymentTradeOrder;
- use common\enums\WechatPayTypeEnum;
- use common\enums\PayTypeEnum;
- use common\enums\SettingEnum;
- use common\helpers\Url;
- use common\models\common\MallSetting;
- use yii\helpers\Json;
- use common\models\common\PaymentTradeGroupOrder;
- /**
- * 支付服务类
- * Class PayService
- * @package services\common
- */
- class PayService extends Service
- {
- /**
- * 当前支持的支付方式
- * @param $mall_id
- * @param string $app_platform
- * @param string $req_source 服务请求来源,1:商品订单;2:余额充值;3:佣金提现;4:余额提现; 198:金币充值;
- * @param array $extra 额外信息,可能有pay_money,支付金额
- * @return array
- */
- public function getEnablePayment($mall_id=0, $app_platform='h5', $req_source = 1, $extra = [])
- {
- if ($mall_id) {
- $config = Yii::$app->services->setting->mall->get($mall_id, 'pay', true);
- } else {
- //系统配置
- $config = Yii::$app->services->setting->platform->get('pay', true);
- }
- $enable_pay_type = [];
- foreach ($config as $key => $value) {
- $payment_mode = json_decode($value, true);
- if (!$payment_mode['status']) {
- continue;
- }
- $enable_pay_type[] = $key;
- }
- $all_payment = PayTypeEnum::allTypeConf($mall_id);
- $enable_payment = [];
- foreach($enable_pay_type as $pay_type)
- {
- $payment_info = $all_payment[$pay_type] ?? [];
- if(empty($payment_info)) continue;
- $trade_type = 'default';
- switch ($payment_info['pay_type']) {
- case PayTypeEnum::WECHAT:
- $trade_type = WechatPayTypeEnum::getTypeByPlatform($app_platform);
- break;
- case PayTypeEnum::ALI:
- $trade_type = AliPayPayTypeEnum::getTypeByPlatform($app_platform);
- break;
- case PayTypeEnum::JOINPAY:
- $trade_type = JoinPayTypeEnum::getTypeByPlatform($app_platform);
- break;
- case PayTypeEnum::HUIJU_BILL:
- $trade_type = HuijuBillTypeEnum::getTypeByPlatform($app_platform);
- break;
- case PayTypeEnum::SANDPAY_FAST_PAY:
- $trade_type = SandPayEnums::getTypeByPlatform($app_platform);
- break;
- case PayTypeEnum::SCORE:
- $trade_type = $pay_type;
- break;
- case PayTypeEnum::MIXED:
- $trade_type = $pay_type;
- break;
- }
- if (!empty($trade_type)) {
- $payment_info['action'] = PayTypeEnum::action($payment_info['pay_type']);
- $payment_info['trade_type'] = $trade_type;
- $payment_mode = json_decode($config[$pay_type], true);
- $enable_payment[$pay_type] = $payment_info;
- }
- }
- // 如果是APP
- if ($app_platform == User::PLATFORM_APP) {
- $groups = SettingEnum::getMallType('mall_basic');
- $data = MallSetting::getConfByGroup($mall_id, $groups, true);
- $seting = !empty($data['pay']['wechat']['value'])
- ? json_decode($data['pay']['wechat']['value'], true) : [];
- $close_app_pay = !empty($seting['close_app_pay']) ? $seting['close_app_pay'] : false;
- // 后台关闭APP支付
- if ($close_app_pay) unset($enable_payment['wechat']);
- }
- $params['enable_payment']=$enable_payment;
- $params['mall_id']=$mall_id;
- $params['app_platform'] = $app_platform;
- $params['req_source'] = $req_source;
- $params['pay_money'] = $extra['pay_money'] ?? 0;
- $params['user_id'] = $extra['user_id'] ?? 0;
- $return = GlobalInvoke::exec(GlobalInvoke::INVOKE_GET_PAYMENT_LIST,$mall_id,$params);
- if(!empty($return)) {
- foreach ($return as $key => $item) { // 判断是否有需要绑定父支付方式的,比如支付宝花呗需要绑定到支付宝支付
- if (!empty($item['bind_to'])) {
- $enable_payment[$item['bind_to']]['children'][] = $item;
- unset($return[$key]);
- }
- }
- if (!empty($return)) {
- $enable_payment = array_merge($enable_payment,$return);
- }
- }
- // source = 5 收银台。 962 总平台没有开启积分支付的情况下,可以单独设置门店支持积分支付,但是目前仅支持购买门店商品,不支持收银台订单,希望优化
- if($req_source == 6 || $req_source == 5){
- $payment = \Yii::$app->services->setting->addons->get($mall_id,AddonsEnum::ADDONS_NAME_STORE, 'payment');
- if($req_source == 6 && !empty($payment['payment_is_enable'])){
- $enable_payment_type = $payment['payment_type'] ? json_decode($payment['payment_type'],true) : [];
- foreach($enable_payment as $key => $pay_type){
- if(!in_array($pay_type['pay_type'],$enable_payment_type)){
- unset($enable_payment[$key]);
- }
- }
- }
- if(!empty($payment['score_pay_setting'])){
- $pay_type_arr = array_column($enable_payment,'pay_type');
- $pay_type = PayTypeEnum::action(PayTypeEnum::SCORE);
- if(!in_array(PayTypeEnum::SCORE,$pay_type_arr) && !empty($payment['score_pay'])){
- $payment_info = $all_payment[$pay_type];
- $payment_info['action'] = $pay_type;
- $payment_info['trade_type'] = $pay_type;
- $enable_payment[$pay_type] = $payment_info;
- }elseif(in_array(PayTypeEnum::SCORE,$pay_type_arr) && empty($payment['score_pay'])){
- unset($enable_payment[$pay_type]);
- }
- }
- }
- if($req_source == PayReqSourceEnum::SCORE_GAME_GOLD_RECHARGE || $req_source == PayReqSourceEnum::SCORE_GAME_PROJECT_PAY){
- //只支持第三方支付
- $filer_list = array_keys(PayTypeEnum::mallParty());
- foreach ($enable_payment as $key => $item){
- if(in_array($item['pay_type'],$filer_list)){
- unset($enable_payment[$key]);
- }
- }
- }
- $payment_list = array_values($enable_payment);
- $params['enable_payment'] = $payment_list;
- $params['user_id'] = $extra['user_id'] ?? 0; // 需要传user_id
- $filter_result = GlobalInvoke::exec(GlobalInvoke::INVOKE_FILTER_PAYMENT_LIST, $mall_id, $params);
- !empty($filter_result) && $payment_list = $filter_result;
- return $payment_list;
- }
- /**
- * 支付金额为0的支付
- * @param $mall_id
- * @return array
- */
- public function getEnablePayMentByZero($mall_id){
- $pay_type_list=[];
- $config = Yii::$app->services->setting->mall->get($mall_id, 'pay', true);
- $balance_name = \Yii::$app->services->setting->mall->get($mall_id, 'balance.balance_name');
- $name = '余额支付';
- $name = str_replace('余额',$balance_name,$name);
- $payment_mode = json_decode($config['balance'], true);
- if(!empty($payment_mode['status'])) {
- $protocol = ($_SERVER['SERVER_PORT'] == 443 ? 'https' : 'http') . '://';
- $host = $protocol . $_SERVER['HTTP_HOST'];
- $pay_type_list[] = [
- 'action' => "balance",
- 'img' => "{$host}/resources/img/payment/balance.png",
- 'name' => $name,
- 'pay_type' => PayTypeEnum::BALANCE,
- 'trade_type' => "default",
- 'is_wallet' => 1
- ];
- }
- return $pay_type_list;
- }
- /**
- * 微信支付
- * @param PaymentTrade $trade 交易流水表记录
- * @param string $tradeType 微信支付类型
- * @param array $params 支付参数
- * @return bool|array
- * @throws
- */
- public function wechat(PaymentTrade $trade, $tradeType, $params = [])
- {
- // 小程序支付
- if ($tradeType == WechatPayTypeEnum::MINI_PROGRAM) {
- return $this->wechatMp($trade, $params);
- }
- // 生成订单
- $order = [
- 'body' => $trade->title, // 内容
- 'out_trade_no' => $trade->out_trade_no, // 订单号
- 'total_fee' => $trade->getOnlinePayFee() * 100,
- 'notify_url' => Url::toApi(['notify/' . PayTypeEnum::action(PayTypeEnum::WECHAT)], TRUE), // 回调地址
- 'detail' => $trade->title,
- ];
- // 判断如果是js支付
- $tradeType == WechatPayTypeEnum::JS && $order['openid'] = $params['openid'];
- // 判断如果是刷卡支付
- //$tradeType == WechatPayTypeEnum::POS && $order['auth_code'] = $params['auth_code'];
- //微信配置
- $config = $this->getWechatConfig($trade->mall_id);
- $result = Yii::$app->payment->wechat($config)->$tradeType($order);
- if (empty($result)) {
- $debug = Yii::$app->payment->wechat($config)->$tradeType($order, true);
- //Yii::$app->services->actionLog->create('wechatPayError', json_encode($debug));
- return $debug;
- }
- return $result;
- }
- /**
- * 微信小程序支付
- * @param $trade
- * @param array $params
- * @return mixed
- * @throws
- */
- public function wechatMp(PaymentTrade $trade, $params = [])
- {
- //微信配置
- $config = $this->getWechatConfig($trade->mall_id);
- //微信小程序配置
- Yii::$app->params['wechatConfig'] = [
- 'app_id' => $config['app_id'] ?? '',
- 'secret' => $config['secret'] ?? '',
- 'token' => $config['token'] ?? '',
- ];
- Yii::$app->params['wechatPaymentConfig'] = [
- 'app_id' => $config['app_id'] ?? '',
- 'secret' => $config['secret'] ?? '',
- 'mch_id' => $config['wechat_mch_id'] ?? '',
- 'key' => $config['wechat_pay_secret'] ?? '',
- 'cert_path' => dirname(Yii::$app->basePath).$config['wechat_cert_pem_file'],
- 'key_path' => dirname(Yii::$app->basePath).$config['wechat_key_pem_file'],
- 'sandbox' => false,
- ];
- \Yii::$app->params['wechatMiniProgramConfig'] = [
- 'app_id' => $config['mini_program_app_id'] ?? '',
- 'secret' => $config['mini_program_secret'] ?? '',
- 'mch_id' => $config['wechat_mch_id'] ?? '',
- 'key' => $config['wechat_pay_secret'],
- 'cert_path' => dirname(Yii::$app->basePath).$config['wechat_cert_pem_file'],
- 'key_path' => dirname(Yii::$app->basePath).$config['wechat_key_pem_file'],
- ];
- $orderData = [
- 'trade_type' => 'JSAPI',
- 'body' => $trade->title,
- 'detail' => $trade->title,
- 'notify_url' => Url::toApi(['notify/wechat-mp'], TRUE), // 支付结果通知网址
- 'out_trade_no' => $trade->out_trade_no, // 支付
- 'total_fee' => $trade->getOnlinePayFee() * 100,
- 'openid' => $params['openid'] ?? '', // trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识,
- ];
- $payment = Factory::payment(Yii::$app->params['wechatMiniProgramConfig']);
- // $payment = Yii::$app->wechat->payment;
- $result = $payment->order->unify($orderData);
- if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
- return $payment->jssdk->sdkConfig($result['prepay_id']);
- }
- // Yii::$app->services->actionLog->create('wechatMpPayError', json_encode($result));
- return $result;
- }
- /**
- * 支付宝支付
- * @param PaymentTrade $trade
- * @param string $tradeType 交易类型
- * @param array $params
- * @return array
- * @throws
- */
- public function alipay(PaymentTrade $trade, $tradeType, $params = [])
- {
- // 生成订单
- $order = [
- 'out_trade_no' => $trade->out_trade_no,
- 'total_amount' => $trade->getOnlinePayFee(),
- 'subject' => $trade->title,
- ];
- $pay_type = PayTypeEnum::action(PayTypeEnum::ALI);
- if ($tradeType == AliPayPayTypeEnum::JS) {
- $order['buyer_id'] = $params['openid'];
- $order['seller_id'] = $config['mch_id'] ?? '';
- // 这里需要取订单详情
- $order['goods_detail'] = $params['goods_detail'] ?? [];
- // 校验是否有分期
- if (!empty($params['period']) && MallAddons::isInstall($trade->mall_id, AddonsEnum::ADDONS_NAME_ALIPAY_MINI_HUABEI)) {
- $huabei_config = Yii::$app->services->setting->addons->get($trade->mall_id, AlipayMiniHuabeiEnum::ADDONS_NAME, 'basic');
- $huabei_config['is_enable'] = $huabei_config['is_enable'] ?? StatusEnum::DISABLED;
- $huabei_config['periods'] = !empty($huabei_config['periods']) ? Json::decode($huabei_config['periods']) : [];
- if ($huabei_config['is_enable'] == StatusEnum::ENABLED && in_array($params['period'], $huabei_config['periods'])) { // 如果是启用状态且当前期数可使用
- $order['extend_params'] = [
- 'hb_fq_num' => $params['period'],
- 'hb_fq_seller_percent' => $huabei_config['is_seller_fee'] == StatusEnum::ENABLED ? 100 : 0,
- ];
- }
- }
- $config = $this->getAlipayConfig($trade);
- } else {
- $config = $this->getConfig($trade->mall_id, $pay_type);
- }
- $notify_url = isset($params['notify_url']) ? $params['notify_url'] : Url::toApi(['notify/' . $pay_type], true);
- $return_url = isset($params['return_url']) ? $params['return_url'] : PlatformSetting::conf("system.h5_url",[],true);
- // 配置
- $config['notify_url'] = $notify_url; //支付通知回调地址
- if($tradeType!='app'){
- $config['return_url'] = $return_url; // 买家付款成功跳转地址
- }
- $config['sandbox'] = false;
- $pay = Yii::$app->payment->alipay($config);
- return [
- 'config' => $pay->$tradeType($order),
- ];
- }
- /**
- * 获取支付宝小程序配置信息
- * @param PaymentTrade $trade
- * @return array|mixed
- */
- public function getAlipayConfig(PaymentTrade $trade)
- {
- $config = Yii::$app->services->setting->mall->get($trade->mall_id, "alipay_mini");
- // 为避免私钥随源码泄露,推荐从文件中读取私钥字符串而不是写入源码中
- $base_path = dirname(Yii::$app->basePath);
- if ($config['is_use_cert'] == StatusEnum::ENABLED) { // 如果启用证书模式
- $config['alipay_public_cert'] = dirname(Yii::$app->basePath).'/'.$config['alipayCertPath'];
- $config['alipay_root_cert'] = dirname(Yii::$app->basePath).'/'.$config['alipayRootCertPath'];
- $config['app_cert'] = dirname(Yii::$app->basePath).'/'.$config['merchantCertPath'];
- } else {
- //注:如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
- $config['alipay_public_key'] = file_get_contents("$base_path/{$config['alipayCertPath']}");
- }
- $config['alipay_app_id'] = $config['app_id'];
- $config['alipay_private_key'] = $config['merchantPrivateKey'];
- return $config;
- }
- /**
- * 汇聚聚合支付
- * @param PaymentTrade $trade
- * @param $tradeType
- * @param array $params
- * @return array|mixed
- * @throws Exception
- */
- public function joinpay(PaymentTrade $trade, $tradeType, $params = [])
- {
- $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY);
- $order = [
- 'title' => $trade->title,
- 'order_no' => $trade->out_trade_no,
- 'amount' => $trade->getOnlinePayFee(),
- 'mall_id' => $trade->mall_id,
- 'notify_url' => Url::toApi(['notify/' . $pay_type], TRUE),
- 'type' => $tradeType,
- 'open_id' => $params['openid'] ?? ''
- ];
- $config = $this->getConfig($trade->mall_id, $pay_type);
- $pay = Yii::$app->payment->joinpay($config);
- return $pay->pay($order);
- }
- /**
- * 汇聚快捷支付
- * @param PaymentTrade $trade
- * @param $tradeType
- * @param array $params
- * @return bool
- * @throws Exception
- */
- public function joinpay_fast(PaymentTrade $trade, $tradeType, $params = [])
- {
- $order = [
- 'order_no' => $trade->out_trade_no,
- 'sms_code' => $params['sms_code'],
- 'created_at' => $trade->created_at
- ];
- $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY_FAST_PAY);
- $config = $this->getConfig($trade->mall_id, $pay_type);
- $pay = Yii::$app->payment->joinpayFastPay($config);
- return $pay->pay($order);
- }
- /**
- * 杉德快捷支付
- * @param PaymentTrade $trade
- * @param $tradeType
- * @param array $params
- * @return bool
- * @throws Exception
- */
- public function sandpay_fast(PaymentTrade $trade, $tradeType, $params = [])
- {
- $order = [
- 'order_no' => $trade->out_trade_no,
- 'sms_code' => $params['sms_code'],
- 'created_at' => $trade->created_at
- ];
- $config = $this->getConfig($trade->mall_id, SandPayEnums::SAND_PAY_FAST);
- $pay = Yii::$app->payment->sandpayFastPay($config);
- return $pay->pay($order);
- }
- /**
- * 汇聚分账支付
- *
- * @param PaymentTrade $trade
- * @param string $tradeType 支付平台类型 [WEIXIN_GZH, WEIXIN_XCX, ...]
- * @param array $params
- * @return array
- * @throws Exception
- */
- public function huiju_bill(PaymentTrade $trade, $tradeType, $params = [])
- {
- $pay_type = PayTypeEnum::action(PayTypeEnum::HUIJU_BILL);
- $config = $this->getConfig($trade->mall_id, $pay_type);
- $pay = Yii::$app->payment->huijuBill($config);
- // 判断支付类型
- if (!in_array($tradeType, HuijuBillTypeEnum::getConstants())) {
- throw new Exception('未定义该支付类型');
- }
- $order = [
- 'trade_id' => $trade->id,
- 'title' => $trade->title,
- 'order_no' => $trade->out_trade_no,
- 'amount' => $trade->pay_fee,
- 'mall_id' => $trade->mall_id,
- 'notify_url' => Url::toApi(['notify/' . $pay_type], TRUE),
- 'type' => $tradeType,
- 'open_id' => $params['openid'] ?? ''
- ];
- return $pay->pay($order);
- }
- const BALANCE_DESC_TYPES = [
- 0 => '支付',
- 101 => '共享平台-保证金支付',
- 102 => '共享平台-尾款支付'
- ];
- /**
- * 余额支付
- * @param PaymentTrade $trade
- * @param $tradeType
- * @param array $params
- * @throws
- * @return
- */
- public function balance(PaymentTrade $trade, $tradeType, $params = [])
- {
- //校验支付密码
- $balance_pay_setting = Yii::$app->services->setting->mall->get($trade->mall_id, 'pay.balance');
- $balance_pay_setting = json_decode($balance_pay_setting,'true');
- if(!empty($balance_pay_setting['pay_password_status']) && $trade->pay_fee>0){
- $user = User::findOne(['id'=>$trade->user_id]);
- if(empty($user->transaction_password)){
- throw new Exception('您未设置支付密码');
- }
- if(empty($params['transaction_password'])){
- throw new Exception('请输入交易密码');
- }
- if (!\Yii::$app->getSecurity()->validatePassword(trim($params['transaction_password']), $user->transaction_password)) {
- throw new Exception('支付密码错误');
- }
- }
- $t = \Yii::$app->db->beginTransaction();
- try{
- //判断是否有卡券插件
- if (MallAddons::isInstall($trade->mall_id, AddonsEnum::ADDONS_NAME_CARD_COUPONS)) {
- $trade_id = $trade->id;
- $tradeOrder = PaymentTradeOrder::findOne(['trade_id'=>$trade_id]);
- //$findOrder = Order::findOne(['out_trade_no'=>$trade->out_trade_no]);
- $giveData = [
- 'mall_id' => $trade->mall_id,
- 'user_id' => $trade->user_id,
- 'pay_fee' => $trade->pay_fee,
- 'source_table' => 'transaction',
- 'source_table_id' => $trade->id,
- 'order_no' => $tradeOrder->order_no,
- ];
- //卡券抵扣
- $cardMoney = CardCouponsScoreGive::setOrderPay($giveData);
- $trade->pay_fee = $cardMoney;
- }
- //2023-04-18
- $desc_prefix = self::BALANCE_DESC_TYPES[$params['balance_desc_type']] ?? self::BALANCE_DESC_TYPES[0];
- $order_no = PaymentTradeOrder::find()->where(['trade_id' => $trade->id])->select('order_no')->scalar();
- $fee = bcmul($trade->getWalletPayFee(), 1, 2);
- $handle_wallet = [
- 'message_id' => 0,
- 'mall_id' => $trade->mall_id,
- 'user_id' => $trade->user_id,
- 'wallet_type' => UserWalletService::WALLET_TYPE_BALANCE,
- 'money' => -$trade->getWalletPayFee(),
- 'change_type' => CapitalLog::CHANGE_TYPE_SUB,
- 'desc' => "订单【{$order_no}】{$desc_prefix} {$fee} 元",
- 'source_table' => 'transaction',
- 'source_table_id' => $trade->id,
- 'from_type' => UserWalletEnum::CONSUME,
- 'capital_type' => UserWalletEnum::CONSUMPTION_ORDER,
- 'order_no' => $order_no,
- ];
- if ($params['model_type'] == 'Area') {
- $handle_wallet['from_type'] = UserWalletEnum::FROM_AREA;
- $handle_wallet['capital_type'] = UserWalletEnum::AREA_APPLY_CONSUME;
- $handle_wallet['desc'] = '购买区代申请资格,支付'.$trade->pay_fee.'元';
- }
- $result = UserWalletService::handle(0,$handle_wallet);
- if(!$result){
- $error_msg = UserWalletService::$static_error ?? '余额支付失败';
- throw new Exception($error_msg);
- }
- $t->commit();
- }catch(\Exception $e){
- $t->rollBack();
- throw new \Exception($e->getMessage());
- }
- // 组合支付不进行修改支付流水
- $trade->setWalletPayType(PayTypeEnum::BALANCE);
- !$trade->isMixedPay() && $trade->is_pay = 1;
- !$trade->isMixedPay() && $trade->pay_time = time();
- if (!$trade->save()) {
- throw new Exception('支付流水修改失败');
- }
- $orders = $this->changePaymentTradeOrder($trade, $trade->pay_type, $tradeType);
- $orders && !$trade->isMixedPay() && Yii::$app->services->pay->notify($orders);
- // 剩余支付金额
- return true;
- }
- /**
- * 积分支付
- * @param PaymentTrade $trade
- * @param $tradeType
- * @param array $params
- * @throws
- * @return
- */
- public function score(PaymentTrade $trade, $tradeType, $params = [])
- {
- //校验支付密码
- $score_pay_setting = Yii::$app->services->setting->mall->get($trade->mall_id, 'pay.score');
- $score_setting = Yii::$app->services->setting->mall->get($trade->mall_id, 'score');
- $score_name = $score_setting['score_name'] ?: '积分';
- $score_balance_ratio = $score_setting['score_balance_ratio'] ?: 1;// 积分兑换人民币比例
- $score_pay_setting = json_decode($score_pay_setting,'true');
- if(!empty($params['source']) && $params['source'] == 6){
- $store_payment = \Yii::$app->services->setting->addons->get($trade->mall_id,AddonsEnum::ADDONS_NAME_STORE, 'payment');
- if(!empty($store_payment['score_pay_password_status'])){
- $score_pay_setting['pay_password_status'] = 1;
- }
- }
- // 门店积分支付设置
- $store_score_payment = Yii::$app->services->setting->addons->get($trade->mall_id, 'Store', 'payment');
- // 是否安装门店插件
- $is_store = Addons::isInstall(AddonsEnum::ADDONS_NAME_STORE);
- $is_store_score = $is_store && !empty($store_score_payment['score_pay_password_status']) && !empty($params['is_store_pay']);
- if((!empty($score_pay_setting['pay_password_status']) || $is_store_score) && $trade->pay_fee>0){
- $user = User::findOne(['id'=>$trade->user_id]);
- if(empty($user->transaction_password)){
- throw new Exception('您未设置支付密码');
- }
- if(empty($params['transaction_password'])){
- throw new Exception('请输入交易密码');
- }
- if (!\Yii::$app->getSecurity()->validatePassword(trim($params['transaction_password']), $user->transaction_password)) {
- throw new Exception('支付密码错误');
- }
- }
- // 检测积分跟人民币比例是否为1:1
- if ($score_balance_ratio != 1) throw new Exception($score_name.'与人民币比例必须为1:1');
- // 检测积分余额是否足够
- $wallet = UserWallet::find()->where(['user_id' => $trade->user_id, 'status' => StatusEnum::ENABLED])->asArray()->one();
- if ($wallet['score'] < $trade->getWalletPayFee()) throw new Exception($score_name . '不足');
- // 获取订单编号
- $order_no = PaymentTradeOrder::find()->where(['trade_id' => $trade->id])->select('order_no')->scalar();
- $fee = bcmul($trade->getWalletPayFee(), 1, 2);
- $handle_wallet = [
- 'message_id' => 0,
- 'mall_id' => $trade->mall_id,
- 'user_id' => $trade->user_id,
- 'wallet_type' => UserWalletService::WALLET_TYPE_SCORE,
- 'money' => -$trade->getWalletPayFee(),
- 'desc' => "订单号【{$order_no}】支付 {$fee} {$score_name}",
- 'source_table' => 'transaction',
- 'source_table_id' => $trade->id,
- 'from_type' => UserWalletEnum::CONSUME,
- 'order_no' => $order_no,
- 'capital_type' => UserWalletEnum::CONSUMPTION_ORDER,
- ];
- $result = UserWalletService::handle(0,$handle_wallet);
- if(!$result){
- $error_msg = UserWalletService::$static_error ?? $score_name.'支付失败';
- throw new Exception($error_msg);
- }
- // 组合支付不进行修改支付流水
- $trade->setWalletPayType(PayTypeEnum::SCORE);
- !$trade->isMixedPay() && $trade->is_pay = 1;
- !$trade->isMixedPay() && $trade->pay_time = time();
- if (!$trade->save()) {
- throw new Exception('支付流水修改失败');
- }
- $orders = $this->changePaymentTradeOrder($trade, $trade->pay_type, $tradeType);
- $orders && !$trade->isMixedPay() && Yii::$app->services->pay->notify($orders);
- return true;
- }
- /**
- * 消费卡支付
- * @param PaymentTrade $trade
- * @param $tradeType
- * @param array $params
- * @throws
- * @return
- */
- public function consumer_card(PaymentTrade $trade, $tradeType, $params = [])
- {
- $t = \Yii::$app->db->beginTransaction();
- try{
- //2023-04-18
- $order_no = PaymentTradeOrder::find()->where(['trade_id' => $trade->id])->select('order_no')->scalar();
- $fee = bcmul($trade->getWalletPayFee(), 1, 2);
- //扣除消费卡余额
- $insert_wallet = [
- 'mall_id' => $trade->mall_id,
- 'user_id' => $trade->user_id,
- 'wallet_type' => \addons\ScoreGame\common\enums\ScoreGameEnum::WALLET_TYPE_CONSUMER_CARD,
- 'change_type' => CapitalLog::CHANGE_TYPE_SUB,
- 'change_num' => -$trade->getWalletPayFee(),
- 'source_table' => 'transaction',
- 'source_table_id' => $trade->id,
- 'from_type' => \addons\ScoreGame\common\enums\ScoreGameEnum::SCORE_CAPITAL_TYPE_CONSUME,
- 'capital_type' => \addons\ScoreGame\common\enums\ScoreGameEnum::SCORE_CAPITAL_TYPE_CONSUME,
- 'order_no' => $order_no,
- 'desc' => "订单号【{$order_no}】支付 {$fee}",
- 'created_at' => time(),
- 'updated_at' => time(),
- ];
- $res = \addons\ScoreGame\common\logic\WalletLogic::handle($insert_wallet);
- if(!$res){
- $error_msg = '扣除消费卡余额失败';
- throw new Exception($error_msg);
- }
- $t->commit();
- }catch(\Exception $e){
- $t->rollBack();
- throw new \Exception($e->getMessage());
- }
- // 组合支付不进行修改支付流水
- $trade->setWalletPayType(PayTypeEnum::BALANCE);
- !$trade->isMixedPay() && $trade->is_pay = 1;
- !$trade->isMixedPay() && $trade->pay_time = time();
- if (!$trade->save()) {
- throw new Exception('支付流水修改失败');
- }
- $orders = $this->changePaymentTradeOrder($trade, $trade->pay_type, $tradeType);
- $orders && !$trade->isMixedPay() && Yii::$app->services->pay->notify($orders);
- // 剩余支付金额
- return true;
- }
- /**
- * @desc:退款
- * @Author: hua
- * @Date: 2021/12/20
- * @Time: 9:03
- * @Copyright: copyright (c) 2021 广东七件事集团
- * @param $totalFee//支付金额
- * @param $refundFee//退款金额
- * @param $out_trade_no//系统订单支付编号
- * @param null $order//订单对象
- * @param string $desc//描述
- * @param string $from_type//退款来源
- * @param string $capital_type//退款类型
- * @return bool
- * @throws Exception
- */
- public function refund($totalFee, $refundFee,$out_trade_no,$order, $desc='',$from_type='',$capital_type='', $pay_type = null)
- {
- try {
- $payment_trade = PaymentTrade::findOne(['out_trade_no' => $out_trade_no]);
- $totalFee = $payment_trade->getOnlinePayFee();
- $pay_type = isset($pay_type) ? $pay_type : $payment_trade->pay_type;
- $payment_refund = new PaymentRefund();
- $payment_refund->user_id =$order->user_id;
- $payment_refund->mall_id =$order->mall_id;
- $payment_refund->order_no = StringHelper::generateOrderNo('tr');
- $payment_refund->out_trade_no = $out_trade_no;
- $payment_refund->amount = $refundFee;
- $payment_refund->pay_type = $pay_type;
- $payment_refund->trade_order_no = $order->order_no;
- $payment_refund->save();
- switch ($pay_type) {
- case PayTypeEnum::BALANCE:
- $res = Yii::$app->payment->balance->refund($payment_refund,$order::tableName(),$order['id'],$desc,$from_type,$capital_type);
- if($res==false) throw new \Exception('余额退款失败');
- break;
- case PayTypeEnum::SCORE:
- $res = Yii::$app->payment->score->refund($payment_refund,$order['order_no'],$order::tableName(),$order['id'],$desc,$from_type,$capital_type);
- if($res==false) throw new \Exception('积分支付退款失败');
- break;
- case PayTypeEnum::WECHAT:
- //$res = Yii::$app->payment->wechat->refunds($payment_refund,$totalFee);
- $wechat_refund = [
- 'out_trade_no' => $out_trade_no,
- 'transaction_id' => '',
- 'out_refund_no'=>$payment_refund->order_no,
- 'total_fee' => bcmul($totalFee ,100,0),
- 'refund_fee' => bcmul($refundFee,100,0),
- ];
- //$config = Yii::$app->services->pay->getConfig($order->mall_id,'wechat');
- $config = $this->getWechatConfig($order->mall_id);
- //微信支付配置获取
- $res = Yii::$app->payment->wechat($config)->refund($wechat_refund);
- if ($res['return_code'] != 'SUCCESS' || $res['result_code'] != 'SUCCESS') {
- $exception = $res['err_code_des'] ?? $res['return_msg'];
- throw new Exception($exception);
- }
- $this->refundNotify($payment_refund);
- break;
- case PayTypeEnum::JOINPAY:
- $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY);
- $config = $this->getConfig($payment_refund->mall_id, $pay_type);
- $refund_class = Yii::$app->payment->joinpay($config);
- $res = $refund_class->refund(['out_refund_no' => $out_trade_no,'refund_order_no'=>$payment_refund->order_no,'refund_amount' => $refundFee,'refund_reason' => $desc]);
- if($res==false) throw new \Exception($refund_class->getError());
- break;
- case PayTypeEnum::JOINPAY_FAST_PAY:
- $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY_FAST_PAY);
- $config = $this->getConfig($payment_refund->mall_id, $pay_type);
- $refund_class = Yii::$app->payment->joinpayFastPay($config);
- $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]);
- if($res==false) throw new \Exception($refund_class->getError());
- break;
- case PayTypeEnum::SANDPAY_FAST_PAY:
- $pay_type = \addons\SandPay\common\enums\SandPayEnums::action(\addons\SandPay\common\enums\SandPayEnums::SANDPAY_FAST_PAY);
- $config = $this->getConfig($payment_refund->mall_id, $pay_type);
- $refund_class = Yii::$app->payment->sandpayFastPay($config);
- $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]);
- if(!$res) throw new \Exception($refund_class->getError());
- break;
- case PayTypeEnum::ALI:
- switch ($payment_trade->trade_type) {
- case AliPay::JS:
- $config = $this->getAlipayConfig($payment_trade);
- break;
- default:
- $config = \Yii::$app->services->pay->getConfig($payment_refund->mall_id,PayTypeEnum::action(PayTypeEnum::ALI));
- }
- $refund_class = Yii::$app->payment->alipay($config);
- $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]);
- if($res['is_success'] && !empty($res['fund_change'])){
- $this->refundNotify($payment_refund);
- }
- if($res == false) throw new \Exception('支付宝退款失败');
- break;
- default:
- throw new \Exception('暂时不支持该支付方式退款');
- }
- return true;
- } catch (Exception $e) {
- throw new \Exception($e->getMessage());
- }
- }
- public function queryByOutTradeNo($mall_id,$out_trade_no){
- $config = $this->getWechatConfig($mall_id);
- $res = Yii::$app->payment->wechat($config)->queryByOutTradeNo($out_trade_no);
- var_dump($res);
- }
- public function queryRefund($mall_id,$out_trade_no){
- $config = $this->getWechatConfig($mall_id);
- $res = Yii::$app->payment->wechat($config)->queryRefund($out_trade_no);
- var_dump($res);
- }
- /**
- * 支付回调
- * @param PaymentTrade $trade
- * @param int $pay_type 支付方式
- * @param string $trade_type 交易类型
- * @return bool
- * @throws Exception
- */
- public function notifyOld(PaymentTrade $trade,$pay_type,$trade_type = '')
- {
- $orders = PaymentTradeOrder::findAll(['trade_id' => $trade->id]);
- foreach ($orders as $order_info) {
- $order_info->is_pay = 1;
- $order_info->pay_type = $pay_type;
- if (!$order_info->save()) {
- throw new Exception($order_info->getErrorMessage());
- }
- $notify_class = $order_info->notify_class;
- if (!class_exists($notify_class)) {
- continue;
- }
- $notify = new $notify_class();
- //todo
- $notify->notify($order_info->toArray());
- }
- //修改支付流水调用记录
- /** @var PaymentTradeLog $log */
- $log = PaymentTradeLog::getLog($trade->id,$pay_type,$trade_type);
- if($log){
- $log->notify_result = 1;
- $log->save();
- }
- return true;
- }
- public function notify($orders)
- {
- foreach ($orders as $order_info) {
- $notify_class = $order_info->notify_class;
- if (!class_exists($notify_class)) {
- continue;
- }
- $notify = new $notify_class();
- //todo
- $notify->notify($order_info->toArray());
- }
- return true;
- }
- /**
- * 修改并获取支付订单信息
- *
- * @param PaymentTrade $trade
- * @param integer $pay_type
- * @param string $trade_type
- * @return array
- */
- public function changePaymentTradeOrder(PaymentTrade $trade, int $pay_type, $trade_type = '')
- {
- $orders = PaymentTradeOrder::findAll(['trade_id' => $trade->id]);
- foreach ($orders as $order_info) {
- $trade->is_pay && $order_info->is_pay = 1;
- $order_info->pay_type = $trade->isMixedPay() ? $trade->pay_type : $pay_type;
- if (!$order_info->save()) {
- throw new Exception($order_info->getErrorMessage());
- }
- }
- // 判断是否为钱包支付
- $trade->isWalletPay() && $trade->setWalletPaySuccess();
- // 判断是否为线上支付
- $trade->isOnlinePay() && $trade->setOnlinePaySuccess();
- //修改支付流水调用记录
- /**
- * @var PaymentTradeLog $log
- */
- $log = PaymentTradeLog::getLog($trade->id, $trade->pay_type, $trade_type);
- if ($log) {
- $log->notify_result = 1;
- $log->save();
- }
- return $orders;
- }
- /**
- * 创建订单流水
- * @param int $mall_id
- * @param int $user_id
- * @param array $orders
- * @throws
- * @return int
- */
- public function createTrade($mall_id, $user_id, $orders)
- {
- $trans = Yii::$app->db->beginTransaction();
- try {
- if (empty($orders) || !is_array($orders)) {
- throw new Exception('订单信息有误');
- }
- $order_amount = 0;
- $title = '';
- $order_nos = '';
- foreach ($orders as $order_info) {
- $order_amount = bcadd($order_amount, $order_info['amount'], 2);
- $title .= str_replace(';', '', $order_info['title']) . ';';
- $is_rand_fee = isset($order_info['order_type']) ? true : false;
- $order_nos.= $order_info['order_no'] . ';';
- }
- //判断支付金额 不能小于0
- if($order_amount < 0){
- throw new Exception('支付金额有误');
- }
- // 全局调用
- $models = [];
- if ($mall_id && $is_rand_fee) {
- $res = GlobalInvoke::exec(GlobalInvoke::RAND_FEE, $mall_id, compact('orders', 'mall_id', 'user_id'));
- if (!empty($res)) list($rand_fee, $models, $rand_rule) = $res;
- }
- $rand_fee = !empty($rand_fee) ? $rand_fee : 0;
- $pay_fee = bcsub($order_amount, $rand_fee, 2);
- if($pay_fee < 0) $pay_fee = 0;
- $trade = [
- 'mall_id' => $mall_id,
- 'user_id' => $user_id,
- 'out_trade_no' => PaymentTrade::createOutTradeNo(),
- 'pay_fee' => $pay_fee,
- 'total_fee' => $order_amount,
- 'rand_fee' => $rand_fee,
- 'title' => mb_convert_encoding(mb_substr($title, 0, 32),'UTF-8'),
- //'notify_url' => '',
- 'return_url' => $orders[0]['return_url'] ?? '',
- ];
- //正则过滤除了汉字字母数字的其它字符
- $trade['title'] = preg_replace('#[^\x{4e00}-\x{9fa5}A-Za-z0-9]#u','',$trade['title']);
- $paymentTrade = new PaymentTrade();
- $paymentTrade->attributes = $trade;
- if (!$paymentTrade->save()) {
- throw new Exception($paymentTrade->getErrorMessage());
- }
- $trade_id = $paymentTrade->id;
- \Yii::$app->custom->logs(('trade_id:'.$trade_id.':order_nos:'.$order_nos));
- $rand_fee_arr = [];
- $total_amount = array_sum(array_column($orders,'amount'));
- $temp_rand_fee = 0;
- $order_type = '';
- foreach ($orders as $k=>$order_info) {
- $order_rand_fee = 0;
- if($rand_fee){
- if(!isset($orders[$k+1])){
- $order_rand_fee = bcsub($rand_fee,$temp_rand_fee,2);
- }else{
- $rate = bcdiv($order_info['amount'],$total_amount,2);
- $order_rand_fee = bcmul($rate,$rand_fee,2);
- $temp_rand_fee = bcadd($order_rand_fee,$temp_rand_fee,2);
- }
- $rand_fee_arr[$order_info['order_no']] = $order_rand_fee;
- $order_type = $order_info['order_type'];
- }
- $model = new PaymentTradeOrder();
- $model->trade_id = $trade_id;
- $model->order_no = $order_info['order_no'];
- $model->order_type = $order_info['order_type']; //订单类型
- $model->pay_fee = $paymentTrade->pay_fee;
- $model->total_fee = $order_info['amount'];
- $model->is_pay = 0;
- $model->title = $order_info['title'];
- $model->notify_class = $order_info['notify_class'] ?? '';
- $model->rand_fee = $order_rand_fee;
- if (!$model->save()) {
- throw new Exception($model->getErrorMessage());
- }
- \Yii::$app->custom->logs('trade_id:'.$trade_id.':out_trade_no:'.$paymentTrade->out_trade_no.':order_no:'.$order_info['order_no']);
- }
- if(!empty($rand_fee_arr)){
- if($order_type=='order'){
- Order::updateOrderPayMoney($rand_fee_arr);
- }
- }
- if (!empty($models)) {
- foreach ($models as $model) {
- $model->trade_id = $trade_id;
- $model->save();
- }
- }
- $trans->commit();
- return array_merge([
- 'trade_id' => $trade_id,
- 'trade_no' => $paymentTrade->out_trade_no,
- ], isset($rand_rule) ? compact('rand_rule') : [] ); // 如果定义随机减规则 合并
- } catch (Exception $e) {
- $trans->rollBack();
- Yii::error($e->getMessage());
- return false;
- }
- }
- public function getByTradeId($id)
- {
- return PaymentTrade::findOne(['id' => $id]);
- }
- public function getByTradeNo($out_trade_no)
- {
- return PaymentTrade::findOne(['out_trade_no' => $out_trade_no]);
- }
- /**
- * 获取商城支付配置
- * @param $mall_id
- * @param $pay_type
- * @return array|mixed
- * @throws
- */
- public function getConfig($mall_id , $pay_type)
- {
- if ($mall_id) {
- $config = Yii::$app->services->setting->mall->get($mall_id, 'pay.' . $pay_type, true);
- } else {
- //获取系统配置
- $config = Yii::$app->services->setting->platform->get($pay_type,true);
- }
- if (empty($config)) {
- throw new Exception('查询不到对应的支付配置');
- }
- return $config && is_string($config) ? json_decode($config, true) : $config;
- }
- /**
- * @param $mall_id
- * @return array|false|null|string
- * @throws Exception
- */
- public function getWechatConfig($mall_id = 0)
- {
- $payment_conf = [];
- if ($mall_id) {
- $payment_conf = Yii::$app->services->setting->mall->get($mall_id, 'pay.wechat', true);
- if (empty($payment_conf)) {
- throw new Exception('查询不到微信支付配置');
- }
- $payment_conf = json_decode($payment_conf,true);
- //获取公众号配置
- $wechat_conf = Yii::$app->services->setting->mall->get($mall_id, 'wechat', true);
- if($wechat_conf){
- $payment_conf = array_merge($payment_conf,$wechat_conf);
- }
- //获取小程序配置
- $mini_program = Yii::$app->services->setting->mall->get($mall_id, 'mini_program', true);
- if($mini_program){
- $payment_conf['mini_program_app_id'] = $mini_program['app_id'];
- $payment_conf['mini_program_secret'] = $mini_program['secret'];
- }
- }else{
- //获取平台配置
- $payment_conf = Yii::$app->services->setting->get('wechat', true);
- if (empty($payment_conf)) {
- throw new Exception('查询不到微信支付配置');
- }
- if (!is_array($payment_conf)) $payment_conf = json_decode($payment_conf,true);
- //获取公众号配置
- // $wechat_conf = Yii::$app->services->setting->get('wechat_public', true);
- // if($wechat_conf){
- // $payment_conf = array_merge($payment_conf,$wechat_conf);
- // }
- //获取小程序配置
- $mini_program = Yii::$app->services->setting->get('mini_program', true);
- if($mini_program){
- $payment_conf['mini_program_app_id'] = $mini_program['app_id'];
- $payment_conf['mini_program_secret'] = $mini_program['secret'];
- }
- }
- return $payment_conf;
- }
- /**
- * todo 需要调用公共调用
- * 校验交易类型
- * @param string $pay_type 支付方式
- * @param string $trade_type 交易类型
- * @throws
- * @return bool
- */
- public function verifyTradeType($pay_type,$trade_type)
- {
- switch ($pay_type) {
- case PayTypeEnum::WECHAT :
- if (!in_array($trade_type, WechatPayTypeEnum::getKeys())) {
- throw new Exception('微信交易类型不符');
- }
- break;
- case PayTypeEnum::ALI :
- if (!in_array($trade_type, ['pc', 'app', 'f2f', 'wap','js'])) {
- throw new Exception('支付宝交易类型不符');
- }
- break;
- case PayTypeEnum::UNION :
- if (!in_array($trade_type, ['app', 'html'])) {
- throw new Exception('银联交易类型不符');
- }
- break;
- // 汇聚支付
- // case PayTypeEnum::JOINPAY :
- // if (!in_array($trade_type, JoinPayTypeEnum::getKeys())) {
- // throw new Exception('汇聚交易类型不符');
- // }
- break;
- }
- return true;
- }
- /**
- * @throws Exception
- */
- public function transfer($paymentTransfer): bool
- {
- $where = [
- 'order_no' => $paymentTransfer['orderNo'],
- 'user_id' => $paymentTransfer['user_id'],
- 'mall_id' => $paymentTransfer['mall_id']
- ];
- $model = PaymentTransfer::getOne([$where]);
- if (!$model) {
- $model = new PaymentTransfer();
- $model->transfer_order_no = $paymentTransfer->orderNo;
- $model->mall_id = $paymentTransfer->cashInfo->mall_id;
- $model->user_id = $paymentTransfer->cashInfo->user_id;
- $model->title = $paymentTransfer->title;
- $model->amount = $paymentTransfer->amount;
- $model->pay_type = $paymentTransfer->transferType;
- $model->is_pay = 0;
- $model->withdrawal_source = $paymentTransfer->withdrawal_source;
- $model->withdrawal_source_id = $paymentTransfer->withdrawal_source_id;
- $model->order_no = 'HM' . substr(md5($paymentTransfer->orderNo), 2,29);
- $model->created_at = time();
- $model->cashInfo = $paymentTransfer->cashInfo;
- }
- if ($model->is_pay == 1) {
- throw new Exception('该订单号已经打款,请勿重复操作');
- }
- $t = \Yii::$app->db->beginTransaction();
- try {
- if($model->pay_type == PayTypeEnum::WECHAT){
- $config = Yii::$app->services->pay->getConfig($model->mall_id,'wechat');
- //判断微信开通的功能:企业打款到零钱(老版本),商家转账到零钱(v3 版本)
- if(isset($config['wechat_transfer']) && $config['wechat_transfer'] == WechatPayTypeEnum::WECHAT_TRANSFER_V3){
- // $result = Yii::$app->payment->wechat($config)->v3_transfer_batches($model, $paymentTransfer->user);
- //$result = Yii::$app->payment->wechat($config)->v3_transfer_bills($model, $paymentTransfer->user);
- // 如果没有选择版本,或者版本选择1,那就用旧版本
- if(!isset($config['wechat_transfer_version']) || $config['wechat_transfer_version'] == 1){
- $result = Yii::$app->payment->wechat($config)->v3_transfer_batches($model, $paymentTransfer->user);
- }else{ // 新版本
- $result = Yii::$app->payment->wechat($config)->v3_transfer_bills($model, $paymentTransfer->user);
- }
- \Yii::error('---+++' . json_encode($result));
- }else{
- $result = Yii::$app->payment->wechat($config)->transfer($model, $paymentTransfer->user);
- }
- }else{
- $class = $this->transferClass($model->pay_type);
- $result = $class->transfer($model, $paymentTransfer->user);
- }
- if ($result) {
- $t->commit();
- return true;
- } else {
- throw new Exception();
- }
- } catch (Exception $e) {
- $t->rollBack();
- Yii::error($e->getMessage());
- throw new Exception($e->getMessage());
- return false;
- }
- }
- /**
- * @throws Exception
- */
- private function transferClass($type)
- {
- switch ($type) {
- case PayTypeEnum::WECHAT:
- $class = 'common\\components\\payment\\WechatPay';
- break;
- case PayTypeEnum::ALI:
- $class = 'common\\components\\payment\\AliPay';
- break;
- case PayTypeEnum::JOINPAY;
- //汇聚代付:
- $class = 'common\\components\\payment\\AlternativePay';
- break;
- default:
- throw new Exception('无效的方式');
- }
- if (!class_exists($class)) {
- throw new Exception('未安装相关平台的插件或未知的客户端平台,平台标识`');
- }
- return new $class();
- }
- /**
- * 退款回调
- * @param PaymentRefund $payment_refund
- * @return bool
- */
- public function refundNotify($payment_refund)
- {
- if (!$payment_refund || $payment_refund->is_pay){
- return true;
- }
- //修改状态
- $payment_refund->is_pay = 1;
- if($payment_refund->save()){
- $trade_order = PaymentTradeOrder::findOne(['order_no' => $payment_refund->trade_order_no,'is_pay'=>1]);
- if($trade_order){
- $trade_order->refund_fee = bcadd($trade_order->refund_fee,$payment_refund->amount,2);
- $trade_order->save();
- }
- }
- return true;
- }
- /**
- * 获取系统已开启支付方式
- * @param $mall_id
- * @return array
- */
- public function getEnablePaymentType($mall_id=0)
- {
- if ($mall_id) {
- $config = Yii::$app->services->setting->mall->get($mall_id, 'pay', true);
- } else {
- //系统配置
- $config = Yii::$app->services->setting->platform->get('pay', true);
- }
- $enable_pay_type = [];
- foreach ($config as $key => $value) {
- $payment_mode = json_decode($value, true);
- if (!$payment_mode['status']) {
- continue;
- }
- $enable_pay_type[] = $key;
- }
- $all_payment = PayTypeEnum::allTypeConf($mall_id);
- $enable_payment = [];
- foreach($enable_pay_type as $pay_type)
- {
- $payment_info = $all_payment[$pay_type] ?? [];
- if(empty($payment_info)) continue;
- $enable_payment[] = [
- "name" => $payment_info['name'],
- "pay_type" => $payment_info['pay_type'],
- ];
- }
- $params['mall_id']=$mall_id;
- $return = GlobalInvoke::exec(GlobalInvoke::INVOKE_GET_PAYMENT_TYPE,$mall_id,$params);
- if(!empty($return)) {
- if (!empty($return)) {
- $enable_payment = array_merge($enable_payment,$return);
- }
- }
- return $enable_payment;
- }
- /**
- * 创建订单流水
- * @param int $mall_id
- * @param int $user_id
- * @param array $orders
- * @throws
- * @return int
- */
- public function createGroupTrade($mall_id, $user_id, $orders, $parent_trade_id,$pay_type = 0)
- {
- $trans = Yii::$app->db->beginTransaction();
- try {
- if (empty($orders) || !is_array($orders)) {
- throw new Exception('订单信息有误');
- }
- $exist = PaymentTrade::findOne($parent_trade_id);
- if(empty($exist)){
- throw new Exception('支付数据有误');
- }
- if($exist->is_pay == 1){
- throw new Exception('订单已支付');
- }
- $exist->is_pay_group = 1;
- $exist->pay_type = $pay_type;
- if(!$exist->save()){
- throw new Exception($exist->getErrorMessage());
- }
- $order_amount = 0;
- $title = '';
- foreach ($orders as $order_info) {
- $order_amount = bcadd($order_amount, $order_info['amount'], 2);
- $title .= str_replace(';', '', $order_info['title']) . ';';
- $is_rand_fee = isset($order_info['order_type']) ? true : false;
- }
- // 全局调用
- $models = [];
- if ($mall_id && $is_rand_fee) {
- $res = GlobalInvoke::exec(GlobalInvoke::RAND_FEE, $mall_id, compact('orders', 'mall_id', 'user_id'));
- if (!empty($res)) list($rand_fee, $models, $rand_rule) = $res;
- }
- $rand_fee = !empty($rand_fee) ? $rand_fee : 0;
- $trade = [
- 'mall_id' => $mall_id,
- 'user_id' => $user_id,
- 'out_trade_no' => PaymentTrade::createOutTradeNo(),
- 'pay_fee' => bcsub($order_amount, $rand_fee, 2),
- 'total_fee' => $order_amount,
- 'rand_fee' => $rand_fee,
- 'title' => mb_convert_encoding(mb_substr($title, 0, 32),'UTF-8'),
- //'notify_url' => '',
- 'return_url' => $orders[0]['return_url'] ?? '',
- 'is_pay_group' => 1,
- 'parent_trade_id' => $parent_trade_id,
- ];
- //正则过滤除了汉字字母数字的其它字符
- $trade['title'] = preg_replace('#[^\x{4e00}-\x{9fa5}A-Za-z0-9]#u','',$trade['title']);
- $paymentTrade = new PaymentTrade();
- $paymentTrade->attributes = $trade;
- if (!$paymentTrade->save()) {
- throw new Exception($paymentTrade->getErrorMessage());
- }
- $trade_id = $paymentTrade->id;
- $rand_fee_arr = [];
- $total_amount = array_sum(array_column($orders,'amount'));
- $temp_rand_fee = 0;
- $order_type = '';
- foreach ($orders as $k=>$order_info) {
- $order_rand_fee = 0;
- if($rand_fee){
- if(!isset($orders[$k+1])){
- $order_rand_fee = bcsub($rand_fee,$temp_rand_fee,2);
- }else{
- $rate = bcdiv($order_info['amount'],$total_amount,2);
- $order_rand_fee = bcmul($rate,$rand_fee,2);
- $temp_rand_fee = bcadd($order_rand_fee,$temp_rand_fee,2);
- }
- $rand_fee_arr[$order_info['order_no']] = $order_rand_fee;
- $order_type = $order_info['order_type'];
- }
- $model = new PaymentTradeGroupOrder();
- $model->trade_id = $trade_id;
- $model->order_no = $order_info['order_no'];
- $model->order_type = $order_info['order_type']; //订单类型
- $model->pay_fee = $paymentTrade->pay_fee;
- $model->total_fee = $order_info['amount'];
- $model->is_pay = 0;
- $model->title = $order_info['title'];
- $model->notify_class = $order_info['notify_class'] ?? '';
- $model->rand_fee = $order_rand_fee;
- if (!$model->save()) {
- throw new Exception($model->getErrorMessage());
- }
- }
- if(!empty($rand_fee_arr)){
- if($order_type=='order'){
- Order::updateOrderPayMoney($rand_fee_arr);
- }
- }
- if (!empty($models)) {
- foreach ($models as $model) {
- $model->trade_id = $trade_id;
- $model->save();
- }
- }
- $trans->commit();
- return array_merge([
- 'trade_id' => $trade_id,
- 'trade_no' => $paymentTrade->out_trade_no,
- ], isset($rand_rule) ? compact('rand_rule') : [] ); // 如果定义随机减规则 合并
- } catch (Exception $e) {
- $trans->rollBack();
- Yii::error($e->getMessage());
- throw new \Exception($e->getMessage());
- }
- }
- /**
- * 退款-不需要传$order
- * @Author:hua
- * @Date:2024-03-08 10:40
- * @Copyright:copyright(c)2024 广东七件事集团
- * @param $refund_fee
- * @param $out_trade_no
- * @param $order_no
- * @param $source_table
- * @param $source_table_id
- * @param string $desc
- * @param string $from_type
- * @param string $capital_type
- * @return bool
- */
- public function commonRefund($refund_fee,$out_trade_no,$order_no,$source_table,$source_table_id, $desc='',$from_type='',$capital_type='')
- {
- try {
- $payment_trade = PaymentTrade::findOne(['out_trade_no' => $out_trade_no]);
- $user_id = $payment_trade->user_id;
- $mall_id = $payment_trade->mall_id;
- $total_fee = $payment_trade->pay_fee;
- $payment_refund = new PaymentRefund();
- $payment_refund->user_id = $user_id;
- $payment_refund->mall_id = $mall_id;
- $payment_refund->order_no = StringHelper::generateOrderNo('tr');
- $payment_refund->out_trade_no = $out_trade_no;
- $payment_refund->amount = $refund_fee;
- $payment_refund->pay_type =$payment_trade->pay_type;
- $payment_refund->trade_order_no = $order_no;
- $payment_refund->save();
- switch ($payment_refund->pay_type){
- case PayTypeEnum::BALANCE:
- $res = Yii::$app->payment->balance->refund($payment_refund,$source_table,$source_table_id,$desc,$from_type,$capital_type);
- if($res==false) throw new \Exception('余额退款失败');
- break;
- case PayTypeEnum::SCORE:
- $res = Yii::$app->payment->score->refund($payment_refund,$order_no,$source_table,$source_table_id,$desc,$from_type,$capital_type);
- if($res==false) throw new \Exception('积分支付退款失败');
- break;
- case PayTypeEnum::WECHAT:
- $wechat_refund = [
- 'out_trade_no' => $out_trade_no,
- 'transaction_id' => '',
- 'out_refund_no'=>$payment_refund->order_no,
- 'total_fee' => bcmul($total_fee ,100,0),
- 'refund_fee' => bcmul($refund_fee,100,0),
- ];
- $config = $this->getWechatConfig($payment_trade->mall_id);
- //微信支付配置获取
- $res = Yii::$app->payment->wechat($config)->refund($wechat_refund);
- if ($res['return_code'] != 'SUCCESS' || $res['result_code'] != 'SUCCESS') {
- $exception = $res['err_code_des'] ?? $res['return_msg'];
- throw new Exception($exception);
- }
- $this->refundNotify($payment_refund);
- break;
- case PayTypeEnum::JOINPAY:
- $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY);
- $config = $this->getConfig($mall_id, $pay_type);
- $refund_class = Yii::$app->payment->joinpay($config);
- $res = $refund_class->refund(['out_refund_no' => $out_trade_no,'refund_order_no'=>$payment_refund->order_no,'refund_amount' => $refund_fee,'refund_reason' => $desc]);
- if($res==false) throw new \Exception($refund_class->getError());
- break;
- case PayTypeEnum::JOINPAY_FAST_PAY:
- $pay_type = \addons\JoinPay\common\enums\JoinPayTypeEnum::action(\addons\JoinPay\common\enums\JoinPayTypeEnum::JOINPAY_FAST_PAY);
- $config = $this->getConfig($mall_id, $pay_type);
- $refund_class = Yii::$app->payment->joinpayFastPay($config);
- $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]);
- if($res==false) throw new \Exception($refund_class->getError());
- break;
- case PayTypeEnum::ALI:
- switch ($payment_trade->trade_type) {
- case AliPay::JS:
- $config = $this->getAlipayConfig($payment_trade);
- break;
- default:
- $config = \Yii::$app->services->pay->getConfig($mall_id,PayTypeEnum::action(PayTypeEnum::ALI));
- }
- $refund_class = Yii::$app->payment->alipay($config);
- $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]);
- if($res['is_success'] && !empty($res['fund_change'])){
- $this->refundNotify($payment_refund);
- }
- if($res == false) throw new \Exception('支付宝退款失败');
- break;
- case PayTypeEnum::OFFLINE:
- break;
- default:
- $res = GlobalInvoke::exec(GlobalInvoke::INVOKE_ORDER_REFUND, $mall_id, [
- 'mall_id' => $mall_id,
- 'order_no' => $order_no,
- 'refund_order_no' => $order_no,
- 'payment_trade_id' => $payment_trade->id,
- 'refund_price' => $refund_fee,
- 'pay_type' => $payment_refund->pay_type,
- ]);
- list($boolean, $msg) = $res;
- if ($boolean === false) {
- throw new \Exception($msg);
- }
- }
- return true;
- } catch (Exception $e) {
- throw new \Exception($e->getMessage().$e->getFile().$e->getLine());
- }
- }
- /**
- * 订单支付验证
- * @param $trade_id
- * @return bool
- */
- public function getPayVerify($trade_id): bool
- {
- try {
- $paymentTradeInfo = PaymentTrade::findOne(['id' => $trade_id]);
- if (empty($paymentTradeInfo)) {
- Yii::$app->custom->logs('支付订单不存在:', $trade_id);
- return false;
- }
- if ($paymentTradeInfo->is_pay == PayTypeEnum::ON_LINE) {
- return false;
- }
- } catch (Exception $e) {
- return false;
- }
- return true;
- }
- }
|