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; } }