app = $app; } /** * 统一下单API */ public function unifiedorder($order_no, $openid, $totalFee, $orderType = OrderTypeEnum::MASTER, $pay_source, $multiple) { //判断是否使用付呗插件,兼容其他商户的付呗支付 by yj 2023.12.16 if(!empty($this->app['config']["is_use_plus"])){ $sub_appid = $this->app['config']["cashier_app_id"]; }else{ $sub_appid = $this->app['config']["app_id"]; } $requestConfigs = array( 'merchant_id' => $this->app['config']["merchant_id"], 'store_id' => $this->app['config']["store_id"], //'sub_appid' => $this->app['config']["app_id"], 'sub_appid'=> $sub_appid, 'merchant_order_sn' => $order_no, 'total_amount' => $totalFee, 'user_id' => $openid, 'pay_type' => "wxpay", 'body' =>$order_no, 'attach' => ['order_type' => $orderType, 'pay_source' => $pay_source, 'multiple' => $multiple], 'notify_url' => base_url() . 'index.php/job/notify/fbpay', ); //使用插件才能传该字段 by yj 2023.12.22 if(!empty($this->app['config']["is_use_plus"])){ $requestConfigs["pay_scene"] = "OPENAPI_PAYMENT_APPLET_PLUGIN"; $requestConfigs["timeout_express"] = date('YmdHis',time() + 60 * 15); } $commonConfigs = array( //公共参数 'method' => 'fbpay.order.create', 'format' => 'json', 'sign_method' => 'md5', 'nonce' => Str::random(32), 'version' => '2.0', 'biz_content' => json_encode($requestConfigs), ); if($this->app['config']["vendor_sn"]) { $commonConfigs['vendor_sn'] = $this->app['config']["vendor_sn"]; } else { $commonConfigs['app_id'] = $this->app['config']["fbapp_id"]; } $commonConfigs["sign"] = $this->makeSign($commonConfigs); $result = $this->curlPostContents($this->gateway,$commonConfigs); $result = json_decode($result); if($result->result_code != 200){ throw new BaseException(['msg' => '付呗支付api: ' . $result->result_message]); } $sign_package = (array)($result->data->sign_package); // log_write($result); //如果是微信小程序 $time = time(); if($pay_source == 'wx') { // 二次签名的参数必须与下面相同 $params = [ 'appId' => $sign_package['appId'],//有所修改 'timeStamp' => $time, 'nonceStr' => $sign_package['nonceStr'], 'package' => $sign_package['package'], 'signType' => 'MD5', ]; //$sign_package['paySign'] = $this->makeSign($params); //$sign_package['paySign'] = $sign_package['paySign']; $sign_package['prepay_id'] = str_replace('prepay_id=', '', $sign_package['package']); $result_data =[ 'prepay_id' => $sign_package['prepay_id'], 'nonceStr' => $sign_package['nonceStr'], 'timeStamp' => $sign_package['timeStamp'], 'paySign' => $sign_package['paySign'], 'signType' => $sign_package['signType'], // 'orderSn'=>$result->data->order_sn,// 付呗新增参数 by yj 2023.12.16 'apiType'=>1,//返回付呗支付标识 0微信原生 1付呗支付 by yj 2023.12.16 ]; if(!empty($this->app['config']["is_use_plus"])){ $result_data["orderSn"] = $result->data->order_sn; } return $result_data; } return $sign_package; } /** * 支付成功异步通知 */ public function notify() { log_write('付呗回调'); $data = json_decode($_POST['data'], true); $attach = json_decode($data['attach'], true); // 实例化订单模型 $PaySuccess = PayTypeSuccessFactory::getFactory($data['merchant_order_sn'], $attach); $app_id = $PaySuccess->isExist(); $app_id == 0 && $this->returnCode(false, '订单不存在1'); $shop_supplier_id = $PaySuccess->isExistSupplier(); // $shop_supplier_id == 0 && $this->returnCode(false, '订单商户不存在2'); // 暂时屏蔽 by lyzflash 20251029 // 支付配置信息 $this->app = $this->getFbPayApp($app_id,$shop_supplier_id); // 保存微信服务器返回的签名sign $dataSign = $_POST['sign']; // sign不参与签名算法 unset($_POST['sign']); // 生成签名 $sign = $this->makeSign($_POST); // 判断签名是否正确 判断支付状态 if ( ($sign !== $dataSign) || ($data['order_status'] !== 'SUCCESS') ) { $this->returnCode(false, '签名失败'); } // 订单支付成功业务处理 $data['transaction_id'] = $data['channel_order_sn']; $status = $PaySuccess->onPaySuccess(OrderPayTypeEnum::WECHAT, $data); if ($status == false) { echo 'error'; exit(); } // 返回状态 echo 'success'; exit(); } /** * 申请退款API */ public function refund($transaction_id, $order_no, $refund_fee) { //请求参数 $requestConfigs = array( 'merchant_order_sn' => $order_no, 'merchant_refund_sn' => $transaction_id, 'refund_amount' => $refund_fee, ); if($this->app['config']["vendor_sn"]) { $requestConfigs['merchant_id'] = $this->app['config']["merchant_id"]; } $commonConfigs = array( //公共参数 'method' => 'fbpay.order.refund', 'format' => 'json', 'sign_method' => 'md5', 'nonce' => Str::random(32), 'version' => '2.0', 'biz_content' => json_encode($requestConfigs), ); if($this->app['config']["vendor_sn"]) { $commonConfigs['vendor_sn'] = $this->app['config']["vendor_sn"]; } else { $commonConfigs['app_id'] = $this->app['config']["fbapp_id"]; } $commonConfigs["sign"] = $this->makeSign($commonConfigs); $result = $this->curlPostContents($this->gateway,$commonConfigs); $result = json_decode($result); if($result->result_code && $result->result_code == 200){ return true; }else{ throw new BaseException(['msg' => 'return_msg: ' . $result->result_message]); } } /** * 企业付款到零钱API */ public function transfers($order_no, $openid, $amount, $desc) { $result = $this->app->transfer->toBalance([ 'partner_trade_no' => $order_no, // 商户订单号,需保持唯一性(只能是字母或者数字,不能包含有符号) 'openid' => $openid, 'check_name' => 'NO_CHECK', // NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名 'amount' => $amount * 100, // 企业付款金额,单位为分 'desc' => $desc, // 企业付款操作说明信息。必填 ]); // 请求失败 if (empty($result)) { throw new BaseException(['msg' => '微信提现到零钱api请求失败']); } // 请求失败 if ($result['return_code'] === 'FAIL') { throw new BaseException(['msg' => 'return_msg: ' . $result['return_msg']]); } if ($result['result_code'] === 'FAIL') { throw new BaseException(['msg' => 'err_code_des: ' . $result['err_code_des']]); } return true; } /** * 上传加密图片 */ public function imgupload($file_data, $bus_type='bankCard') { //请求参数 $requestConfigs = array( 'bus_type' => $bus_type, 'file_data' => $file_data, ); $commonConfigs = array( //公共参数 'method' => 'openapi.agent.base.imgupload.security', 'format' => 'json', 'sign_method' => 'md5', 'nonce' => Str::random(32), 'version' => '2.0', 'biz_content' => json_encode($requestConfigs), ); if($this->app['config']["vendor_sn"]) { $commonConfigs['vendor_sn'] = $this->app['config']["vendor_sn"]; } else { $commonConfigs['app_id'] = $this->app['config']["fbapp_id"]; } $commonConfigs["sign"] = $this->makeSign($commonConfigs); $result = $this->curlPostContents($this->gateway,$commonConfigs); $result = json_decode($result); if($result->result_code && $result->result_code == 200){ $resource_id = (array)($result->data->resource_id); return $resource_id; }else{ throw new BaseException(['msg' => 'return_msg: ' . $result->result_message]); } } /** * 分账接收方入驻 */ public function subaccount($data) { //请求参数 $requestConfigs = $data; $commonConfigs = array( //公共参数 'method' => 'openapi.agent.account.subaccount.income', 'format' => 'json', 'sign_method' => 'md5', 'nonce' => Str::random(32), 'version' => '2.0', 'biz_content' => json_encode($requestConfigs), ); if($this->app['config']["vendor_sn"]) { $commonConfigs['vendor_sn'] = $this->app['config']["vendor_sn"]; } else { $commonConfigs['app_id'] = $this->app['config']["fbapp_id"]; } $commonConfigs["sign"] = $this->makeSign($commonConfigs); $result = $this->curlPostContents($this->gateway,$commonConfigs); $result = json_decode($result); // print_r($result);exit; if($result->result_code && $result->result_code == 200){ $resutl_data = (array)($result->data); return $resutl_data["account_id"]; }else{ throw new BaseException(['msg' => '请把信息填写完整再提交: ' . $result->result_message]); } } /** * 分账接收方入驻回调 */ public function subaccountResult() { log_write('入驻回调'); $data = json_decode($_POST['data'], true); $where["merchant_order_sn"]=$data["merchant_order_sn"]; $model = UserAuthModel::detailById($where); if(empty($model)){ $this->returnCode(false, '数据不存在'); } // 数据处理 $save_data['account_id'] = $data['account_id']; $save_data['auth_status'] = $data['status']; $save_data['fail_message'] = $data['fail_message']; $status = $model->save($save_data); if ($status == false) { echo 'error'; exit(); } // 返回状态 echo 'success'; exit(); } /** * 分账提现API */ public function withdraw($data) { //请求参数 $requestConfigs = array( 'merchant_order_sn' => $data["merchant_order_sn"], 'account_id' => $data["account_id"], 'amount' => $data["amount"], 'type' => 1,//提现方式 1慢 2快(每日十次) 'notify_url' => base_url() . 'index.php/job/notify/withdraw_result' ); if($this->app['config']["vendor_sn"]) { $requestConfigs['merchant_id'] = $this->app['config']["merchant_id"]; } $commonConfigs = array( //公共参数 'method' => 'openapi.agent.account.withdraw', 'format' => 'json', 'sign_method' => 'md5', 'nonce' => Str::random(32), 'version' => '2.0', 'biz_content' => json_encode($requestConfigs), ); if($this->app['config']["vendor_sn"]) { $commonConfigs['vendor_sn'] = $this->app['config']["vendor_sn"]; } else { $commonConfigs['app_id'] = $this->app['config']["fbapp_id"]; } $commonConfigs["sign"] = $this->makeSign($commonConfigs); $result = $this->curlPostContents($this->gateway,$commonConfigs); $result = json_decode($result); if($result->result_code && $result->result_code == 200){ $resutl_data = (array)($result->data); $save_data = [ "bank_name"=>$resutl_data["bank_name"],//银行名称 "bank_card_no"=>$resutl_data["bank_card_no"],//银行卡号 "create_time"=>$resutl_data["create_time"],//申请付呗打款的时间 "finish_time"=>$resutl_data["finish_time"],//预计到账的时间 "status"=>$resutl_data["status"],//提现中 "amount"=>$resutl_data["amount"],//提现金额 ]; FbCashApply::update($save_data, ['merchant_order_sn' => $resutl_data["merchant_order_sn"]]); return true; }else{ throw new BaseException(['msg' => 'return_msg: ' . $result->result_message]); } } /** * 分账结果回调 */ public function withdrawResult() { log_write('分账结果回调'); $data = json_decode($_POST['data_list'], true); $status = FbCashApplyService::applySuccess($_POST['merchant_order_sn'],$data); if ($status == false) { echo 'error'; exit(); } // 返回状态 echo 'success'; exit(); } /** * 微信参数配置 */ public function wxconfig() { //请求参数 $requestConfigs = array( 'store_id' => $this->app['config']["store_id"], 'merchant_id' => $this->app['config']["merchant_id"], 'sub_appid' => $this->app['config']['is_embed'] ? $this->app['config']["embed_app_id"] : $this->app['config']["app_id"], 'account_type' => '01', 'jsapi_path' => base_url() . 'h5/pages/order/', ); $commonConfigs = array( //公共参数 'method' => 'fbpay.order.wxconfig', 'format' => 'json', 'sign_method' => 'md5', 'nonce' => Str::random(32), 'version' => '2.0', 'biz_content' => json_encode($requestConfigs), ); if($this->app['config']["vendor_sn"]) { $commonConfigs['vendor_sn'] = $this->app['config']["vendor_sn"]; } else { $commonConfigs['app_id'] = $this->app['config']["fbapp_id"]; } $commonConfigs["sign"] = $this->makeSign($commonConfigs); $result = $this->curlPostContents($this->gateway,$commonConfigs); $result = json_decode($result); /* if($result->result_code && $result->result_code == 200){ return true; }else{ throw new BaseException(['msg' => '付呗微信配置返回: ' . $result->result_message]); }*/ if($result->result_code && $result->result_code == 200 && ($result->data->sub_appid_code == 1 || $result->data->jsapi_code == 1)){ return true; }else{ if ($result->data->sub_appid_code != 1) { throw new BaseException(['msg' => '付呗微信配置返回: ' . $result->data->sub_appid_msg]); return false; } if ($result->data->jsapi_code != 1) { throw new BaseException(['msg' => '付呗微信配置返回: ' . $result->data->jsapi_msg]); return false; } throw new BaseException(['msg' => '付呗微信配置返回: ' . $result->result_message]); } } /** * 返回状态给微信服务器 */ private function returnCode($returnCode = true, $msg = null) { // 返回状态 $return = [ 'return_code' => $returnCode ? 'SUCCESS' : 'FAIL', 'return_msg' => $msg ?: 'OK', ]; // 记录日志 log_write([ 'describe' => '返回微信支付状态', 'data' => $return ]); die($this->toXml($return)); } /** * 输出xml字符 * @param $values * @return bool|string */ private function toXml($values) { if (!is_array($values) || count($values) <= 0 ) { return false; } $xml = ""; foreach ($values as $key => $val) { if (is_numeric($val)) { $xml .= "<" . $key . ">" . $val . ""; } else { $xml .= "<" . $key . ">"; } } $xml .= ""; return $xml; } /** * 生成签名 */ private function makeSign($values) { //签名步骤一:按字典序排序参数 ksort($values); $string = $this->toUrlParams($values); //签名步骤二:在string后加入KEY $string = trim($string, '&') . $this->app['config']['secret']; //签名步骤三:MD5加密 $string = md5($string); //签名步骤四:所有字符转为大写 $result = strtoupper($string); return $result; } /** * 格式化参数格式化成url参数 */ private function toUrlParams($values) { $buff = ''; foreach ($values as $k => $v) { if ($k != 'sign' && $v != '' && !is_array($v)) { $buff .= $k . '=' . $v . '&'; } } return trim($buff, '&'); } public static function getFbPayApp($app_id,$shop_supplier_id =0){ // 获取当前小程序信息 $wxConfig = AppWxModel::getAppWxCache($app_id); //获取后台是否设置了商户独立收款 by yj 2024.3.12 //$is_independent = SettingModel::getIndependentOpen(); $is_independent = SupplierModel::getIndependentOpen($shop_supplier_id); if(!empty($is_independent)){ //分每个商户有单独的付呗支付信息 $app = AppPayModel::detail($shop_supplier_id); }else{ $app = AppModel::detail($app_id); } // 验证fbpay_vendor_sn或fbpay_app_id是否填写,不能全为空 if (empty($app['fbpay_vendor_sn']) && empty($app['fbpay_app_id'])) { throw new BaseException(['msg' => '请到 [后台-应用-小程序设置] 服务商开放平台vendor-sn和商户开放平台app_id不能全为空']); } if (empty($app['fbpay_secret']) || empty($app['fbpay_merchant_id']) || empty($app['fbpay_store_id'])) { throw new BaseException(['msg' => '请到 [后台-应用-小程序设置] 填写相关参数']); } $config = [ 'app_id' => $wxConfig['wxapp_id'], 'vendor_sn' => $app['fbpay_vendor_sn'], 'secret' => $app['fbpay_secret'], 'merchant_id' => $app['fbpay_merchant_id'], 'store_id' => $app['fbpay_store_id'], 'fbapp_id' => $app['fbpay_app_id'], 'is_use_plus' => $app['is_use_plus'],// 用于判断是否用付呗收银台插件 'cashier_app_id'=> $app['fbpay_cashier_app_id'],// 付呗收银台插件使用 'is_embed' => $app['is_embed'], // 是否开启半屏小程序支付 'embed_app_id'=> $app['embed_app_id'], // 半屏小程序的appId 'embed_key' => $app['embed_key'], // 半屏小程序安全密钥 'embed_path' => $app['embed_path'], // 半屏小程序支付路径 ]; return Factory::payment($config); } /** * 提交提交结果 * @param $url 网关地址 * @param array $data 请求参数 * @param int $timeout 超时时间 * @return bool|string * @throws Exception */ public static function curlPostContents($url, $data = array(), $timeout=10){ $data_string = json_encode($data); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$url); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_POSTFIELDS,$data_string); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); curl_setopt($ch, CURLOPT_TIMEOUT,$timeout); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json', 'Content-Length: ' . strlen($data_string)) ); $result = curl_exec($ch); if ($no = curl_errno($ch)) { $error = curl_error($ch); curl_close($ch); if(in_array(intval($no), [7, 28], true)) { throw new BaseException(['msg' => '连接或请求超时' . $error]); } } curl_close($ch); return $result; } }