mall_id = $mall_id; } } // 下单 /** * 作者:zengfanhua * 创建时间:2023/12/28 15:10:04 * @trade Object */ public function create(PaymentTrade $trade, $trade_type, $openid,$smsCode, $extra_config = []) { $trade_type = trim($trade_type); $config = \Yii::$app->services->setting->addons->get($trade->mall_id, AddonsEnum::ADDONS_NAME_HUIFU_PAY, 'basic'); if (!empty($extra_config)) { foreach ($extra_config as $config_key => $config_value) { isset($config[$config_key]) && $config[$config_key] = $config_value; } } new WechatForm(['mall_id' => $trade->mall_id]); $order_setting = \Yii::$app->services->setting->mall->get($trade->mall_id, 'order'); $order_auto_handle_time = 30; if (!empty($order_setting)) { $order_auto_handle_time = $order_setting['payment_expire_time']; } $expireDate = time() + ($order_auto_handle_time * 60); $huifuService = new HuifuService($trade->mall_id); $notify_url = \Yii::$app->services->setting->platform->get('system.api_url') . "/huifu-pay/notify/notify"; $result = []; //分账对象 $splitAccount = []; $total_price=0; if ($config['huifupay_ledger_status'] && !empty(json_decode($config['huifupay_ledger_list'], true))) { $sList = json_decode($config['huifupay_ledger_list'], true); foreach ($sList as $key => $value) { $scale = bcdiv($value['num'], 100,4); $price = bcmul($trade->getOnlinePayFee(), $scale, 2); $item = [ 'num' => $price, 'id' => $value['id'] ]; $splitAccount[] = $item; $total_price=bcadd($price,$total_price,2); } //精度损失处理 if($total_price<$trade->getOnlinePayFee()) { $no_price = bcsub($trade->getOnlinePayFee(),$total_price,2); $base_price=$splitAccount[count($splitAccount)-1]['num']; $splitAccount[count($splitAccount)-1]['num']=bcadd($base_price,$no_price,2); } } // 判断是否需要延迟分账 delay_acct_flag $t_data['delay_acct_flag'] = self::getSplitAccount($trade); // 是否延迟分账 // 判断是否为门店订单,如果是门店订单,那么就判断是否需要延迟分账 // try { switch ($trade_type) { case 'app_alipay': case 'h5_alipay': $data = [ 'goods_title' => $trade->title, 'amount' => $trade->getOnlinePayFee(), 'huifu_id' => $config['huifupay_huifu_id'], 'delay_acct_flag'=>$t_data['delay_acct_flag'], ]; $result = $huifuService->preAliOrder($data, $notify_url, $expireDate, $splitAccount); break; case 'app_wechat': case 'h5_wechat': case 'wechat': $data = [ 'goods_title' => $trade->title, 'amount' => $trade->getOnlinePayFee(), 'huifu_id' => $config['huifupay_huifu_id'], 'delay_acct_flag'=>$t_data['delay_acct_flag'], ]; $resultPre = $huifuService->preWxOrder($data, $notify_url, $expireDate, $splitAccount); //除了小程序,获取openid,生成一个中间页打开微信小程序 $out_trade_no = $trade->out_trade_no; $data = [ 'path' => '/plugins4/HuifuPay/call-pay', 'query' => 'trade_sn=' . $out_trade_no . '&pre_order_id=' . $resultPre['pre_order_id'], 'env_version' => 'trial', ]; $wechatService = new WechatService(); $res = $wechatService->getScheme($data); if (!empty($res['errcode'])) { throw new Exception($res['errmsg']); } $result = ['openlink' => $res['openlink'], 'req_seq_id' => $resultPre['req_seq_id']]; break; case 'mp_wechat': //$mp_wechat_appid = \Yii::$app->params['wechatMiniProgramConfig']['app_id']; $data = [ 'goods_title' => $trade->title, 'amount' => $trade->getOnlinePayFee(), 'huifu_id' => $config['huifupay_huifu_id'], 'delay_acct_flag'=>$t_data['delay_acct_flag'], ]; //$result = $huifuService->preAliOrder($data, $notify_url, $expireDate,$splitAccount); $result = $huifuService->preWxOrder($data, $notify_url, $expireDate, $splitAccount); break; case 'big_pay': $result = ['trade_no' => $trade->out_trade_no]; return ['result' => $result]; case 'balance_pay': $data = [ 'goods_title' => $trade->title, 'amount' => $trade->getOnlinePayFee(), 'huifu_id' => $config['huifupay_huifu_id'], 'delay_acct_flag'=>$t_data['delay_acct_flag'], ]; if(empty($splitAccount)) { throw new Exception('余额支付必须分账,请在后台设置'); } $result = $huifuService->balancePay($data, $splitAccount); if ($result['trans_stat'] == 'S') { //修改交易状态 $trade->pay_type = PayTypeEnum::HUIFUPAY; $trade->is_pay = 1; $trade->pay_time = time(); $trade->third_trade_no=$result['req_seq_id']; if (!$trade->save()) { throw new Exception('支付流水修改失败'); } $orders = Yii::$app->services->pay->changePaymentTradeOrder($trade, PayTypeEnum::HUIFUPAY, 'balance_pay'); Yii::$app->services->pay->notify($orders); return ['result' => ['trade_no' => $trade->out_trade_no]]; } return ['result' => 'fail']; case 'fast_pay': if(empty($smsCode)) { throw new Exception('请输入短信验证码'); } $data = [ 'goods_title' => $trade->title, 'sms_code' => $smsCode, 'huifu_id' => $config['huifupay_huifu_id'], 'delay_acct_flag'=>$t_data['delay_acct_flag'], ]; $payOrder = HuifuPayOrderModel::find() ->where(['trade_no' => $trade->out_trade_no]) ->orderBy('id desc') ->one(); $data['order_id']=$payOrder['hf_order_no']; $result = $huifuService->sendPaySmsConfirm($data, $notify_url); return ['amount' => $trade->total_fee, 'order_no' => $trade->out_trade_no]; case 'T_MINIAPP': //查询用户open_id $user_info = UserInfo::findOne(['user_id' => $trade['user_id'], 'status' => StatusEnum::ENABLED, 'platform' => 'mp-wx']); $data = [ 'goods_title' => $trade->title, 'amount' => sprintf('%.2f', $trade->getOnlinePayFee()), 'huifu_id' => $config['huifupay_huifu_id'], 'delay_acct_flag'=>$t_data['delay_acct_flag'], 'trade_type'=>$trade_type, 'mall_id'=>$trade['mall_id'], 'open_id'=>!empty($user_info) ? $user_info['openid'] : '', ]; $result = $huifuService->preWxOrder2($data, $notify_url, $expireDate, $splitAccount); if(!empty($result['resp_code']!='00000100')){ throw new Exception($result['resp_desc']); } break; } // } catch (\Throwable $e) { // dd($e->getMessage()); // } //创建本地关联订单 try { if ($result) { //添加记录 $order = new HuifuPayOrderModel(); $order->mall_id = $trade->mall_id; $order->hf_order_no = $result['req_seq_id']; //汇付流水号 $order->trade_no = $trade->out_trade_no; $order->created_at = time(); $order->updated_at = time(); $order->payment_id = $trade->id; $order->trade_type = $trade_type; $order->save(); $trade->trade_type=$trade_type; $trade->save(); } } catch (\Exception $e) { throw new Exception($e->getMessage()); } switch ($trade_type){ case 'T_MINIAPP': $pay_info = json_decode($result['pay_info'],true); $pay_info['timestamp'] = $pay_info['timeStamp']; return $pay_info; default: return ['result' => $result]; } } /** * 分账 * @param PaymentTrade $trade * @return array|array[]|string */ public static function getSplitAccount(PaymentTrade $trade) { // 判断是否为门店订单 $to = PaymentTradeOrder::findOne(['trade_id' => $trade->id]); if (strpos($to->order_no, 'C') === 0) { $order = StoreCashierOrder::findOne(['order_no' => $to->order_no]); } else { $order = Order::findOne(['order_no' => $to->order_no]); } if (!empty($order->store_id)) { // 判断是否有设置分账 $storeSetting = StoreSettings::findOne(['store_id' => $order->store_id]); if ($storeSetting->store_service_fee == 2 && !empty($storeSetting->huifu_mch_no)) { // 需要分账 // 这里写分账数据 // return [ // [ // 'huifu_id' => $storeSetting->huifu_mch_no, // 'div_amt' => bcmul($trade->getOnlinePayFee(), bcsub(1,bcmul($storeSetting->store_service_ratio, 0.01, 2), 2), 2), // ] // ]; return 'Y'; } } return 'N'; } public function bigPay(PaymentTrade $trade, $cardId) { $huifuService = new HuifuService($trade->mall_id); $config = \Yii::$app->services->setting->addons->get($trade->mall_id, AddonsEnum::ADDONS_NAME_HUIFU_PAY, 'basic'); $data = [ 'goods_title' => $trade->title, 'amount' => $trade->getOnlinePayFee(), 'huifu_id' => $config['huifupay_huifu_id'], ]; //获得银行卡信息 $card = UserBankCard::findOne(['id' => $cardId]); if (!$card) { throw new Exception('找不到银行卡'); } $data['real_name'] = $card->payer_name; $data['bank_card_no'] = $card->bank_card_no; $notify_url = \Yii::$app->services->setting->platform->get('system.api_url') . "/huifu-pay/notify/notify"; $result = $huifuService->bigPay($notify_url, $data); //创建本地关联订单 try { if ($result) { //添加记录 $order = new HuifuPayOrderModel(); $order->mall_id = $trade->mall_id; $order->hf_order_no = $result['req_seq_id']; //汇付流水号 $order->trade_no = $trade->out_trade_no; $order->created_at = time(); $order->updated_at = time(); $order->payment_id = $trade->id; $order->trade_type = 'big_pay'; $order->save(); $trade->trade_type='big_pay'; $trade->save(); } } catch (\Exception $e) { throw new Exception($e->getMessage()); } return $result; } /** * 支付回调 * * @param PaymentTrade $trade * @param array $params 回调参数 * @return boolean */ public function notify(PaymentTrade $trade, array $params) { $lock_key = self::LOCK . $trade['out_trade_no']; $identification = uniqid(); $transaction = Yii::$app->db->beginTransaction(); try { //加锁 $res = LockHelper::lock($lock_key, $identification, 100, 100); if (!$res) throw new \PHPUnit\Util\Exception('无法获取锁'); // 支付完成 if ($trade->is_pay == 1) return true; // 支付金额验证 if (bccomp($params['trans_amt'], $trade->getOnlinePayFee()) !== 0) throw new Exception('支付金额不一致'); // 修改支付流水 $trade->pay_type = $trade->isMixedPay() ? PayTypeEnum::MIXED : PayTypeEnum::HUIFUPAY; $trade->is_pay = 1; $trade->pay_time = time(); $trade->third_trade_no = $params['req_seq_id'] ?? ''; $trade->second_trade_no = $params['out_ord_id'] ?? ''; if (!$trade->save()) throw new Exception('支付流水修改失败'); // 业务回调 $orders = Yii::$app->services->pay->changePaymentTradeOrder($trade, PayTypeEnum::HUIFUPAY); $transaction->commit(); Yii::$app->services->pay->notify($orders); LockHelper::uLock($lock_key, $identification); return true; } catch (\Exception $e) { $transaction->rollBack(); LockHelper::uLock($lock_key, $identification); // 记录报错日志 FileHelper::writeLog($this->getLogPath('error'), json_encode(ArrayHelper::toArray([ 'message' => $e->getMessage(), 'line' => $e->getLine(), 'file' => method_exists($e, 'getFile') ? $e->getFile() : '', 'trace' => $e->getTrace(), 'code' => $e->getCode(), ]))); return false; } } /** * @param $type * @return string */ protected function getLogPath($type) { return Yii::getAlias('@runtime') . "/huifu/" . date('Y_m_d') . '/' . $type . '.txt'; } public function refund($params, $extra_config = []) { try { $mall_id = $this->mall_id; $refund = $params['refund']; $order_id = $refund['order_id']; $refund_source = $params['refund_source'] ?? 'Mall'; $pay_type = $params['pay_type']; if ($pay_type != PayTypeEnum::HUIFUPAY) return false; if ($refund_source === 'Mall') { $order = Order::findOne(['id' => $order_id, 'mall_id' => $mall_id]); if (!empty($params['order_type']) && $params['order_type'] == 'LuckyGroup') { if (MallAddons::isInstall($mall_id, 'LuckyGroup')) { $order = LuckyGroupOrder::findOne(['id' => $order_id, 'mall_id' => $mall_id, 'status' => StatusEnum::ENABLED]); } } if (empty($order)) { throw new Exception('订单不存在'); } } $payment_trade = PaymentTrade::findOne(['id' => $params['payment_trade_id'],'is_pay' => 1]); $third_trade_no = $payment_trade['third_trade_no']; $config = \Yii::$app->services->setting->addons->get($this->mall_id, AddonsEnum::ADDONS_NAME_HUIFU_PAY, 'basic'); if (!empty($extra_config)) { foreach ($extra_config as $config_key => $config_value) { isset($config[$config_key]) && $config[$config_key] = $config_value; } } //分账对象 $splitAccount = []; $total_price=0; if ($config['huifupay_ledger_status'] && !empty(json_decode($config['huifupay_ledger_list'], true))) { $sList = json_decode($config['huifupay_ledger_list'], true); foreach ($sList as $key => $value) { $scale = bcdiv($value['num'], 100,4); $price = bcmul($payment_trade->getOnlinePayFee(), $scale, 2); $item = [ 'num' => $price, 'id' => $value['id'] ]; $splitAccount[] = $item; $total_price=bcadd($price,$total_price,2); } //精度损失处理 if($total_price<$payment_trade->getOnlinePayFee()) { $no_price = bcsub($payment_trade->getOnlinePayFee(),$total_price,2); $base_price=$splitAccount[count($splitAccount)-1]['num']; $splitAccount[count($splitAccount)-1]['num']=bcadd($base_price,$no_price,2); } } if ($payment_trade['trade_type'] == 'big_pay') { throw new Exception('大额支付不支持在线退款'); } $huifuService = new HuifuService($mall_id); $data = [ 'huifu_id' => $config['huifupay_huifu_id'], 'amount' => sprintf('%.2f', $params['refund_price']), 'old_req_date' => date("Ymd", $payment_trade['created_at']), 'org_req_seq_id' => $third_trade_no, ]; if ($payment_trade['trade_type'] == 'balance_pay') { $result=$huifuService->balanceReturn($data,$splitAccount); } else if($payment_trade['trade_type'] == 'big_pay'||$payment_trade['trade_type'] == 'fast_pay') { $result=$huifuService->onlineRefund($data,$splitAccount); } else { $result=$huifuService->refund($data,$splitAccount); } if ($result['trans_stat']=='S'||$result['trans_stat']=='P') { return [true, 'ok']; } return [false, '退款失败']; } catch (\Exception $e) { return [false, $e->getMessage()]; } } }