diff --git a/admin/controllers/receiver/Orders.php b/admin/controllers/receiver/Orders.php index fae18113..8cc2a0ee 100644 --- a/admin/controllers/receiver/Orders.php +++ b/admin/controllers/receiver/Orders.php @@ -301,7 +301,7 @@ class Orders extends HD_Controller{ $update['if_ins'] = $params['if_ins'] ? 1:0; if($params['bx_imgs']){ $imgs = explode(',',$params['bx_imgs']); - $update['bx_imgs'] = json_encode($imgs,JSON_UNESCAPED_UNICODE); + $update['ins_img'] = json_encode($imgs,JSON_UNESCAPED_UNICODE); } if($agent){ $result = $this->order_agents_model->update($update,['id'=>$agent['id']]); diff --git a/api/config/wxpay.php b/api/config/wxpay.php new file mode 100644 index 00000000..e71fa49d --- /dev/null +++ b/api/config/wxpay.php @@ -0,0 +1,19 @@ + 'wxe66f905683582780', //服应用ID + 'mchid' => '1612096731', //商户号 + 'merchantSerialNumber' => '761590F1FF6DFC2466894F96E2DE1169CE644A74', //商户号API证书序列号 + 'merchantPrivateKey' => '/home/dev28/liche/api/third_party/WXconfig/apiclient_key.pem', //商户私钥路径 + 'wechatpayCertificate' => '/home/dev28/liche/api/third_party/WXconfig/apiclient_wechatpay.pem', //微信支付平台证书路径 + 'sub_appid' => 'wx98e64c11aac45966' //子商户应用ID +]; + diff --git a/api/controllers/wxapp/app/Payment.php b/api/controllers/wxapp/app/Payment.php index 0b7e35cb..8b71a56c 100644 --- a/api/controllers/wxapp/app/Payment.php +++ b/api/controllers/wxapp/app/Payment.php @@ -28,17 +28,52 @@ class Payment extends Wxapp{ if($row['status']>1){ throw new Exception('订单已支付', API_CODE_FAIL); } - if($row['status']=1 && !$row['status_detail']!=11){ + if($row['status']=1 && $row['status_detail']!=11){ throw new Exception('订单已过期', API_CODE_FAIL); } - if($row['total_price']>0){ $url = http_host_com('api'); - $notify_url = $url."/wxapp/{$this->app_key}/wxnotify"; + $notify_url = $url."/wxapp/{$this->app_key}/wxnotify_v3"; if($this->uid<=10){ $row['total_price'] = 0.01; } - $result = $this->pay($sid, $row['total_price'], $this->session['openid'], $row['item_title'], $notify_url); + + $this->config->load('wxpay'); + $wx_config = $this->config->item('default'); + $this->load->library('WechatPayV3'); + $params = [ + 'merchantId' => $wx_config['mchid'], + 'merchantSerialNumber' => $wx_config['merchantSerialNumber'], + 'merchantPrivateKey' => $wx_config['merchantPrivateKey'], + 'wechatpayCertificate' => $wx_config['wechatpayCertificate'], + ]; + $WechatPayV3 = new WechatPayV3($params); + $n_time = time(); + $json = [ + 'sp_appid' => $wx_config['appid'], + 'sp_mchid' => $wx_config['mchid'], + 'sub_appid' => $wx_config['sub_appid'], + 'sub_mchid' => $row['mch_id'], + 'description' => $row['item_title'], + 'out_trade_no' => $row['sid'], + 'notify_url' => $notify_url, + 'settle_info' => [ + 'profit_sharing' => true + ], + 'amount' => [ + 'total' => $row['total_price']*100, + ], + 'payer' => [ + 'sub_openid' => $this->session['openid'] + ], + ]; + $noncestr = getNonceStr(20); + $resq = $WechatPayV3->unifiedOrder($json,$json['sub_appid'],$noncestr); + if(!$resq['code']){ + debug_log("[下单失败]:" . $resq['msg'], 'underorder.log','wxpay'); + throw new Exception('微信下单失败', API_CODE_FAIL); + } + $result = $resq['data']; }else{ $this->load->service('apporder/payment_service', array('app_id' => $this->app_id)); $result = $this->payment_service->after_pay($sid); @@ -46,44 +81,4 @@ class Payment extends Wxapp{ return $result; } - /** - * 支付方法 - * @param $trade_no - * @param $price - * @param $openid - * @param $body - * @param $notify_url - * @param $attach - * @param $detail - * @return bool|json数据,可直接填入js函数作为参数|mixed - * @throws WxPayException - */ - private function pay($trade_no,$price,$openid,$body,$notify_url,$attach = '',$detail = ''){ - if(!$body){return false;} - require_once APPPATH."third_party/WXconfig/liche_WxPay.Config.php"; - require_once APPPATH."third_party/WXpay/WxPay.Api.php"; - $config = new WxPayConfig(); - $wxpay = new WxPayUnifiedOrder(); - $wxpay->SetVersion('1.0'); - $wxpay->SetBody($body); //简单描述 - $attach && $wxpay->SetAttach($attach); //附加信息 - $wxpay->SetNotify_url($notify_url); - $wxpay->SetOut_trade_no($trade_no); //订单号 - $wxpay->SetTotal_fee($price * 100); //支付价格 - $wxpay->SetTime_start(date("YmdHis")); //交易起始时间 - $wxpay->SetTime_expire($out_time); //交易结束时间 - $wxpay->SetTrade_type("JSAPI"); //设置交易类型 - $wxpay->SetOpenid($openid); //openid - $detail && $wxpay->SetDetail($detail); - $return = WxPayApi::unifiedOrder($config, $wxpay); //统一支付 - if($return['result_code'] == 'SUCCESS') { - $wxpay_api = new WxPayJsApiPay(); - $jsApiParameters = WxPayApi::GetJsApiParameters($return, $config, $wxpay_api); - $jsApiParameters = json_decode($jsApiParameters, true); - return $jsApiParameters; - }else{ - throw new Exception($return['return_msg']?$return['return_msg'].$return['err_code_des']:$return['return_msg'], API_CODE_FAIL); - } - } - } diff --git a/api/controllers/wxapp/app/Series.php b/api/controllers/wxapp/app/Series.php index 4af311fc..8ac60586 100644 --- a/api/controllers/wxapp/app/Series.php +++ b/api/controllers/wxapp/app/Series.php @@ -60,7 +60,7 @@ class Series extends Wxapp{ return $list; } //获取车系属性 - public function get_attrs(){ + protected function get_attrs(){ $s_id = $this->input_param('id'); $type = $this->input_param('type'); $page = $this->input_param('page'); @@ -85,10 +85,6 @@ class Series extends Wxapp{ 'id' => $val['id'], 'title' => $val['title'] ]; - if($val['type']==1){ - $temp['price'] = $jsodnata['price']; - $temp['deposit'] = $jsodnata['deposit']; - } $lists[] = $temp; } } @@ -98,4 +94,25 @@ class Series extends Wxapp{ ]; return $data; } + + //获取车型信息信息 + protected function get_info(){ + $s_id = $this->input_param('car_id'); + $v_id = $this->input_param('v_id'); + $color_id = $this->input_param('color_id'); + $incolor_id = $this->input_param('incolor_id'); + $this->load->model('auto/auto_cars_model'); + + if(!$s_id || !$v_id || !$color_id || !$incolor_id){ + throw new Exception('参数错误', ERR_PARAMS_ERROR); + } + + $attrs = "{$color_id}_{$v_id}_{$incolor_id}"; + $car = $this->auto_cars_model->get(['attrs'=>$attrs,'s_id'=>$s_id]); + $data = [ + 'price' => $car['price_car'] ? $car['price_car'] : 0, + 'deposit' => $car['price_book'] ? $car['price_book'] : 0 + ]; + return $data; + } } diff --git a/api/controllers/wxapp/liche/Contract.php b/api/controllers/wxapp/liche/Contract.php index 2774ef98..ed1cc870 100644 --- a/api/controllers/wxapp/liche/Contract.php +++ b/api/controllers/wxapp/liche/Contract.php @@ -23,6 +23,9 @@ class Contract extends Wxapp{ $this->load->model('receiver/order/receiver_order_ckcars_model','ckcars_model'); $this->load->model('receiver/order/receiver_order_bills_model','bills_model'); $this->load->model('receiver/order/receiver_order_deliverys_model','deliverys_model'); + + $this->load->model("biz/biz_model"); + $this->load->model("sys/sys_company_model"); } protected function get(){ @@ -43,9 +46,7 @@ class Contract extends Wxapp{ list($h5_url,$title) = $this->orders_entity->get_contract_h5($id,$type,1); list($pdf2img_url,$title) = $this->orders_entity->get_contract_h5($id,$type); if(!$contract){ - $cid = create_order_no(350200,$this->app_key,2); $add_data = [ - 'cid' => $cid, 'o_id' => $id, 'type' => $type, 'c_time' => time() @@ -54,12 +55,15 @@ class Contract extends Wxapp{ if(!$con_id){ throw new Exception('创建合同失败', API_CODE_INVILD_PARAM); } + $cid = create_contract_no(350200,$con_id,$type); + $this->contracts_model->update(['cid'=>$cid],['id'=>$con_id]); //html转pdf $save_path = 'data/contracts/'.date('Ymd'); $filename = $cid.'.pdf'; $c_res = $this->pdf->html2pdf($pdf2img_url,FCPATH.$save_path,$filename); $update = [ - 'file' => $save_path.'/'.$filename + 'file' => $save_path.'/'.$filename, + 'cid' => $cid ]; $this->contracts_model->update($update,['id'=>$con_id]); }else{ @@ -91,6 +95,7 @@ class Contract extends Wxapp{ $img = $this->input_param('img'); $row = $this->contracts_model->get(['type'=>$type,'o_id'=>$id]); + $order = $this->orders_model->get(['id'=>$id],'biz_id'); if(!$row['file'] || !$img){ throw new Exception('参数错误', API_CODE_INVILD_PARAM); @@ -98,20 +103,28 @@ class Contract extends Wxapp{ if($row['status']==1){ throw new Exception('已签名', API_CODE_INVILD_PARAM); } + //获取公司印章 + $biz = $this->biz_model->get(['id'=>$order['biz_id']],'company_id'); + $company = $this->sys_company_model->get(['id'=>$biz['company_id']],'img_seal,id'); + $img_seal = $company['img_seal'] ? build_qiniu_image_url($company['img_seal']) : ''; + if(!$img_seal){ + throw new Exception('公司公章未上传', API_CODE_FAIL); + } //pdf转图片 $pdf_url = http_host_com('api').'/'.$row['file']; $this->load->library('pdf'); - $pdf = new Pdf(); + $pdf = new Pdf(); //正式 $imgs = $pdf->pdf2img($pdf_url); if(!$imgs){ throw new Exception('签名失败,图片转换失败', API_CODE_INVILD_PARAM); } $this->load->library('receiver/sign_entity'); + $sign_entity = new Sign_entity(['comp_img'=>$img_seal]); switch($type){ case 0: //整车 $sign_img = array_pop($imgs); - $res = $this->sign_entity->merge($sign_img,$img,1050,230,300); + $res = $sign_entity->merge($sign_img,$img,1070,500,230); if(!$res){ throw new Exception('签名失败', API_CODE_INVILD_PARAM); } @@ -131,7 +144,7 @@ class Contract extends Wxapp{ break; case 1: //协议 $sign_img = array_pop($imgs); - $res = $this->sign_entity->merge($sign_img,$img,1050,1600,300); + $res = $sign_entity->merge($sign_img,$img,1050,1650,300); if(!$res){ throw new Exception('签名失败', API_CODE_INVILD_PARAM); } @@ -153,7 +166,7 @@ class Contract extends Wxapp{ break; case 2: //确认信息 $sign_img = array_pop($imgs); - $res = $this->sign_entity->merge($sign_img,$img,1050,430,300); + $res = $sign_entity->merge($sign_img,$img,1050,430,450); if(!$res){ throw new Exception('签名失败', API_CODE_INVILD_PARAM); } @@ -167,10 +180,9 @@ class Contract extends Wxapp{ $result = $this->contracts_model->update($update,['id'=>$row['id']]); if($result){ $this->ckcars_model->update(['status'=>2],['o_id'=>$id]); - $this->orders_model->update(['status'=>3],['id'=>$id]); - if(!$this->bills_model->count(['o_id'=>$id])){ - $this->bills_model->add(['o_id'=>$id,'c_time'=>time()]); - } + //生成支付订单 + $this->load->library('receiver/orders_entity'); + $this->orders_entity->check_finish($id,$this->app_id,$this->session); throw new Exception('签名成功', API_CODE_SUCCESS); }else{ throw new Exception('签名失败', API_CODE_INVILD_PARAM); @@ -178,7 +190,7 @@ class Contract extends Wxapp{ break; case 3: //车辆交接 $sign_img = array_pop($imgs); - $res = $this->sign_entity->merge($sign_img,$img,400,1400,'','',false); + $res = $sign_entity->merge($sign_img,$img,1050,1500,350); if(!$res){ throw new Exception('签名失败', API_CODE_INVILD_PARAM); } diff --git a/api/controllers/wxapp/liche/Order.php b/api/controllers/wxapp/liche/Order.php index c5133dd6..958bd0e2 100644 --- a/api/controllers/wxapp/liche/Order.php +++ b/api/controllers/wxapp/liche/Order.php @@ -9,13 +9,19 @@ defined('WXAPP_APP') OR exit('No direct script access allowed'); */ require_once APPPATH . 'controllers/wxapp/Wxapp.php'; class Order extends Wxapp{ - + private $type_array = [ + 3 => '定金', + 4 => '定金', + 5 => '委托服务费', + 6 => '首付', + ]; public function __construct($inputs, $app_key){ parent::__construct($inputs, $app_key); $this->login_white = array();//登录白名单 $this->uid = $this->session['uid']; $this->load->model('apporder/order_purchase_model'); + $this->load->model('receiver/order/receiver_orders_model'); } /** @@ -34,7 +40,7 @@ class Order extends Wxapp{ if($type){ $where['status'] = $type; } - $fileds = 'id,sid,item_title,total_price,jsondata,status,status_detail'; + $fileds = 'id,sid,item_title,total_price,jsondata,status,status_detail,type'; $rows = $this->order_purchase_model->select($where,'id desc',$page,$size,$fileds); $total = $this->order_purchase_model->count($where); $list = []; @@ -63,10 +69,20 @@ class Order extends Wxapp{ ]; } } + $type_name = '金额'; + if($this->type_array[$val['type']]){ + if($val['type']==6){ + $r_order = $this->receiver_orders_model->get(['id'=>$cf_id],'payway'); + $type_name = $r_order['payway'] ? '尾款' : '首付'; + }else{ + $type_name = $this->type_array[$val['type']]; + } + } $list[] = [ 'id' => $val['id'], 'sid' => $val['sid'], 'cover' => $jsondata['cover'] ? build_qiniu_image_url($jsondata['cover']) : '', + 'type_name' => $type_name, 'title' => $val['item_title'], 'price' => $val['total_price'], 'color' => isset($car['color']['jsondata']['title']) ? $car['color']['jsondata']['title'] : "", diff --git a/api/controllers/wxapp/liche/User.php b/api/controllers/wxapp/liche/User.php index 41ed451f..c7cdadbf 100644 --- a/api/controllers/wxapp/liche/User.php +++ b/api/controllers/wxapp/liche/User.php @@ -307,7 +307,12 @@ class User extends Wxapp{ if($ckcar_row['status']){ if($val['id']==5){ $state = 2; - $progressOpt = ['title'=> '确认车辆','url'=>'/pages/mine/signContract/queRen?id='.$row['id']]; + if($ckcar_row['status']==2){ //未支付 + $title = $row['payway'] ? '去支付尾款' : '去支付首付'; + $progressOpt = ['title'=> $title,'url'=>'/pages/order/index']; + }else{ + $progressOpt = ['title'=> '确认车辆','url'=>'/pages/mine/signContract/queRen?id='.$row['id']]; + } }else{ $state = 1; } diff --git a/api/controllers/wxapp/liche/Wxnotify.php b/api/controllers/wxapp/liche/Wxnotify.php index ceb646b9..d94bb68c 100755 --- a/api/controllers/wxapp/liche/Wxnotify.php +++ b/api/controllers/wxapp/liche/Wxnotify.php @@ -7,7 +7,6 @@ */ //微信支付回调 require_once APPPATH."third_party/WXpay/WxPay.Api.php"; -require_once APPPATH."third_party/WXconfig/liche_WxPay.Config.php"; class Wxnotify extends CI_Controller{ private $log_file = 'liche_pay.log'; @@ -18,12 +17,21 @@ class Wxnotify extends CI_Controller{ parent::__construct(); $this->load->model('app/app_wxpaylog_model', 'wxpaylog_model'); $this->load->model('apporder/order_purchase_model','purchase_model'); + $input = file_get_contents("php://input"); + debug_log("[info] ". __FUNCTION__ . "# input:" . $input, $this->log_file); + //xml 转数组 + $obj = simplexml_load_string($input,"SimpleXMLElement", LIBXML_NOCDATA); + $obj_array = json_decode(json_encode($obj),true); + $mch_id = $obj_array['mch_id']; + $config_file = APPPATH."third_party/WXconfig/liche_WxPay.Config.php"; + if(!file_exists($config_file)){ + debug_log("[error] ". __FUNCTION__ . ":商户配置文件不存在", $this->log_file); + exit(); + } try{ //如果返回成功则验证签名 $config = new WxPayConfig(); $wxpay = new WxPayNotifyResults(); - $input = file_get_contents("php://input"); - debug_log("[info] ". __FUNCTION__ . "# input:" . $input, $this->log_file); $result = WxPayNotifyResults::Init($config, $input); $this->notify = $result->GetValues(); }catch (WxPayException $e){ diff --git a/api/controllers/wxapp/liche/Wxnotify_v3.php b/api/controllers/wxapp/liche/Wxnotify_v3.php new file mode 100644 index 00000000..2d49eaea --- /dev/null +++ b/api/controllers/wxapp/liche/Wxnotify_v3.php @@ -0,0 +1,77 @@ +load->model('app/app_wxpaylog_model', 'wxpaylog_model'); + $this->load->model('apporder/order_purchase_model','purchase_model'); + + $input = file_get_contents('php://input'); + debug_log("[info] ". __FUNCTION__ . "# input:" . $input, $this->log_file,$this->log_dir); + $this->notify_data = json_decode($input,true); + } + + public function index(){ + $client = new GuzzleHttp\Client(); + try { + $resp = $client->request('POST', $this->desc_url, ['form_params' => $this->notify_data]); + $result = json_decode($resp->getBody(),true); + if(!$result['code']){ //解密失败 + debug_log("[error] ". __FUNCTION__ . "# 解密失败:" . $resp->getBody(), $this->log_file,$this->log_dir); + }else{ + debug_log("[info] ". __FUNCTION__ . "# 解密成功:" . $resp->getBody(), $this->log_file,$this->log_dir); + $sid = $result['data']['out_trade_no']; + if($sid){ + debug_log("[start] ". __FUNCTION__ . ": sid:".$sid, $this->log_file,$this->log_dir); + $order = $this->purchase_model->get(array('sid'=>$sid,'app_id'=>$this->app_id)); + if(!$order){ + debug_log("[error] ". __FUNCTION__ . ":{$sid}_订单不存在", $this->log_file,$this->log_dir); + } + //执行失败 + $add = array( + 'app_id' => $this->app_id, + 'sid' => $sid?$sid:0, + 'trade_no' => $result['data']['transaction_id'], + 'notify_param' => json_encode($result['data'],JSON_UNESCAPED_UNICODE) + ); + + $ret = $this->wxpaylog_model->add($add); + if(!$ret){ + debug_log("[error] ". __FUNCTION__ . ": sql:".$this->wxpaylog_model->db->last_query(), $this->log_file,$this->log_dir); + } + if($result['data']['trade_state'] != 'SUCCESS'){ //支付失败 + debug_log("[error] ". __FUNCTION__ . ":支付失败,sid={$sid},app_id=", $this->log_file,$this->log_dir); + }else{ //支付成功 + $this->load->service('apporder/payment_service', array('app_id' => $this->app_id)); + $result = $this->payment_service->after_pay($sid,$result['data']['amount']['payer_total']/100); + if($result['code']){ + debug_log("[success] ". __FUNCTION__ . ":操作成功", $this->log_file,$this->log_dir); + }else{ + debug_log("[error] ". __FUNCTION__ . ":".$result['msg'], $this->log_file,$this->log_dir); + } + } + debug_log("[finish] ". __FUNCTION__ . ": sid:".$sid, $this->log_file,$this->log_dir); + }else{ + debug_log("[finish] ". __FUNCTION__ . ": 参数错误:".json_encode($result,JSON_UNESCAPED_UNICODE), $this->log_file,$this->log_dir); + } + } + } catch (RequestException $e) { + debug_log("[error] ". __FUNCTION__ . "# 请求失败:" . $e->getResponse(), $this->log_file,$this->log_dir); + } + echo json_encode(['code'=>'SUCCESS','message'=>'成功'],JSON_UNESCAPED_UNICODE); + } + +} diff --git a/api/controllers/wxapp/licheb/Cusorder.php b/api/controllers/wxapp/licheb/Cusorder.php index 5cbcb4aa..5c3d21e0 100644 --- a/api/controllers/wxapp/licheb/Cusorder.php +++ b/api/controllers/wxapp/licheb/Cusorder.php @@ -23,6 +23,10 @@ class Cusorder extends Wxapp{ $this->load->model('receiver/order/receiver_order_signs_model','order_signs_model'); $this->load->model('receiver/order/receiver_order_bills_model','order_bills_model'); $this->load->model('receiver/order/receiver_order_deliverys_model','order_deliverys_model'); + + $this->load->model('receiver/receiver_services_model','services_model'); + $this->load->model('receiver/receiver_service_package_model','package_model'); + $this->load->model('auto/auto_series_model'); $this->load->model('auto/auto_brand_model'); $this->load->model('auto/auto_attr_model'); @@ -47,6 +51,11 @@ class Cusorder extends Wxapp{ $price = $this->input_param('price'); $deposit = $this->input_param('deposit'); $payway = $this->input_param('payway'); + $pack_id = $this->input_param('pack_id'); + $main_type = $this->input_param('main_type'); + $ifentrust = $this->input_param('ifentrust'); + $entrust_name = $this->input_param('entrust_name'); + $entrust_idcard = $this->input_param('entrust_idcard'); $row = $this->customers_model->get(['id'=>$cus_id]); $series_row = $this->auto_series_model->get(['id'=>$car_id]); @@ -100,6 +109,16 @@ class Cusorder extends Wxapp{ 'c_time' => time() ]; $payway && $data['payway'] = 1; + $pack_id && $data['pack_id'] = $pack_id; + $main_type && $data['main_type'] = 1; + if($ifentrust){ + $data['ifentrust'] = 1; + $info_json = [ + 'entrust_name' => $entrust_name, + 'entrust_idcard' => $entrust_idcard + ]; + $data['info_json'] = json_encode($info_json,JSON_UNESCAPED_UNICODE); + } $o_id = $this->orders_model->add($data); if($o_id){ $sign_data = [ @@ -117,6 +136,7 @@ class Cusorder extends Wxapp{ protected function put(){ $id = $this->input_param('id'); $payway = $this->input_param('payway'); + $pack_id = $this->input_param('pack_id'); $row = $this->orders_model->get(['id'=>$id]); if(!$row){ throw new Exception('订单不存在', ERR_PARAMS_ERROR); @@ -125,13 +145,13 @@ class Cusorder extends Wxapp{ $result = false; $up_data = []; - if(strlen($payway)){ + if(strlen($payway)|| $pack_id){ if($row['status']>0){ throw new Exception('修改失败,已签订合同', ERR_PARAMS_ERROR); } - $up_data['payway'] = $payway; + strlen($payway) && $up_data['payway'] = $payway; + $pack_id && $up_data['pack_id'] = $pack_id; } - if($up_data){ $result = $this->orders_model->update($up_data,['id'=>$id]); } @@ -168,6 +188,11 @@ class Cusorder extends Wxapp{ $keyword = $this->input_param('keyword'); $status = $this->input_param('status'); $ismy = $this->input_param('ismy'); //是否只显示自己 + $page = $this->input_param('page'); + $size = $this->input_param('size'); + + !$page && $page = 1; + !$size && $size = 10; $where = [ 'biz_id' => $biz_id @@ -255,14 +280,28 @@ class Cusorder extends Wxapp{ } $loan_status = $loan['status']>0 ? 2 : 1; } - + $pack_title = ''; + if($row['pack_id']){ + $pack_row = $this->package_model->get(['id'=>$row['pack_id']]); + $srv_rows = ''; + $pack_row['srv_ids'] && $srv_rows = $this->services_model->select(["id in ({$pack_row['srv_ids']})"=>null],'','','','title'); + $srv_rows && $pack_title = implode('+',array_column($srv_rows,'title')); + } $car_data = [ '车辆名称' => $brand['name'].$series['name'], '车辆级别' => $version, '颜色' => $color, '车辆合同售价' => $row['price'], - '定金' => $row['deposit'] + '定金' => $row['deposit'], + '代办包' => $pack_title, + '购车主体' => $row['main_type'] ? '公司' : '个人', + '是否委托' => $row['ifentrust'] ? '是' : '否', ]; + if($row['ifentrust']){ + $info_json = json_decode($row['info_json'],true); + $car_data['委托人姓名'] = $info_json['entrust_name'] ? $info_json['entrust_name'] : ''; + $car_data['委托人身份证'] = $info_json['entrust_idcard'] ? $info_json['entrust_idcard'] : ''; + } //开票信息 $bill_status = 0; $bill_data = []; @@ -297,7 +336,8 @@ class Cusorder extends Wxapp{ 'loan_data' => $loan_data, 'bill_status' => $bill_status, 'bill_data' => $bill_data, - 'ckcar_status' => $ckcar_status + 'ckcar_status' => $ckcar_status, + 'pack_id' => $row['pack_id'] ]; return $data; } diff --git a/api/controllers/wxapp/licheb/Customerlogs.php b/api/controllers/wxapp/licheb/Customerlogs.php index e1e6d974..185d5884 100644 --- a/api/controllers/wxapp/licheb/Customerlogs.php +++ b/api/controllers/wxapp/licheb/Customerlogs.php @@ -37,11 +37,18 @@ class Customerlogs extends Wxapp{ $count = $this->customer_oplogs_model->count($where); $lists = []; if($count){ - $rows = $this->customer_oplogs_model->select($where,'id desc',$page,$size,'log,uname,type,c_time'); + $rows = $this->customer_oplogs_model->select($where,'id desc',$page,$size,'log,uname,type,c_time,imgs'); foreach($rows as $key => $val){ $record = ''; $second = 0; $content = $val['log']; + $img_json = json_decode($val['imgs'],true); + $imgs = []; + if($img_json){ + foreach($img_json as $val2){ + $imgs[] = build_qiniu_image_url($val2); + } + } if($val['type']==2){ $rec_row = $this->receiver_xz_model->get(['id'=>$val['log']],'rec_url,duration'); $content = '拨打电话'; @@ -53,6 +60,7 @@ class Customerlogs extends Wxapp{ 'content' => "【{$val['uname']}】".$content, 'record_url' => $record, 'second' => $second, + 'imgs' => $imgs, 'c_time' => date('Y.m.d',$val['c_time']) ]; } diff --git a/api/controllers/wxapp/licheb/Protocol.php b/api/controllers/wxapp/licheb/Protocol.php index 661dd1b1..686f2137 100644 --- a/api/controllers/wxapp/licheb/Protocol.php +++ b/api/controllers/wxapp/licheb/Protocol.php @@ -16,29 +16,57 @@ class Protocol extends CI_Controller{ $this->load->model('receiver/order/receiver_order_agents_model','agents_model'); $this->load->model('receiver/order/receiver_order_loans_model','loans_model'); $this->load->model('receiver/order/receiver_order_ckcars_model','ckcars_model'); + $this->load->model('receiver/receiver_service_package_model','package_model'); + $this->load->model('receiver/receiver_services_model','services_model'); + $this->load->model('auto/auto_series_model'); $this->load->model('auto/auto_brand_model'); + $this->load->model('auto/auto_cars_model'); + + $this->load->model("biz/biz_model"); + $this->load->model('area_model'); + $this->load->model("sys/sys_company_model"); $this->load->model("items/items_model"); + $this->load->model('apporder/order_purchase_model','purchase_model'); } //整车合同 public function car(){ $wxapp = $this->input->get('wxapp'); - //$id = $this->input->get('id'); - //$row = $this->orders_model->get(['id'=>$id]); - //$contract = $this->contracts_model->get(['o_id'=>$id,'type'=>0]); - //if($row){ - // $brand = $this->auto_brand_model->get(['id'=>$row['brand_id']],'name'); - // $series = $this->auto_series_model->get(['id'=>$row['s_id']],'name'); - // $car_json = json_decode($row['car_json'],true); - // $color = isset($car_json['color']) ? $car_json['color']['title'] : ''; - // $version = isset($car_json['version']) ? $car_json['version']['title'] : ''; - // $row['brand_name'] = $brand['name'].$series['name'].' '.$version; - // $row['color'] = $color; - //} - //$row['day'] = $contract ? date('Y年m月d日',$contract['c_time']):date('Y年m月d日'); - //$row['price_rmb'] = num_to_rmb($row['price']); - //$row['cid'] = $contract['cid']; + $id = $this->input->get('id'); + $row = $this->orders_model->get(['id'=>$id]); + $contract = $this->contracts_model->get(['o_id'=>$id,'type'=>0]); + if($row){ + $brand = $this->auto_brand_model->get(['id'=>$row['brand_id']],'name'); + $series = $this->auto_series_model->get(['id'=>$row['s_id']],'name'); + $car_json = json_decode($row['car_json'],true); + $row['color'] = isset($car_json['color']) ? $car_json['color']['title'] : ''; + $row['incolor'] = isset($car_json['incolor']) ? $car_json['incolor']['title'] : ''; + $row['version'] = isset($car_json['version']) ? $car_json['version']['title'] : ''; + $row['brand_name'] = $brand['name']; + $row['series_name'] = $series['name']; + //获取门店信息 + $biz = $this->biz_model->get(['id'=>$row['biz_id']]); + $city = $this->area_model->get(['county_id'=>$biz['county_id']],'city_name,county_name'); + $row['address'] = $city['city_name'].$city['county_name'].'  '.$biz['address']; + $company = $this->sys_company_model->get(['id'=>$biz['company_id']]); + $row['company'] = $company; + //获取选择代办包 + $pack_row = $this->package_model->get(['id'=>$row['pack_id']],'srv_ids'); + $srv_total = count(explode(',',$pack_row['srv_ids'])); + if($srv_total==4){ + $row['give_time'] = 30; + }elseif($srv_total==3){ + $row['give_time'] = 45; + }else{ + $row['give_time'] = 60; + } + } + $row['info_json'] = json_decode($row['info_json'],true); + $row['price_rmb'] = num_to_rmb($row['price']); + $row['cid'] = $contract['cid']; + $row['day'] = $contract ? date('Y年m月d日',$contract['c_time']):date('Y年m月d日'); + $row['dep_price'] = number_format(5000,2); //定金 $folder = $wxapp ? 'protocol' : 'html2pdf'; $this->load->view("wxapp/licheb/{$folder}/car",$row); } @@ -46,31 +74,64 @@ class Protocol extends CI_Controller{ //代理合同 public function agent(){ $wxapp = $this->input->get('wxapp'); - //$id = $this->input->get('id'); - //$row = $this->orders_model->get(['id'=>$id]); - //$contract = $this->contracts_model->get(['o_id'=>$id,'type'=>1]); - //$row['day'] = $contract ? date('Y年m月d日',$contract['c_time']):date('Y年m月d日'); - //$row['cid'] = $contract['cid']; + $id = $this->input->get('id'); + $row = $this->orders_model->get(['id'=>$id]); + $contract = $this->contracts_model->get(['o_id'=>$id,'type'=>1]); + $total_price = 0; + if($row){ + //获取门店信息 + $biz = $this->biz_model->get(['id'=>$row['biz_id']]); + $company = $this->sys_company_model->get(['id'=>$biz['company_id']]); + $row['company'] = $company; + //获取服务包 + $packs = $this->package_model->get(['id'=>$row['pack_id']],'srv_ids'); + if($packs['srv_ids']){ + $row['services'] = $services = $this->services_model->select(["id in ({$packs['srv_ids']})"],'','','','title,field_name'); + } + $attrs = "{$row['cor_id']}_{$row['v_id']}_{$row['incor_id']}"; + $car = $this->auto_cars_model->get(['attrs'=>$attrs,'s_id'=>$row['s_id']]); + //获取挂牌价 + $this->load->model('sys/sys_city_model'); + $city = $this->sys_city_model->get(['city_id'=>$biz['city_id']],'fee_carno'); + $car['fee_carno'] = $city['fee_carno']; + if($services){ + foreach($services as $key=>$val){ + $field_arr = explode('.',$val['field_name']); + if($car[$field_arr[1]]){ + $total_price += $car[$field_arr[1]]; + } + } + } + } + $row['total_price'] = $total_price; + $row['info_json'] = json_decode($row['info_json'],true); + $row['day'] = $contract ? date('Y年m月d日',$contract['c_time']):date('Y年m月d日'); + $row['cid'] = $contract['cid']; $folder = $wxapp ? 'protocol' : 'html2pdf'; $this->load->view("wxapp/licheb/{$folder}/agent",$row); } //车辆信息确认单 public function car_ck(){ $wxapp = $this->input->get('wxapp'); - //$id = $this->input->get('id'); - //$row = $this->orders_model->get(['id'=>$id]); - //if($row){ - // $brand = $this->auto_brand_model->get(['id'=>$row['brand_id']],'name'); - // $series = $this->auto_series_model->get(['id'=>$row['s_id']],'name'); - // $car_json = json_decode($row['car_json'],true); - // $version = isset($car_json['version']) ? $car_json['version']['title'] : ''; - // $row['brand_name'] = $brand['name'].$series['name'].' '.$version; - //} - //$contract = $this->contracts_model->get(['o_id'=>$id,'type'=>2]); - //$items = $this->items_model->get(['id'=>$row['item_id']],'vin'); - //$row['day'] = $contract ? date('Y年m月d日',$contract['c_time']):date('Y年m月d日'); - //$row['cid'] = $contract['cid']; - //$row['vin'] = $items['vin']; + $id = $this->input->get('id'); + $row = $this->orders_model->get(['id'=>$id]); + if($row){ + $brand = $this->auto_brand_model->get(['id'=>$row['brand_id']],'name'); + $series = $this->auto_series_model->get(['id'=>$row['s_id']],'name'); + $car_json = json_decode($row['car_json'],true); + $version = isset($car_json['version']) ? $car_json['version']['title'] : ''; + $row['brand_name'] = $brand['name'].$series['name'].' '.$version; + //获取门店信息 + $biz = $this->biz_model->get(['id'=>$row['biz_id']]); + $company = $this->sys_company_model->get(['id'=>$biz['company_id']]); + $row['company'] = $company; + } + $contract = $this->contracts_model->get(['o_id'=>$id,'type'=>2]); + $items = $this->items_model->get(['id'=>$row['item_id']],'vin'); + $row['info_json'] = json_decode($row['info_json'],true); + $row['day'] = $contract ? date('Y年m月d日',$contract['c_time']):date('Y年m月d日'); + $row['cid'] = $contract['cid']; + $row['vin'] = $items['vin']; $folder = $wxapp ? 'protocol' : 'html2pdf'; $this->load->view("wxapp/licheb/{$folder}/car_ck",$row); } @@ -78,34 +139,64 @@ class Protocol extends CI_Controller{ public function car_fh(){ $this->load->model('app/licheb/app_licheb_users_model'); $wxapp = $this->input->get('wxapp'); - //$id = $this->input->get('id'); + $id = $this->input->get('id'); - //$row = $this->orders_model->get(['id'=>$id]); - //$agent = $this->agents_model->get(['o_id'=>$id]); - //$contract = $this->contracts_model->get(['o_id'=>$id,'type'=>3]); - //$items = $this->items_model->get(['id'=>$row['item_id']],'vin'); - //if($row){ - // //贷款 - // if(!$row['payway']){ - // $row['loan'] = $this->loans_model->get(['o_id'=>$id]); - // } - // $brand = $this->auto_brand_model->get(['id'=>$row['brand_id']],'name'); - // $series = $this->auto_series_model->get(['id'=>$row['s_id']],'name'); - // $car_json = json_decode($row['car_json'],true); - // $info_json = json_decode($row['info_json'],true); - // $color = isset($car_json['color']) ? $car_json['color']['title'] : ''; - // $version = isset($car_json['version']) ? $car_json['version']['title'] : ''; - // $row['brand_name'] = $brand['name'].$series['name'].' '.$version; - // $row['color'] = $color; - // $row['cardid'] = $info_json['cardid']; - // //获取销售 - // $admin = $this->app_licheb_users_model->get(['id'=>$row['admin_id']],'uname'); - // $row['uname'] = $admin['uname']; - //} - //$row['agent'] = $agent; - //$row['day'] = $contract ? date('Y年m月d日',$contract['c_time']):date('Y年m月d日'); - //$row['cid'] = $contract['cid']; - //$row['vin'] = $items['vin']; + $row = $this->orders_model->get(['id'=>$id]); + $agent = $this->agents_model->get(['o_id'=>$id]); + $contract = $this->contracts_model->get(['o_id'=>$id,'type'=>3]); + $items = $this->items_model->get(['id'=>$row['item_id']],'vin'); + $total_price = 0; + if($row){ + //贷款 + if(!$row['payway']){ + $row['loan'] = $this->loans_model->get(['o_id'=>$id]); + } + $brand = $this->auto_brand_model->get(['id'=>$row['brand_id']],'name'); + $series = $this->auto_series_model->get(['id'=>$row['s_id']],'name'); + $car_json = json_decode($row['car_json'],true); + $row['info_json'] = $info_json = json_decode($row['info_json'],true); + $row['color'] = isset($car_json['color']) ? $car_json['color']['title'] : ''; + $row['incolor'] = isset($car_json['incolor']) ? $car_json['incolor']['title'] : ''; + $row['version'] = isset($car_json['version']) ? $car_json['version']['title'] : ''; + $row['brand_name'] = $brand['name']; + $row['series_name'] = $series['name']; + $row['cardid'] = $info_json['cardid']; + //获取门店信息 + $biz = $this->biz_model->get(['id'=>$row['biz_id']]); + $company = $this->sys_company_model->get(['id'=>$biz['company_id']]); + $row['company'] = $company; + //获取服务包 + $packs = $this->package_model->get(['id'=>$row['pack_id']],'srv_ids'); + if($packs['srv_ids']){ + $row['services'] = $services = $this->services_model->select(["id in ({$packs['srv_ids']})"],'','','','title,field_name'); + } + $attrs = "{$row['cor_id']}_{$row['v_id']}_{$row['incor_id']}"; + $car = $this->auto_cars_model->get(['attrs'=>$attrs,'s_id'=>$row['s_id']]); + //获取挂牌价 + $this->load->model('sys/sys_city_model'); + $city = $this->sys_city_model->get(['city_id'=>$biz['city_id']],'fee_carno'); + $car['fee_carno'] = $city['fee_carno']; + if($services){ + foreach($services as $key=>$val){ + $field_arr = explode('.',$val['field_name']); + if($car[$field_arr[1]]){ + $total_price += $car[$field_arr[1]]; + } + } + } + $where = [ + 'item_id' => $row['id'], + 'app_id' => 1, + 'status>' => 1 + ]; + $pay = $this->purchase_model->sum('total_price',$where); + $row['pay_price'] = $pay['total_price']; + } + $row['total_price'] = $total_price; + $row['agent'] = $agent; + $row['day'] = $contract ? date('Y年m月d日',$contract['c_time']):date('Y年m月d日'); + $row['cid'] = $contract['cid']; + $row['vin'] = $items['vin']; $folder = $wxapp ? 'protocol' : 'html2pdf'; $this->load->view("wxapp/licheb/{$folder}/car_fh",$row); } diff --git a/api/controllers/wxapp/licheb/Services.php b/api/controllers/wxapp/licheb/Services.php new file mode 100644 index 00000000..2a3cf0a4 --- /dev/null +++ b/api/controllers/wxapp/licheb/Services.php @@ -0,0 +1,54 @@ +login_white = array('get_package');//登录白名单 + $this->load->model('receiver/receiver_services_model','services_model'); + $this->load->model('receiver/receiver_service_package_model','package_model'); + } + //服务包 + protected function get_package(){ + $page = $this->input_param('page'); + $size = $this->input_param('size'); + + !$page && $page = 1; + !$size && $size = 10; + $where = [ + 'status' => 1 + ]; + $count = $this->package_model->count($where); + $list = []; + if($count){ + $rows = $this->package_model->select($where,'id asc',$page,$size); + foreach($rows as $key=>$val){ + $title = ''; + if($val['srv_ids']){ + $s_where = [ + "id in ({$val['srv_ids']})" => null + ]; + $s_rows = $this->services_model->select($s_where,'','','','id,title'); + $s_rows && $title = implode('+',array_column($s_rows,'title')); + } + $list[] = [ + 'id' => $val['id'], + 'title' => $title + ]; + } + } + $data = [ + 'list' => $list, + 'total' => $count + ]; + return $data; + } +} diff --git a/api/third_party/WXconfig/liche_WxPay.Config.php b/api/third_party/WXconfig/liche_WxPay.Config.php index bc9b9388..75af3369 100755 --- a/api/third_party/WXconfig/liche_WxPay.Config.php +++ b/api/third_party/WXconfig/liche_WxPay.Config.php @@ -10,7 +10,7 @@ class WxPayConfig const APPID = 'wx98e64c11aac45966'; const APPSECRET = 'f8eec7be1c87a1c8e40213e144821ec3'; const MCHID = '1611216095'; - const KEY = '53e08ba6735ab9777c0369e0c8ea0f7b'; + const KEY = 'd1ddc03f6178767795dc283e68a80e81'; const SIGN_TYPE = 'MD5'; const NOTIFY_URL = ''; diff --git a/api/views/wxapp/licheb/html2pdf/agent.php b/api/views/wxapp/licheb/html2pdf/agent.php index 26c63c85..a0f1619e 100644 --- a/api/views/wxapp/licheb/html2pdf/agent.php +++ b/api/views/wxapp/licheb/html2pdf/agent.php @@ -31,23 +31,21 @@
委托服务协议
-
协议编号:(右对齐,6位地区编码+合同类型id+yymmdd+6位随机数)
-
甲方(出卖人):(根据门店所属公司)
-
统一社会信用代码:(根据门店所属公司)
-
乙方(买受人):(用户真实姓名)
-
(证件类型):证件号码
-
联系电话:(用户手机号码)
-
#假如有委托人#
-
委托代理人:(委托人真实姓名)
-
(证件类型):委托人证件号码
-
联系电话:(委托人手机号码)
+
协议编号:
+
甲方(出卖人):
+
统一社会信用代码:
+
乙方(买受人):
+
联系电话:
+ +
委托代理人:
+
委托人身份证:
+
乙方为节省时间和精力,就甲乙双方《车辆买卖合同》标的的车辆,自愿委托甲方办理下列委托事项,并达成如下协议:
一、委托代办服务事项(乙方授权甲方代办下列事项)
-
代办保险服务 每个车一个价格
-
代办上牌服务 每个地级市一个价格
-
代办金融服务 (公式)
-
购买精品包
-
代办服务总价:(计算出合同总价,含保险)元, 壹千贰百叁十个肆玩万元整,保险费用由乙方直接支付给保险公司。
+ +
+ +
代办服务总价:元, ,保险费用由乙方直接支付给保险公司。
二、双方特别约定
1、车牌选号若为代选号牌时,均由甲方电话通知乙方,因乙方未能及时选号等其它原因,甲方有权代理决定并不对最终选号结果负责;若为自选号牌,甲方可配合乙方上牌,但不对最终选号结果负责。
2、乙方委托甲方代办车辆挂牌服务时,乙方应事先办妥机动车车辆保险,投保险种包括但不限于车辆损失险和第三者责任险。甲方在代办服务过程中造成车辆毁损、灭失的,乙方应当先向保险公司索赔,赔付不足部分由甲方予以修复或赔偿。
diff --git a/api/views/wxapp/licheb/html2pdf/car.php b/api/views/wxapp/licheb/html2pdf/car.php index 3f737453..2d19544d 100644 --- a/api/views/wxapp/licheb/html2pdf/car.php +++ b/api/views/wxapp/licheb/html2pdf/car.php @@ -9,36 +9,37 @@
车辆买卖合同
合同编号:
-
甲方(出卖人):(根据门店所属公司)
-
统一社会信用代码:(根据门店所属公司)
-
乙方(买受人):(用户真实姓名)
-
联系电话:(用户手机号码)
-
#假如有委托人#
-
委托代理人:(委托人真实姓名)
-
联系电话:(委托人手机号码)
+
甲方(出卖人):
+
统一社会信用代码:
+
乙方(买受人):
+
联系电话:
+ +
委托代理人:
+
委托人身份证:
+
甲、乙双方依据《中华人民共和国合同法》及其他有关法律、法规的规定,在平等、自愿、协商一致的基础上,就买卖汽车事宜,订立本合同。
第一条 车辆基本情况
- 品牌: + 品牌: - 车系: + 车系:
- 车型: + 车型: - 车身颜色: + 车身颜色:
- 内饰颜色: + 内饰颜色: @@ -68,14 +69,16 @@
车辆单价为车身单价,续航里程为300KM以上的车辆已按扣减国家补贴后的价格结算。不包含车辆购置税、车辆保险费、挂牌杂费等其他费用。乙方如需委托甲方代理上牌、委托运输等服务的,双方应另外签订《委托服务协议》。
第三条付款方式
-
乙方在签订合同时即付定金人民币: 元,定金在结算时冲抵车款。
-
乙方选择一次性付款方式 / 分期付款方式
+
乙方在签订合同时即付定金人民币:元,定金在结算时冲抵车款。
+
乙方选择
第四条 车辆交付
-
1、交车时间:合同签订之日起 XX天内。
+
1、交车时间:合同签订之日起 天内。
+
2、提车方式:乙方自提
-
3、交车地点 :    XX市XX区/县 XXXX店  先按当前门店  。
+
3、交车地点 :
4、交车时里程表记录小于100公里
5、备注:如乙方购买续航里程为300KM以上车辆,上牌性质是公户或营运车辆需确保购买车辆自行驶证注册日期起1年之内行驶2万公里,且至车辆注册之日起2年内不得过户,如因违反此条约使甲方在申领国补过程中造成的损失,一切责任及后果由乙方承担。
第五条 车辆确认
diff --git a/api/views/wxapp/licheb/html2pdf/car_ck.php b/api/views/wxapp/licheb/html2pdf/car_ck.php index 3da3ec72..e8eb9e37 100644 --- a/api/views/wxapp/licheb/html2pdf/car_ck.php +++ b/api/views/wxapp/licheb/html2pdf/car_ck.php @@ -8,15 +8,14 @@
车辆信息确认单
-
甲方(出卖人):(根据门店所属公司)
-
统一社会信用代码:(根据门店所属公司)
-
乙方(买受人):(用户真实姓名)
-
(证件类型):证件号码
-
联系电话:(用户手机号码)
-
#假如有委托人#
-
委托代理人:(委托人真实姓名)
-
(证件类型):委托人证件号码
-
联系电话:(委托人手机号码)
+
甲方(出卖人):
+
统一社会信用代码:
+
乙方(买受人):
+
联系电话:
+ +
委托代理人:
+
委托人身份证:
+
乙方于在甲方购买 车辆 1 台,并确认车架号为:。乙方已对上述车辆的厂牌型号、配置等进行认真检查并验收合格无异议。
diff --git a/api/views/wxapp/licheb/html2pdf/car_fh.php b/api/views/wxapp/licheb/html2pdf/car_fh.php index 60d40f64..76acbaef 100644 --- a/api/views/wxapp/licheb/html2pdf/car_fh.php +++ b/api/views/wxapp/licheb/html2pdf/car_fh.php @@ -8,15 +8,15 @@
车辆交付表
-
甲方(出卖人):(根据门店所属公司)
-
统一社会信用代码:(根据门店所属公司)
-
乙方(买受人):(用户真实姓名)
-
(证件类型):证件号码
-
联系电话:(用户手机号码)
-
#假如有委托人#
-
委托代理人:(委托人真实姓名)
-
(证件类型):委托人证件号码
-
联系电话:(委托人手机号码)
+
甲方(出卖人):
+
统一社会信用代码:
+
乙方(买受人):
+
身份证号码:
+
联系电话:
+ +
委托代理人:
+
委托人身份证:
+
一、车辆信息
@@ -25,12 +25,12 @@ 品牌: @@ -54,8 +54,8 @@
- 车系: + 车系:
- 车型: + 车型: 车身颜色: @@ -38,15 +38,15 @@
- 内饰颜色: + 内饰颜色: - 车架号:XW111111111111 + 车架号:
- 车牌号:闽D232333 + 车牌号:
二、结算信息
-
应付款合计 50100 ,其中包含:车辆价款 50000,委托代办款项 3000
-
已付款合计 50000
+
应付款合计 ,其中包含:车辆价款 ,委托代办款项
+
已付款合计
三、证件资料交接信息
证件信息由后台配置,销售交付时钩选才显示:
diff --git a/api/views/wxapp/licheb/protocol/agent.php b/api/views/wxapp/licheb/protocol/agent.php index aab1655c..5db66610 100644 --- a/api/views/wxapp/licheb/protocol/agent.php +++ b/api/views/wxapp/licheb/protocol/agent.php @@ -12,23 +12,21 @@
委托代理服务协议
-
协议编号:(右对齐,6位地区编码+合同类型id+yymmdd+6位随机数)
-
甲方(出卖人):(根据门店所属公司)
-
统一社会信用代码:(根据门店所属公司)
-
乙方(买受人):(用户真实姓名)
-
(证件类型):证件号码
-
联系电话:(用户手机号码)
-
#假如有委托人#
-
委托代理人:(委托人真实姓名)
-
(证件类型):委托人证件号码
-
联系电话:(委托人手机号码)
+
协议编号:
+
甲方(出卖人):
+
统一社会信用代码:
+
乙方(买受人):
+
联系电话:
+ +
委托代理人:
+
委托人身份证:
+
乙方为节省时间和精力,就甲乙双方《车辆买卖合同》标的的车辆,自愿委托甲方办理下列委托事项,并达成如下协议:
一、委托代办服务事项(乙方授权甲方代办下列事项)
-
代办保险服务 每个车一个价格
-
代办上牌服务 每个地级市一个价格
-
代办金融服务 (公式)
-
购买精品包
-
代办服务总价:(计算出合同总价,含保险)元, 壹千贰百叁十个肆玩万元整,保险费用由乙方直接支付给保险公司。
+ +
+ +
代办服务总价:元,,保险费用由乙方直接支付给保险公司。
二、双方特别约定
1、车牌选号若为代选号牌时,均由甲方电话通知乙方,因乙方未能及时选号等其它原因,甲方有权代理决定并不对最终选号结果负责;若为自选号牌,甲方可配合乙方上牌,但不对最终选号结果负责。
2、乙方委托甲方代办车辆挂牌服务时,乙方应事先办妥机动车车辆保险,投保险种包括但不限于车辆损失险和第三者责任险。甲方在代办服务过程中造成车辆毁损、灭失的,乙方应当先向保险公司索赔,赔付不足部分由甲方予以修复或赔偿。
diff --git a/api/views/wxapp/licheb/protocol/car.php b/api/views/wxapp/licheb/protocol/car.php index 0c4ff0bf..9166692a 100644 --- a/api/views/wxapp/licheb/protocol/car.php +++ b/api/views/wxapp/licheb/protocol/car.php @@ -13,36 +13,37 @@
车辆买卖合同
合同编号:
-
甲方(出卖人):(根据门店所属公司)
-
统一社会信用代码:(根据门店所属公司)
-
乙方(买受人):(用户真实姓名)
-
联系电话:(用户手机号码)
-
#假如有委托人#
-
委托代理人:(委托人真实姓名)
-
联系电话:(委托人手机号码)
+
甲方(出卖人):
+
统一社会信用代码:
+
乙方(买受人):
+
联系电话:
+ +
委托代理人:
+
委托人身份证:
+
甲、乙双方依据《中华人民共和国合同法》及其他有关法律、法规的规定,在平等、自愿、协商一致的基础上,就买卖汽车事宜,订立本合同。
第一条 车辆基本情况
- 品牌: + 品牌: - 车系: + 车系:
- 车型: + 车型: - 车身颜色: + 车身颜色:
- 内饰颜色: + 内饰颜色: @@ -72,14 +73,16 @@
车辆单价为车身单价,续航里程为300KM以上的车辆已按扣减国家补贴后的价格结算。不包含车辆购置税、车辆保险费、挂牌杂费等其他费用。乙方如需委托甲方代理上牌、委托运输等服务的,双方应另外签订《委托服务协议》。
第三条付款方式
-
乙方在签订合同时即付定金人民币: 元,定金在结算时冲抵车款。
-
乙方选择一次性付款方式 / 分期付款方式
+
乙方在签订合同时即付定金人民币:元,定金在结算时冲抵车款。
+
乙方选择
第四条 车辆交付
-
1、交车时间:合同签订之日起 XX天内。
+
1、交车时间:合同签订之日起 天内。
+
2、提车方式:乙方自提
-
3、交车地点 :    XX市XX区/县 XXXX店  先按当前门店  。
+
3、交车地点 :
4、交车时里程表记录小于100公里
5、备注:如乙方购买续航里程为300KM以上车辆,上牌性质是公户或营运车辆需确保购买车辆自行驶证注册日期起1年之内行驶2万公里,且至车辆注册之日起2年内不得过户,如因违反此条约使甲方在申领国补过程中造成的损失,一切责任及后果由乙方承担。
第五条 车辆确认
diff --git a/api/views/wxapp/licheb/protocol/car_ck.php b/api/views/wxapp/licheb/protocol/car_ck.php index c1c4f53d..65bf4b47 100644 --- a/api/views/wxapp/licheb/protocol/car_ck.php +++ b/api/views/wxapp/licheb/protocol/car_ck.php @@ -12,15 +12,14 @@
车辆信息确认单
-
甲方(出卖人):(根据门店所属公司)
-
统一社会信用代码:(根据门店所属公司)
-
乙方(买受人):(用户真实姓名)
-
(证件类型):证件号码
-
联系电话:(用户手机号码)
-
#假如有委托人#
-
委托代理人:(委托人真实姓名)
-
(证件类型):委托人证件号码
-
联系电话:(委托人手机号码)
+
甲方(出卖人):
+
统一社会信用代码:
+
乙方(买受人):
+
联系电话:
+ +
委托代理人:
+
委托人身份证:
+
乙方于在甲方购买 车辆 1 台,并确认车架号为:。乙方已对上述车辆的厂牌型号、配置等进行认真检查并验收合格无异议。
diff --git a/api/views/wxapp/licheb/protocol/car_fh.php b/api/views/wxapp/licheb/protocol/car_fh.php index 9e17fe01..72f712b8 100644 --- a/api/views/wxapp/licheb/protocol/car_fh.php +++ b/api/views/wxapp/licheb/protocol/car_fh.php @@ -12,15 +12,15 @@
车辆交付表
-
甲方(出卖人):(根据门店所属公司)
-
统一社会信用代码:(根据门店所属公司)
-
乙方(买受人):(用户真实姓名)
-
(证件类型):证件号码
-
联系电话:(用户手机号码)
-
#假如有委托人#
-
委托代理人:(委托人真实姓名)
-
(证件类型):委托人证件号码
-
联系电话:(委托人手机号码)
+
甲方(出卖人):
+
统一社会信用代码:
+
乙方(买受人):
+
身份证号码:
+
联系电话:
+ +
委托代理人:
+
委托人身份证:
+
一、车辆信息
@@ -29,12 +29,12 @@ 品牌: @@ -58,8 +58,8 @@
- 车系: + 车系:
- 车型: + 车型: 车身颜色: @@ -42,15 +42,15 @@
- 内饰颜色: + 内饰颜色: - 车架号:XW111111111111 + 车架号:
- 车牌号:闽D232333 + 车牌号:
二、结算信息
-
应付款合计 50100 ,其中包含:车辆价款 50000,委托代办款项 3000
-
已付款合计 50000
+
应付款合计 ,其中包含:车辆价款 ,委托代办款项
+
已付款合计
三、证件资料交接信息
证件信息由后台配置,销售交付时钩选才显示:
diff --git a/common/composer.json b/common/composer.json index 3cef7554..d9ccf189 100644 --- a/common/composer.json +++ b/common/composer.json @@ -1,6 +1,7 @@ { "require": { "gregwar/image": "^2.1", - "tencentcloud/ocr": "^3.0" + "tencentcloud/ocr": "^3.0", + "wechatpay/wechatpay-guzzle-middleware": "^0.2.2" } } diff --git a/common/composer.lock b/common/composer.lock index 3311f081..6d64f719 100644 --- a/common/composer.lock +++ b/common/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6f7afccbfb645077ee3dcb371ebf6039", + "content-hash": "4f7bbe710bfa187154b75afba695ea66", "packages": [ { "name": "gregwar/cache", @@ -908,6 +908,57 @@ "description": "TencentCloudApi php sdk ocr", "homepage": "https://github.com/tencentcloud-sdk-php/ocr", "time": "2021-07-16T01:13:52+00:00" + }, + { + "name": "wechatpay/wechatpay-guzzle-middleware", + "version": "0.2.2", + "source": { + "type": "git", + "url": "https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware.git", + "reference": "6782ac33ed8cf97628609a71cdc5e84a6a40677a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wechatpay-apiv3/wechatpay-guzzle-middleware/zipball/6782ac33ed8cf97628609a71cdc5e84a6a40677a", + "reference": "6782ac33ed8cf97628609a71cdc5e84a6a40677a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-openssl": "*", + "php": ">=5.5" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.3" + }, + "suggest": { + "ext-bcmath": "Require bcmath in php 5.* version.", + "guzzlehttp/guzzle": "For using wechatpay guzzle middleware." + }, + "bin": [ + "tool/CertificateDownloader.php" + ], + "type": "library", + "autoload": { + "psr-4": { + "WechatPay\\GuzzleMiddleware\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "WechatPay API V3 Guzzle Middleware", + "homepage": "https://wechatpay-api.gitbook.io/wechatpay-api-v3/", + "keywords": [ + "wechatpay" + ], + "time": "2021-03-05T03:09:29+00:00" } ], "packages-dev": [], diff --git a/common/helpers/comm_helper.php b/common/helpers/comm_helper.php index 254bcd2e..f72682c3 100644 --- a/common/helpers/comm_helper.php +++ b/common/helpers/comm_helper.php @@ -1039,3 +1039,16 @@ if(!function_exists('convert_url_query')){ return $params; } } +if(!function_exists('create_contract_no')){ + /** + * 生成合同编号 + * int $city_id + * int $id 合同id + * int $type 合同类型 (0整车合同 1代理协议 2确定信息 3交接信息) + */ + function create_contract_no($city_id = 350200, $id, $type = 0) + { + return $city_id . $type . $id . date('Ymd') . sprintf("%06d", rand(1,999999)); + } + +} diff --git a/common/libraries/Pdf.php b/common/libraries/Pdf.php index efdc163e..b6122464 100644 --- a/common/libraries/Pdf.php +++ b/common/libraries/Pdf.php @@ -45,6 +45,7 @@ class Pdf { $pdf->setPrintHeader(false); $pdf->setPrintFooter(false); $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED); + $pdf->setCellHeightRatio(1.1); //设置行高 $pdf->SetMargins(PDF_MARGIN_LEFT, 5,PDF_MARGIN_RIGHT); $pdf->SetHeaderMargin(PDF_MARGIN_HEADER); diff --git a/common/libraries/WechatPayV3.php b/common/libraries/WechatPayV3.php new file mode 100644 index 00000000..d54d0974 --- /dev/null +++ b/common/libraries/WechatPayV3.php @@ -0,0 +1,90 @@ +merchantId = $config['merchantId']; //商户号 + $this->merchantSerialNumber = $config['merchantSerialNumber']; //商户API证书序列号 + $this->merchantPrivateKey = $config['merchantPrivateKey']; //商户证书路径 + $this->wechatpayCertificate = $config['wechatpayCertificate']; //微信支付平台证书 + } + + public function unifiedOrder($json,$appid,$noncestr){ + $url = 'https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi'; + // 商户相关配置 + //$merchantId = '1612096731'; // 商户号 + //$merchantSerialNumber = '761590F1FF6DFC2466894F96E2DE1169CE644A74'; // 商户API证书序列号 + $merchantPrivateKey = PemUtil::loadPrivateKey($this->merchantPrivateKey); // 商户私钥 + // 微信支付平台配置 + $wechatpayCertificate = PemUtil::loadCertificate($this->wechatpayCertificate); // 微信支付平台证书 + + // 构造一个WechatPayMiddleware + $wechatpayMiddleware = WechatPayMiddleware::builder() + ->withMerchant($this->merchantId, $this->merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置 + ->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array + ->build(); + + // 将WechatPayMiddleware添加到Guzzle的HandlerStack中 + $stack = GuzzleHttp\HandlerStack::create(); + $stack->push($wechatpayMiddleware, 'wechatpay'); + + // 创建Guzzle HTTP Client时,将HandlerStack传入 + $client = new GuzzleHttp\Client(['handler' => $stack]); + + // 接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签 + try { + $resp = $client->request('POST', $url, [ + 'json' => $json, + 'headers' => [ 'Accept' => 'application/json' ] + ]); + + $body = json_decode($resp->getBody(),true); + if(!$body['prepay_id']){ + return ['code'=>0,'msg'=>'下单失败']; + } + //微信支付(小程序)签名 + $timeStamp = time(); + $str = $this->getWechartSign($appid,$timeStamp,$noncestr,'prepay_id='.$body['prepay_id']); + $data = array('appid'=>$appid,'timeStamp'=>"$timeStamp",'package'=>'prepay_id='.$body['prepay_id'],'paySign'=>$str,'nonceStr'=>$noncestr,'signType'=>'RSA'); + + return ['code'=>1,'data'=>$data]; + } catch (RequestException $e) { + // 进行错误处理 + //echo $e->getMessage()."\n"; + $error_msg = $e->getMessage(); + if ($e->hasResponse()) { + //echo $e->getResponse()->getStatusCode().' '.$e->getResponse()->getReasonPhrase()."\n"; + //echo $e->getResponse()->getBody(); + } + return ['code'=>0,'msg'=>$error_msg]; + } + } + //调起支付的签名 + private function getWechartSign($appid,$timeStamp,$noncestr,$prepay_id){ + $str = $appid."\n".$timeStamp."\n".$noncestr."\n".$prepay_id."\n"; + $key = file_get_contents($this->merchantPrivateKey); + $str = $this->getSha256WithRSA($str,$key); + return $str; + } + + private function getSha256WithRSA($content, $privateKey){ + $binary_signature = ""; + $algo = "SHA256"; + openssl_sign($content, $binary_signature, $privateKey, $algo); + $sign = base64_encode($binary_signature); + return $sign; + } + +} diff --git a/common/libraries/receiver/Orders_entity.php b/common/libraries/receiver/Orders_entity.php index d3a53468..d0da3e6b 100644 --- a/common/libraries/receiver/Orders_entity.php +++ b/common/libraries/receiver/Orders_entity.php @@ -6,6 +6,7 @@ class Orders_entity{ private $ci; + const SRV_MCH_ID = '1612636924'; //收取服务费商户号 厦门狸车服务 public function __construct(){ $this->ci = & get_instance(); @@ -22,11 +23,11 @@ class Orders_entity{ switch($type){ case 0; $path = '/wxapp/licheb/protocol/car'; - $title = '车辆整车合同'; + $title = '车辆买卖合同'; break; case 1: $path = '/wxapp/licheb/protocol/agent'; - $title = '委托代理服务协议'; + $title = '委托服务协议'; break; case 2: $path = '/wxapp/licheb/protocol/car_ck'; @@ -34,7 +35,7 @@ class Orders_entity{ break; case 3: $path = '/wxapp/licheb/protocol/car_fh'; - $title = '车辆交接信息'; + $title = '车辆交付表'; break; default: } @@ -46,9 +47,9 @@ class Orders_entity{ /** * 签完成协议后操作 - * @param $oid int 订单id + * @param $oid int 订单id * @param $app_id int 小程序id - * @param $userinfo array() 小程序用户信息 + * @param $userinfo array 小程序用户信息 */ public function sign_after($oid,$app_id,$userinfo){ $this->ci->load->model('receiver/order/receiver_orders_model','orders_model'); @@ -79,16 +80,19 @@ class Orders_entity{ } }else{ $this->ci->signs_model->update(['status'=>1],['o_id'=>$row['id']]); - $res = $this->c_order($row,$app_id,$userinfo); + $res = $this->c_order(self::SRV_MCH_ID,$row,$app_id,$userinfo); } return $res; } /** * 创建定金消费订单 - * @param $oder array 订单数据 + * @param $mch_id string 微信商户号 + * @param $oder array 订单数据 + * @param $app_id int 应用id + * @param $userinfo array 用户信息 * return boolean */ - public function c_order($order,$app_id,$userinfo){ + public function c_order($mch_id,$order,$app_id,$userinfo){ $this->ci->load->model('apporder/order_purchase_model'); $this->ci->load->model('auto/auto_series_model'); $this->ci->load->model('auto/auto_brand_model'); @@ -107,6 +111,7 @@ class Orders_entity{ 'app_id' => $app_id, 'app_uid' => $userinfo['uid'], 'sid' => $sid, + 'mch_id' => $mch_id, 'item_id' => $order['id'], 'item_title' => $brand['name'].$series['name'], 'item_num' => 1, @@ -244,6 +249,120 @@ class Orders_entity{ } return $res; } + + //确认车辆完成创建两个支付订单 + public function check_finish($oid,$app_id,$userinfo){ + $this->ci->load->model('receiver/order/receiver_orders_model','orders_model'); + $this->ci->load->model('apporder/order_purchase_model'); + $this->ci->load->model('auto/auto_series_model'); + $this->ci->load->model('auto/auto_brand_model'); + $this->ci->load->model('auto/auto_cars_model'); + $this->ci->load->model("biz/biz_model"); + $this->ci->load->model('receiver/receiver_service_package_model','package_model'); + $this->ci->load->model('receiver/receiver_services_model','services_model'); + $this->ci->load->model("sys/sys_company_model"); + $this->ci->load->model('sys/sys_city_model'); + + $row = $this->ci->orders_model->get(['id'=>$oid]); + if(!$row){ + return false; + } + $brand = $this->ci->auto_brand_model->get(['id'=>$row['brand_id']],'name'); + $series = $this->ci->auto_series_model->get(['id'=>$row['s_id']],'name'); + $car_json = json_decode($row['car_json'],true); + $color = isset($car_json['color']) ? $car_json['color'] : ''; + + $jsondata['car'] = $car_json; + if($color['jsondata']['img']){ + $jsondata['cover'] = $color['jsondata']['img']; + } + //获取门店信息 + $biz = $this->ci->biz_model->get(['id'=>$row['biz_id']],'company_id,city_id'); + $company = $this->ci->sys_company_model->get(['id'=>$biz['company_id']]); + //获取服务包 + $packs = $this->ci->package_model->get(['id'=>$row['pack_id']],'srv_ids'); + $services = []; + if($packs['srv_ids']){ + $services = $this->ci->services_model->select(["id in ({$packs['srv_ids']})"],'','','','title,field_name'); + } + $attrs = "{$row['cor_id']}_{$row['v_id']}_{$row['incor_id']}"; + $car = $this->ci->auto_cars_model->get(['attrs'=>$attrs,'s_id'=>$row['s_id']]); + //获取挂牌价 + $city = $this->ci->sys_city_model->get(['city_id'=>$biz['city_id']],'fee_carno'); + $car['fee_carno'] = $city['fee_carno']; + $srv_price = 0; + if($services){ + foreach($services as $key=>$val){ + $field_arr = explode('.',$val['field_name']); + if($car[$field_arr[1]]){ + $srv_price += $car[$field_arr[1]]; + } + } + } + if($row['payway']){ //全款 + $to_com_price = $car['price']; //给销售公司金额 裸车价格 + $to_srv_price = $car['price'] + $srv_price - $to_com_price - $row['deposit'] - $car['price_insure']; //给服务公司金额 裸车价格+服务费-给销售公司金额-定金-保险 + $to_srv_price = $to_srv_price>0 ? $to_srv_price : 0; + }else{ //分期 + $to_com_price = $car['first_pay']; //给销售公司金额 + $to_srv_price = $car['first_pay'] + $srv_price - $to_com_price - $row['deposit'] - $car['price_insure']; //给服务公司的金额 首付+服务费-给销售公司-定金-保险 + $to_srv_price = $to_srv_price>0 ? $to_srv_price : 0; + } + $add_data = []; + if($to_srv_price>0){ + $order_type = 5; + $sid = create_order_no(350200,'liche',1,$order_type); + $add_data[] = [ + 'app_id' => $app_id, + 'app_uid' => $userinfo['uid'], + 'sid' => $sid, + 'mch_id' => self::SRV_MCH_ID, + 'item_id' => $row['id'], + 'item_title' => $brand['name'].$series['name'], + 'item_num' => 1, + 'type' => $order_type, + 'item_price' => $to_srv_price, + 'total_price' => $to_srv_price, + 'uname' => $userinfo['nickname'], + 'mobile' => $userinfo['mobile'], + 'payway' => 1, + 'status' => 1, + 'status_detail' => 11, + 'jsondata' => json_encode($jsondata,JSON_UNESCAPED_UNICODE), + 'c_time' => time(), + 'cf_id' => $row['id'] + ]; + } + if($to_com_price>0){ + $order_type = 6; + $sid = create_order_no(350200,'liche',1,$order_type); + $add_data[] = [ + 'app_id' => $app_id, + 'app_uid' => $userinfo['uid'], + 'sid' => $sid, + 'mch_id' => $company['wx_mchid'], + 'item_id' => $row['id'], + 'item_title' => $brand['name'].$series['name'], + 'item_num' => 1, + 'type' => $order_type, + 'item_price' => $to_com_price, + 'total_price' => $to_com_price, + 'uname' => $userinfo['nickname'], + 'mobile' => $userinfo['mobile'], + 'payway' => 1, + 'status' => 1, + 'status_detail' => 11, + 'jsondata' => json_encode($jsondata,JSON_UNESCAPED_UNICODE), + 'c_time' => time(), + 'cf_id' => $row['id'] + ]; + } + $result = false; + if($add_data){ + $result = $this->ci->order_purchase_model->add_batch($add_data); + } + return $result; + } } ?> diff --git a/common/libraries/receiver/Sign_entity.php b/common/libraries/receiver/Sign_entity.php index 6119aea4..58488ec6 100644 --- a/common/libraries/receiver/Sign_entity.php +++ b/common/libraries/receiver/Sign_entity.php @@ -9,8 +9,9 @@ class Sign_entity{ private $ci; private $comp_img = 'https://qimg.haodian.cn/hdi/2021/07/f2308e9066d290eb/79063515aefc302f.png'; //公司印章图片地址 - public function __construct(){ + public function __construct($params=[]){ $this->ci = & get_instance(); + $params['comp_img'] && $this->comp_img = $params['comp_img']; } /** @@ -63,60 +64,4 @@ class Sign_entity{ } } - /** - * 图片签名 - * @param $orgin_url string 签名图片地址 - * @param $user_image base64 用户签名文件对象 - * @param $width int 用户签名x坐标 - * @param $height int 用户签名y坐标 - * @param $o_width int 公司签名x坐标 - * @param $o_height int 公司签名y坐标 - * @param $need_c boolean 是否需要公司章 - * @param $s_path string 图片保存地址 - * return string 返回合成后图片地址 - */ - public function test($origin_url,$user_file,$width,$height,$o_width='',$o_height='',$need_c=true,$s_path='' ){ - - $user_file = "iVBORw0KGgoAAAANSUhEUgAAAXcAAAKdCAMAAADvKdm1AAAAAXNSR0IB2cksfwAAAFFQTFRFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXKkVQgAAABt0Uk5TADCA/99A78/Q8CAQwKBwv+BQkLBgr59/P2+PuKEyZQAAC7pJREFUeJzt3ely6zYMhuHKuy0v8ZJz2t7/hdaCvCTxJskkPvXgff51ppPSGJUEQVD66y8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+J8oCvUIQhoMh6OxehABTYbD4VQ9iIBmx7jP1YMIqHreB+pBxLM4hn1YqkcRz/gY9qV6EAEd05nhSj2IgFZM7xLLY9xJ392V1bK6UI8inhXZu0JRPe5r9Sji2RzD/qEeRDzbIauqwohNk8KAZEahrEpiO/Uo4qke9zklMW8lFWAJHncJHncNHncJHncNHncJHncNHncJHncNHncNHneJMY+7xI6mGYnqvGOrHkQ8BT2REutj2PfqQQQ0Pcb9oB5EPAuOVSUOx7DP1IMIaE+PmIK1onJ30t2W3jyJFV0zClYCpifSHR3vEnS8a9DxLnGg412hHFESU6DjXcIqYpyruvusFlUOPLxVx9kU3v1xjUxiTSFSwQozdG+0keRVgwealVqyrLtXRiG6bybqMN+aRAj8TB3lOyK8crJczdVhvkFDQjPVcUeq3H07G35EmGcSsMoMsXJXpUQcd7izPROVGXfsmTSqihh1d3f2HjGOmdxVTQRUxNxZzwwFYHcp90xobHHa2scoIvbHpYw8Ya5xtLiWkUek8H4+v1QRN+rBxGG5e9UAXF2O58KBl/LavFF92oAijZPdNXcvP1hbvViL2LkyU1THVhEO6PSm3+ruB6Z4H5a6f7lmsOfYycPiZ4tYPcWTxWe2uTnuKMji8zvcmVUGXxda5GCHqjd1yCVF4czshvDNKVM5p1CT1fjBjGKFg0//8QRhBYK73XQ76gUZDR7P41Wf64Rj7iyKJ1tTqxeQTGZh7yF4tHquz7VhJLZ+XhBYMtNkYan7k/cQLJhpsvh81Q/JTJPD+HVUmWkyeJi6XzHTpPckdb9ipklt0exUiZkmsduq+13MNGltm57lMdOkdL/qfhczTUK75nc7FvR1JFO0Ocdbc+iXyveGmVf2HPqlsf7RMPMCh35p2CzT5r2cVlH4lW08QVifUrv7wTs6yN5WTtufnJYzOsje9dklPaGD7F02ZbS/Hsy29T3WlTfrMGPQQfYOmy86vXODbesb6lSm21O75TpCV11SmatVu90WLjqlMhdcR+ioYypzMaZXtYvOqczFgF7V9or25YEbM5LJtqzjumMqc2HJJJeK29gkmSNsrpqytjaW6n0PNsWzfWrKntMkb7FaUYtvztbURN8GmnHc2lSSNfXyx2Zkkw2lWVPP7AIOHTWvpX6HUsHa2kS6NfXbX2SKfy7lmnq2pzT5StI19fJHP+ioeSHtmnpGafKFXO8lpDT5VPo19Yxr9E/kWFNPOOd+LMuaekYy+VCeNfWMZPKBzO/6JZm8L9+aekIyeY9FJe934UkmbxVvtIY1xjn3T/X3C3I/i5xz//BeR15ztoawtp7VYff4CNaaBoMvfnmF/XTOTVJjLOxe31je02Bwsn67EbKN+pybKzj1Wuf4HVXbt5LG12H3zO2swSB6Gm9hdw6CNRjETuPrsHv/Tx8+jdeEPXwarwp78DReF/bQabwy7IHT+MKlBPlQ1DTeDrGVvztoGp/3ELuJkGl8Hz5YG/DWWfZD7EbC3To75D/EbiTYrTP/WtgjoW6dSRP37yLdOutR2CPdOutV2MO8em877VfYT7XJ3+pR5DbqW9hPr96b/uGdwj2silgX2TGd1Ke1GW1nw3nf3tx7qAM/+f1HR76HSts/HSP/h082/TO2PH44Uo8jnvW8fytPCCWB19j2L8WNwXbSvGHVH3dcRQZRimQ9U/JZRQ2+TyGyDHbi2hdVkWyuHkREh+GcKg0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBmfxfqEYS04+t6CvZlePUgAqq+VL9UDyIe+9QYnwByNzmGfa8eRDzjY9jnfDvV3TGZGa7UgwhodIz7Vj2IeBbVqso0427Nqiqx5Hu1CmU1zfBhbHfVl+Bn6kEEtDrGfaceRDxltVmlCOyummb4Kra/atM0UA8ingObJokNtRmFMcm7xJ4agcKCgyaJFeeqClaaoX/D3eAY9g/1IOKxEgGPu7sD59kSlAgk2DNpUCKQYM+kwZ5JgiRSgz2TBkmkBHsmDZJICfZMGiSREuyZNEgiNdgzSZBEakxpAVYoSCIlVjQrKVglknuT7tYkkRJUIiXGdLxLrKhEKlCa0aA0ozGiNKOwpTQjwfmehK2q3M52t+N8T4EmMY0VLyNQsMI7r1Zyt2HPpHCgRKBQjkhmFAYcqyosJpx3KHxSmVEY8y4CiSmviVQgh5Swwgz1X3dVHXJODunNyu7kkO6sMEMO6c1ySDoi3VGYkRhwqKqwIIeUoDAjseVwT4HTDo0di6rCmJ2qxJQWAoUB5V8FUneNDam7woF6mIIdMpG6u1txyKRA6i5hBQI6N9xRdZewOwa8VMkdR9kSa1J3BSsQTP/meXdWlSErIx55T4Ph2YRH3o/tmPbLIRsnV7ZjmpeL1YwH3tOv82FHOeeB97O97piqeX5CZcyFpZCnuow98J/a8USx+VqXYfvkZf29PaxaWkcsrdlZOWz5458pj+VWTn+eZNMv5mF3M5+XH1WlRjWeIGyj+qNfhn7g7KyB4KbovqdnLLPPu3P5okriJ0zx2awf1MGsfYnA52Ip490GAgv8lCw+C6sPPGhTWhH4XCxzf/iCGQv8xnVAQXw+P+SwQ5BfjuMJYncnc/+qnBH4DGzlnD2bwOvA86KrpCyVeXHBoJgT+MTqVOZVgl6wf0rreSpzZdUbivHJvEhlrg5kkwm9SmV+/qucgiTxOpX5Ysnamki9pjadtO0UhLU1gU2TVObKkhrW1rcN2p4msbamYBumdt9sZm1N4KZ9oAHW1rfdtg80YGsrgX/DvfaBBqjUvMf63Lvc2bO1lcB3delzb43Av2H7RmJC4Dsrv/S5t0fgu7rfpNQYge/mUZNSYwS+i+L9dykR+Pbqq5Jv3hgj8K1tkkSMwLeUqrZF4FuxcCV5UV4deC78NVLcvV7QDT3ajdVraqpQEfim0qypFwS+meTnRXXgOXJ9Lt2aesGthNfsqCP1e/II/CtFow7U1gj8c9aklGOfY3dceWHKA00bfzv8Za6DPFaHPc9nIwj8Y7/yhZ3AP2Zhz/di8ZLujrvWbfqtu6Ct5h7bL+X9Ng2Bv9Xs6tibCPyNb2/Jy2bLOch3P96Slw0HUN8s3L7EROC/2iToHmiIwF95zTKGwJ9lKLm//M8R+FPm7vhWawJfEUSBwItiQOBFEYgeeNnvjx144a+PHHjpb48bePEvjxr4f9S/O2TgFxv9rw4X+O35u4bi3xwt8KM66nP5L64D/696GF7sjRnDZQ++NFYH/jNIC992dnzY+/FNjoOduU578AgEUx92T/hAiLe6kyzQ6toXpbVp08Pnzw4ah5sgq2uPbG2SH3H7zFthqe2EG8beyvoz6f+oxxHPaXVlkvdW7125fuZuXG+hWF29LWwLNWEL5a3cxypQ9seOSV7jwCSvUW+hyOTdnbZQZPLuBkMOQyTqOhnlGndFfRjyWz2OcE6HIVFOvHukPgyhJu/uVK6hauCtZJIXoSYvQk1epODEW+NUk2cL5e1UriGtcUc/mciBfjKNLd3aGqd+MtIab3Rri5wKlATe3YC0RuNA4DXqyjD5pLu6WkOZzF197joln/RW55Mk8u7qMhn3Xf2RyIusmGo0DmQ1Ggc2UBorpngJSyeZ4v3ZzpUp3h9TvAhTvAZTvIhN8SOmeHc2xW/Uowgo+TfY0cyStVWi/GBtlWD7JML2SYTtk0a9fVKPIiCb4lla/VXvaOrHy71jqS5279WDCKhggteoMhr1GCIi7hrEXYO4axB3DeKu8TEcztRjiGg7++C1EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoLf+A6jRZVspl5i5AAAAAElFTkSuQmCC"; - - $arrContextOptions=array( - "ssl"=>array( - "verify_peer"=>false, - "verify_peer_name"=>false, - ), - ); - //临时保存用户签名图片 - if (!file_exists(FCPATH.'/temp')){ - $oldumask = umask(0); - mkdir(FCPATH.'/temp', 0777, true); - umask($oldumask); - } - $file_name = time().rand(1,9999999); - !$s_path && $s_path = FCPATH.'temp/'.md5('sign'.$file_name).'.jpg'; - $imgPath = FCPATH.'temp/'.md5($file_name).'.png'; - $res = file_put_contents($imgPath,base64_decode($user_file)); - if(!$res){ - return false; - } - $yh_image = Image::open($imgPath)->cropResize(150,150)->rotate(-90); - - - //原始签名文件 - $data = file_get_contents($origin_url,false,stream_context_create($arrContextOptions)); - $imgobj = Image::fromData($data); - if($need_c){ - !$o_height && $o_height = $height; - //公司 - $gs_data = file_get_contents($this->comp_img,false,stream_context_create($arrContextOptions)); - $gs_image = Image::fromData($gs_data)->cropResize(200,200); - $imgobj->merge($gs_image,$o_width,$o_height); - } - $imgobj->merge($yh_image,$width,$height)->save($s_path); - @unlink($imgPath); - if(file_exists($s_path)){ - return str_replace(FCPATH,'',$s_path); - }else{ - return false; - } - } } diff --git a/common/models/receiver/Receiver_service_package_model.php b/common/models/receiver/Receiver_service_package_model.php new file mode 100644 index 00000000..c12dd103 --- /dev/null +++ b/common/models/receiver/Receiver_service_package_model.php @@ -0,0 +1,13 @@ +table_name, 'default'); + } +} diff --git a/common/models/receiver/Receiver_services_model.php b/common/models/receiver/Receiver_services_model.php new file mode 100644 index 00000000..d1bfbe44 --- /dev/null +++ b/common/models/receiver/Receiver_services_model.php @@ -0,0 +1,13 @@ +table_name, 'default'); + } +} diff --git a/common/models/receiver/order/Receiver_order_ckcars_model.php b/common/models/receiver/order/Receiver_order_ckcars_model.php index b46b5714..07a7706a 100644 --- a/common/models/receiver/order/Receiver_order_ckcars_model.php +++ b/common/models/receiver/order/Receiver_order_ckcars_model.php @@ -11,7 +11,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); class Receiver_order_ckcars_model extends HD_Model { private $table_name = 'lc_receiver_order_ckcars'; - private $status_arr = [ 0 => '车辆确认中',1 => '用户未签名',2=>'已确认']; + private $status_arr = [ 0 => '车辆确认中',1 => '用户未签名',2 => '尾款未支付' ,3 => '已确定']; public function __construct() { diff --git a/common/services/apporder/Payment_service.php b/common/services/apporder/Payment_service.php index dddb4b78..b65b496a 100644 --- a/common/services/apporder/Payment_service.php +++ b/common/services/apporder/Payment_service.php @@ -19,6 +19,8 @@ class Payment_service extends HD_Service{ $this->load->model('receiver/order/receiver_orders_model','orders_model'); $this->load->model('receiver/order/receiver_order_signs_model','order_signs_model'); + $this->load->model('receiver/order/receiver_order_bills_model','bills_model'); + $this->load->model('receiver/order/receiver_order_ckcars_model','ckcars_model'); } @@ -30,12 +32,12 @@ class Payment_service extends HD_Service{ public function after_pay($sid,$pay_price = ''){ if($sid){ debug_log("[start] ". __FUNCTION__ . ": sid:".$sid, $this->log_file); - $order = $this->purchase_model->get(array('sid'=>$sid,'app_id'=>$this->app_id,'status'=>1)); + $order = $this->purchase_model->get(array('sid'=>$sid,'app_id'=>$this->app_id)); if(!$order){ debug_log("[error] ". __FUNCTION__ . ":{$sid}_订单不存在", $this->log_file); return array('code'=>0,'msg'=>'订单不存在'); } - if(!$order['status']>1){ + if($order['status']>1){ debug_log("[error] ". __FUNCTION__ . ":{$sid}_订单已支付", $this->log_file); return array('code'=>0,'msg'=>'订单已支付'); } @@ -92,6 +94,29 @@ class Payment_service extends HD_Service{ return array('code'=>0,'msg'=>'更新失败'); } break; + case 5: //委托服务费 + case 6: //首付或尾款 + $upd = array('status'=>2,'status_detail'=>21,'pay_time'=>date('Y-m-d H:i:s')); + $pay_price && $upd['pay_price'] = $pay_price; + $res = $this->purchase_model->update($upd,array('id'=>$order['id'])); + if($res){ + $nopay = $this->purchase_model->count(['app_id'=>$this->app_id,'cf_id'=>$order['item_id'],'type in (5,6)'=>null,'status'=>1]); //未支付的尾款或者服务费 + if(!$nopay){ + //更新订单状态 + $row = $this->orders_model->get(['id'=>$order['item_id']]); + if($row){ + $this->ckcars_model->update(['status'=>3],['o_id'=>$row['id']]); + $this->orders_model->update(['status'=>3],['id'=>$row['id']]); + if(!$this->bills_model->count(['o_id'=>$row['id']])){ + $this->bills_model->add(['o_id'=>$row['id'],'c_time'=>time()]); + } + } + } + return array('code'=>1,'msg'=>'操作成功'); + }else{ + return array('code'=>0,'msg'=>'更新失败'); + } + break; default: debug_log("[error] ". __FUNCTION__ . ":{$item['type']}_未知商品类型", $this->log_file); return array('code'=>0,'msg'=>'未知商品类型'); diff --git a/common/vendor/bin/CertificateDownloader.php b/common/vendor/bin/CertificateDownloader.php new file mode 120000 index 00000000..8a1312ec --- /dev/null +++ b/common/vendor/bin/CertificateDownloader.php @@ -0,0 +1 @@ +../wechatpay/wechatpay-guzzle-middleware/tool/CertificateDownloader.php \ No newline at end of file diff --git a/common/vendor/composer/autoload_psr4.php b/common/vendor/composer/autoload_psr4.php index 3458ee1d..b216aa6b 100644 --- a/common/vendor/composer/autoload_psr4.php +++ b/common/vendor/composer/autoload_psr4.php @@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'WechatPay\\GuzzleMiddleware\\' => array($vendorDir . '/wechatpay/wechatpay-guzzle-middleware/src'), 'TencentCloud\\' => array($vendorDir . '/tencentcloud/common/src/TencentCloud', $vendorDir . '/tencentcloud/ocr/src/TencentCloud'), 'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'), 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), diff --git a/common/vendor/composer/autoload_static.php b/common/vendor/composer/autoload_static.php index 05f2ef3a..f6396a8e 100644 --- a/common/vendor/composer/autoload_static.php +++ b/common/vendor/composer/autoload_static.php @@ -19,6 +19,10 @@ class ComposerStaticInitd0872984a1db7aa104ae1184a3170d3e ); public static $prefixLengthsPsr4 = array ( + 'W' => + array ( + 'WechatPay\\GuzzleMiddleware\\' => 27, + ), 'T' => array ( 'TencentCloud\\' => 13, @@ -43,6 +47,10 @@ class ComposerStaticInitd0872984a1db7aa104ae1184a3170d3e ); public static $prefixDirsPsr4 = array ( + 'WechatPay\\GuzzleMiddleware\\' => + array ( + 0 => __DIR__ . '/..' . '/wechatpay/wechatpay-guzzle-middleware/src', + ), 'TencentCloud\\' => array ( 0 => __DIR__ . '/..' . '/tencentcloud/common/src/TencentCloud', diff --git a/common/vendor/composer/installed.json b/common/vendor/composer/installed.json index f7e69ad3..f589d125 100644 --- a/common/vendor/composer/installed.json +++ b/common/vendor/composer/installed.json @@ -929,5 +929,58 @@ ], "description": "TencentCloudApi php sdk ocr", "homepage": "https://github.com/tencentcloud-sdk-php/ocr" + }, + { + "name": "wechatpay/wechatpay-guzzle-middleware", + "version": "0.2.2", + "version_normalized": "0.2.2.0", + "source": { + "type": "git", + "url": "https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware.git", + "reference": "6782ac33ed8cf97628609a71cdc5e84a6a40677a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wechatpay-apiv3/wechatpay-guzzle-middleware/zipball/6782ac33ed8cf97628609a71cdc5e84a6a40677a", + "reference": "6782ac33ed8cf97628609a71cdc5e84a6a40677a", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-openssl": "*", + "php": ">=5.5" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.3" + }, + "suggest": { + "ext-bcmath": "Require bcmath in php 5.* version.", + "guzzlehttp/guzzle": "For using wechatpay guzzle middleware." + }, + "time": "2021-03-05T03:09:29+00:00", + "bin": [ + "tool/CertificateDownloader.php" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "WechatPay\\GuzzleMiddleware\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "WechatPay API V3 Guzzle Middleware", + "homepage": "https://wechatpay-api.gitbook.io/wechatpay-api-v3/", + "keywords": [ + "wechatpay" + ] } ] diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/.gitignore b/common/vendor/wechatpay/wechatpay-guzzle-middleware/.gitignore new file mode 100644 index 00000000..f86afcbc --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/.gitignore @@ -0,0 +1,9 @@ +# ref: https://github.com/github/gitignore/blob/master/Composer.gitignore + +composer.phar +/vendor/ +test/ + +# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +composer.lock \ No newline at end of file diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/LICENSE b/common/vendor/wechatpay/wechatpay-guzzle-middleware/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/README.md b/common/vendor/wechatpay/wechatpay-guzzle-middleware/README.md new file mode 100644 index 00000000..ed2d28f3 --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/README.md @@ -0,0 +1,262 @@ +# wechatpay-guzzle-middleware + +## 概览 + +[微信支付API v3](https://wechatpay-api.gitbook.io/wechatpay-api-v3/)的[Guzzle HttpClient](http://docs.guzzlephp.org/)中间件Middleware,实现了请求签名的生成和应答签名的验证。 + +如果你是使用Guzzle的商户开发者,可以在构造`GuzzleHttp\Client`时将`WechatPayGuzzleMiddleware`传入,得到的`GuzzleHttp\Client`实例在执行请求时将自动携带身份认证信息,并检查应答的微信支付签名。 + + + +## 项目状态 + +当前版本为`0.2.0`测试版本。请商户的专业技术人员在使用时注意系统和软件的正确性和兼容性,以及带来的风险。 + + + +## 环境要求 + +我们开发和测试使用的环境如下: + ++ PHP 5.5+ / PHP 7.0+ ++ guzzlehttp/guzzle 6.0+ + + + +## 安装 + +可以使用PHP包管理工具composer引入SDK到项目中: + +#### Composer + +方式一:在项目目录中,通过composer命令行添加: +```shell +composer require wechatpay/wechatpay-guzzle-middleware +``` + + +方式二:在项目的composer.json中加入以下配置: + +```json + "require": { + "wechatpay/wechatpay-guzzle-middleware": "^0.2.0" + } +``` +添加配置后,执行安装 +```shell +composer install +``` + + + +## 开始 + +首先,通过`WechatPayMiddlewareBuilder`构建一个`WechatPayMiddleware`,然后将其加入`GuzzleHttp\Client`的`HandlerStack`中。我们提供相应的方法,可以方便的传入商户私钥和微信支付平台证书等信息。 + +```php +use GuzzleHttp\Exception\RequestException; +use WechatPay\GuzzleMiddleware\WechatPayMiddleware; +use WechatPay\GuzzleMiddleware\Util\PemUtil; + +// 商户相关配置 +$merchantId = '1000100'; // 商户号 +$merchantSerialNumber = 'XXXXXXXXXX'; // 商户API证书序列号 +$merchantPrivateKey = PemUtil::loadPrivateKey('/path/to/mch/private/key.pem'); // 商户私钥 +// 微信支付平台配置 +$wechatpayCertificate = PemUtil::loadCertificate('/path/to/wechatpay/cert.pem'); // 微信支付平台证书 + +// 构造一个WechatPayMiddleware +$wechatpayMiddleware = WechatPayMiddleware::builder() + ->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置 + ->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array + ->build(); + +// 将WechatPayMiddleware添加到Guzzle的HandlerStack中 +$stack = GuzzleHttp\HandlerStack::create(); +$stack->push($wechatpayMiddleware, 'wechatpay'); + +// 创建Guzzle HTTP Client时,将HandlerStack传入 +$client = new GuzzleHttp\Client(['handler' => $stack]); + + +// 接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签 +try { + $resp = $client->request('GET', 'https://api.mch.weixin.qq.com/v3/...', [ // 注意替换为实际URL + 'headers' => [ 'Accept' => 'application/json' ] + ]); + + echo $resp->getStatusCode().' '.$resp->getReasonPhrase()."\n"; + echo $resp->getBody()."\n"; + + $resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/...', [ + 'json' => [ // JSON请求体 + 'field1' => 'value1', + 'field2' => 'value2' + ], + 'headers' => [ 'Accept' => 'application/json' ] + ]); + + echo $resp->getStatusCode().' '.$resp->getReasonPhrase()."\n"; + echo $resp->getBody()."\n"; +} catch (RequestException $e) { + // 进行错误处理 + echo $e->getMessage()."\n"; + if ($e->hasResponse()) { + echo $e->getResponse()->getStatusCode().' '.$e->getResponse()->getReasonPhrase()."\n"; + echo $e->getResponse()->getBody(); + } + return; +} +``` + +### 上传媒体文件 + +```php +// 参考上述指引说明,并引入 `MediaUtil` 正常初始化,无额外条件 +use WechatPay\GuzzleMiddleware\Util\MediaUtil; +// 实例化一个媒体文件流,注意文件后缀名需符合接口要求 +$media = new MediaUtil('/your/file/path/with.extension'); + +// 正常使用Guzzle发起API请求 +try { + $resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/[merchant/media/video_upload|marketing/favor/media/image-upload]', [ + 'body' => $media->getStream(), + 'headers' => [ + 'Accept' => 'application/json', + 'content-type' => $media->getContentType(), + ] + ]); + // POST 语法糖 + $resp = $client->post('merchant/media/upload', [ + 'body' => $media->getStream(), + 'headers' => [ + 'Accept' => 'application/json', + 'content-type' => $media->getContentType(), + ] + ]); + echo $resp->getStatusCode().' '.$resp->getReasonPhrase()."\n"; + echo $resp->getBody()."\n"; +} catch (Exception $e) { + echo $e->getMessage()."\n"; + if ($e->hasResponse()) { + echo $e->getResponse()->getStatusCode().' '.$e->getResponse()->getReasonPhrase()."\n"; + echo $e->getResponse()->getBody(); + } + return; +} +``` + +### 敏感信息加/解密 + +```php +// 参考上上述说明,引入 `SensitiveInfoCrypto` +use WechatPay\GuzzleMiddleware\Util\SensitiveInfoCrypto; +// 上行加密API 多于 下行解密,默认为加密,实例后直接当方法用即可 +$encryptor = new SensitiveInfoCrypto(PemUtil::loadCertificate('/path/to/wechatpay/cert.pem')); + +// 正常使用Guzzle发起API请求 +try { + // POST 语法糖 + $resp = $client->post('/v3/applyment4sub/applyment/', [ + 'json' => [ + 'business_code' => 'APL_98761234', + 'contact_info' => [ + 'contact_name' => $encryptor('value of `contact_name`'), + 'contact_id_number' => $encryptor('value of `contact_id_number'), + 'mobile_phone' => $encryptor('value of `mobile_phone`'), + 'contact_email' => $encryptor('value of `contact_email`'), + ], + //... + ], + 'headers' => [ + // 命令行获取证书序列号 + // openssl x509 -in /path/to/wechatpay/cert.pem -noout -serial | awk -F= '{print $2}' + // 或者使用工具类获取证书序列号 `PemUtil::parseCertificateSerialNo($certificate)` + 'Wechatpay-Serial' => 'must be the serial number via the downloaded pem file of `/v3/certificates`', + 'Accept' => 'application/json', + ], + ]); + echo $resp->getStatusCode().' '.$resp->getReasonPhrase()."\n"; + echo $resp->getBody()."\n"; +} catch (Exception $e) { + echo $e->getMessage()."\n"; + if ($e->hasResponse()) { + echo $e->getResponse()->getStatusCode().' '.$e->getResponse()->getReasonPhrase()."\n"; + echo $e->getResponse()->getBody(); + } + return; +} + +// 单例加解密示例如下 +$crypto = new SensitiveInfoCrypto($wechatpayCertificate, $merchantPrivateKey); +$encrypted = $crypto('Alice'); +$decrypted = $crypto->setStage('decrypt')($encrypted); +``` + +## 定制 + +当默认的本地签名和验签方式不适合你的系统时,你可以通过实现`Signer`或者`Verifier`来定制签名和验签。比如,你的系统把商户私钥集中存储,业务系统需通过远程调用进行签名,你可以这样做。 + +```php +use WechatPay\GuzzleMiddleware\Auth\Signer; +use WechatPay\GuzzleMiddleware\Auth\SignatureResult; +use WechatPay\GuzzleMiddleware\Auth\WechatPay2Credentials; + +class CustomSigner implements Signer +{ + public function sign($message) + { + // 调用签名RPC服务,然后返回包含签名和证书序列号的SignatureResult + return new SignatureResult('xxxx', 'yyyyy'); + } +} + +$credentials = new WechatPay2Credentials($merchantId, new CustomSigner); + +$wechatpayMiddleware = WechatPayMiddleware::builder() + ->withCredentials($credentials) + ->withWechatPay([ $wechatpayCertificate ]) + ->build(); +``` + + + +## 常见问题 + +### 如何下载平台证书? + +使用`WechatPayMiddlewareBuilder`需要调用`withWechatpay`设置[微信支付平台证书](https://wechatpay-api.gitbook.io/wechatpay-api-v3/ren-zheng/zheng-shu#ping-tai-zheng-shu),而平台证书又只能通过调用[获取平台证书接口](https://wechatpay-api.gitbook.io/wechatpay-api-v3/jie-kou-wen-dang/ping-tai-zheng-shu#huo-qu-ping-tai-zheng-shu-lie-biao)下载。为了解开"死循环",你可以在第一次下载平台证书时,按照下述方法临时"跳过”应答签名的验证。 + +```php +use WechatPay\GuzzleMiddleware\Validator; + +class NoopValidator implements Validator +{ + public function validate(\Psr\Http\Message\ResponseInterface $response) + { + return true; + } +} + +$wechatpayMiddleware = WechatPayMiddleware::builder() + ->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) + ->withValidator(new NoopValidator) // NOTE: 设置一个空的应答签名验证器,**不要**用在业务请求 + ->build(); +``` + +**注意**:业务请求请使用标准的初始化流程,务必验证应答签名。 + +### 证书和回调解密需要的AesGcm解密在哪里? + +请参考[AesUtil.php](src/Util/AesUtil.php)。 + +### 配合swoole使用时,上传文件接口报错 + +建议升级至swoole 4.6+,swoole在 4.6.0 中增加了native-curl([swoole/swoole-src#3863](https://github.com/swoole/swoole-src/pull/3863))支持,我们测试能正常使用了。 +更详细的信息,请参考[#36](https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware/issues/36)。 + +## 联系我们 + +如果你发现了**BUG**或者有任何疑问、建议,请通过issue进行反馈。 + +也欢迎访问我们的[开发者社区](https://developers.weixin.qq.com/community/pay)。 diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/composer.json b/common/vendor/wechatpay/wechatpay-guzzle-middleware/composer.json new file mode 100644 index 00000000..d5164b4a --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/composer.json @@ -0,0 +1,28 @@ +{ + "name": "wechatpay/wechatpay-guzzle-middleware", + "version": "0.2.2", + "description": "WechatPay API V3 Guzzle Middleware", + "type": "library", + "keywords": [ + "wechatpay" + ], + "homepage": "https://wechatpay-api.gitbook.io/wechatpay-api-v3/", + "license": "Apache-2.0", + "require": { + "php": ">=5.5", + "ext-openssl": "*" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.3" + }, + "autoload": { + "psr-4": { "WechatPay\\GuzzleMiddleware\\" : "src/" } + }, + "bin": [ + "tool/CertificateDownloader.php" + ], + "suggest": { + "ext-bcmath": "Require bcmath in php 5.* version.", + "guzzlehttp/guzzle": "For using wechatpay guzzle middleware." + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/CertificateVerifier.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/CertificateVerifier.php new file mode 100644 index 00000000..6947c43f --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/CertificateVerifier.php @@ -0,0 +1,70 @@ + publicKey) + */ + protected $publicKeys = []; + + /** + * Constructor + * + * @param array of string|resource $certifcates WechatPay Certificates (string - PEM formatted \ + * certificate, or resource - X.509 certificate resource returned by openssl_x509_read) + */ + public function __construct(array $certificates) + { + foreach ($certificates as $certificate) { + $serialNo = PemUtil::parseCertificateSerialNo($certificate); + $this->publicKeys[$serialNo] = \openssl_get_publickey($certificate); + } + } + + /** + * Verify signature of message + * + * @param string $serialNumber certificate serial number + * @param string $message message to verify + * @param string $signautre signature of message + * + * @return bool + */ + public function verify($serialNumber, $message, $signature) + { + $serialNumber = \strtoupper(\ltrim($serialNumber, '0')); // trim leading 0 and uppercase + if (!isset($this->publicKeys[$serialNumber])) { + return false; + } + if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) { + throw new \RuntimeException("当前PHP环境不支持SHA256withRSA"); + } + $signature = \base64_decode($signature); + return \openssl_verify($message, $signature, $this->publicKeys[$serialNumber], + 'sha256WithRSAEncryption'); + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/PrivateKeySigner.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/PrivateKeySigner.php new file mode 100644 index 00000000..5c6edf50 --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/PrivateKeySigner.php @@ -0,0 +1,72 @@ +certificateSerialNumber = $serialNumber; + $this->privateKey = $privateKey; + } + + /** + * Sign Message + * + * @param string $message Message to sign + * + * @return string + */ + public function sign($message) + { + if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) { + throw new \RuntimeException("当前PHP环境不支持SHA256withRSA"); + } + + if (!\openssl_sign($message, $sign, $this->privateKey, 'sha256WithRSAEncryption')) { + throw new \UnexpectedValueException("签名验证过程发生了错误"); + } + return new SignatureResult(\base64_encode($sign), $this->certificateSerialNumber); + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/SignatureResult.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/SignatureResult.php new file mode 100644 index 00000000..e963edd0 --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/SignatureResult.php @@ -0,0 +1,64 @@ +sign = $sign; + $this->certificateSerialNumber = $serialNumber; + } + + /** + * Get Signature + * + * @return string + */ + public function getSign() + { + return $this->sign; + } + + /** + * Get Certificate Serial Number + * + * @return string + */ + public function getCertificateSerialNumber() + { + return $this->certificateSerialNumber; + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/Signer.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/Signer.php new file mode 100644 index 00000000..094a131b --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/Signer.php @@ -0,0 +1,33 @@ +merchantId = $merchantId; + $this->signer = $signer; + } + + /** + * Get schema of credentials + * + * @return string + */ + public function getSchema() + { + return 'WECHATPAY2-SHA256-RSA2048'; + } + + /** + * Get token of credentials + * + * @param RequestInterface $request Api request + * + * @return string + */ + public function getToken(RequestInterface $request) + { + $nonce = $this->getNonce(); + $timestamp = $this->getTimestamp(); + + $message = $this->buildMessage($nonce, $timestamp, $request); + + $signResult = $this->signer->sign($message); + $sign = $signResult->getSign(); + $serialNo = $signResult->getCertificateSerialNumber(); + + $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', + $this->merchantId, $nonce, $timestamp, $serialNo, $sign + ); + return $token; + } + + /** + * Get Merchant Id + * + * @return string + */ + public function getMerchantId() + { + return $this->merchantId; + } + + /** + * Get sign timestamp + * + * @return integer + */ + protected function getTimestamp() + { + return \time(); + } + + /** + * Get nonce + * + * @return string + */ + protected function getNonce() + { + static $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charactersLength = strlen($characters); + $randomString = ''; + for ($i = 0; $i < 32; $i++) { + $randomString .= $characters[rand(0, $charactersLength - 1)]; + } + return $randomString; + } + + /** + * Build message to sign + * + * @param string $nonce Nonce string + * @param integer $timestamp Unix timestamp + * @param RequestInterface $request Api request + * + * @return string + */ + protected function buildMessage($nonce, $timestamp, RequestInterface $request) + { + $body = ''; + $bodyStream = $request->getBody(); + // non-seekable stream need to be handled by the caller + if ($bodyStream->isSeekable()) { + $body = (string)$bodyStream; + $bodyStream->rewind(); + } + + return $request->getMethod()."\n". + $request->getRequestTarget()."\n". + $timestamp."\n". + $nonce."\n". + $body."\n"; + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/WechatPay2Validator.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/WechatPay2Validator.php new file mode 100644 index 00000000..bdedae62 --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/WechatPay2Validator.php @@ -0,0 +1,120 @@ +verifier = $verifier; + } + + /** + * Validate Response + * + * @param ResponseInterface $response Api response to validate + * + * @return bool + */ + public function validate(ResponseInterface $response) + { + $serialNo = $this->getHeader($response, 'Wechatpay-Serial'); + $sign = $this->getHeader($response, 'Wechatpay-Signature'); + $timestamp = $this->getHeader($response, 'Wechatpay-TimeStamp'); + $nonce = $this->getHeader($response, 'Wechatpay-Nonce'); + + if (!isset($serialNo, $sign, $timestamp, $nonce)) { + return false; + } + + if (!$this->checkTimestamp($timestamp)) { + // log here + return false; + } + + $body = $this->getBody($response); + $message = "$timestamp\n$nonce\n$body\n"; + + return $this->verifier->verify($serialNo, $message, $sign); + } + + /** + * Build message to sign + * + * @param string $nonce Nonce string + * @param integer $timestamp Unix timestamp + * @param RequestInterface $request Api request + * + * @return string + */ + protected function getHeader(ResponseInterface $response, $name) + { + $values = $response->getHeader($name); + return empty($values) ? null : $values[count($values) - 1]; + } + + /** + * Check whether timestamp is valid + * + * @param integer $timestamp Unix timestamp + * + * @return bool + */ + protected function checkTimestamp($timestamp) + { + // reject responses beyond 5 minutes + return \abs((int)$timestamp - \time()) <= 300; + } + + /** + * Build message to sign + * + * @param string $nonce Nonce string + * @param integer $timestamp Unix timestamp + * @param RequestInterface $request Api request + * + * @return string + */ + protected function getBody(ResponseInterface $response) + { + $body = ''; + $bodyStream = $response->getBody(); + // non-seekable stream need to be handled by the caller + if ($bodyStream->isSeekable()) { + $body = (string)$bodyStream; + $bodyStream->rewind(); + } + return $body; + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Credentials.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Credentials.php new file mode 100644 index 00000000..93c6129f --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Credentials.php @@ -0,0 +1,39 @@ +aesKey = $aesKey; + } + + /** + * Decrypt AEAD_AES_256_GCM ciphertext + * + * @param string $associatedData AES GCM additional authentication data + * @param string $nonceStr AES GCM nonce + * @param string $ciphertext AES GCM cipher text + * + * @return string|bool Decrypted string on success or FALSE on failure + */ + public function decryptToString($associatedData, $nonceStr, $ciphertext) + { + $ciphertext = \base64_decode($ciphertext); + if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) { + return false; + } + + // ext-sodium (default installed on >= PHP 7.2) + if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && + \sodium_crypto_aead_aes256gcm_is_available()) { + return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); + } + + // ext-libsodium (need install libsodium-php 1.x via pecl) + if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && + \Sodium\crypto_aead_aes256gcm_is_available()) { + return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); + } + + // openssl (PHP >= 7.1 support AEAD) + if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) { + $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE); + $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE); + + return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr, + $authTag, $associatedData); + } + + throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php'); + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Util/MediaUtil.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Util/MediaUtil.php new file mode 100644 index 00000000..c9c96b11 --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Util/MediaUtil.php @@ -0,0 +1,146 @@ +filepath = $filepath; + $this->fileStream = $fileStream; + $this->composeStream(); + } + + /** + * Compose the GuzzleHttp\Psr7\FnStream + */ + private function composeStream() + { + $basename = \basename($this->filepath); + $stream = isset($this->fileStream) ? $this->fileStream : new LazyOpenStream($this->filepath, 'r'); + if (!$stream->isSeekable()) { + $stream = new CachingStream($stream); + } + + $json = \GuzzleHttp\json_encode([ + 'filename' => $basename, + 'sha256' => \GuzzleHttp\Psr7\hash($stream, 'sha256'), + ]); + $this->meta = $json; + + $multipart = new MultipartStream([ + [ + 'name' => 'meta', + 'contents' => $json, + 'headers' => [ + 'Content-Type' => 'application/json', + ], + ], + [ + 'name' => 'file', + 'filename' => $basename, + 'contents' => $stream, + ], + ]); + $this->multipart = $multipart; + + $this->stream = FnStream::decorate($multipart, [ + // for signature + '__toString' => function () use ($json) { + return $json; + }, + // let the `CURL` to use `CURLOPT_UPLOAD` context + 'getSize' => function () { + return null; + }, + ]); + } + + /** + * Get the `meta` of the multipart data string + */ + public function getMeta() + { + return $this->meta; + } + + /** + * Get the `GuzzleHttp\Psr7\FnStream` context + */ + public function getStream() + { + return $this->stream; + } + + /** + * Get the `Content-Type` of the `GuzzleHttp\Psr7\MultipartStream` + */ + public function getContentType() + { + return 'multipart/form-data; boundary=' . $this->multipart->getBoundary(); + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Util/PemUtil.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Util/PemUtil.php new file mode 100644 index 00000000..208e8c9c --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Util/PemUtil.php @@ -0,0 +1,108 @@ + + * // Encrypt usage: + * $encryptor = new SensitiveInfoCrypto( + * PemUtil::loadCertificate('/downloaded/pubcert.pem') + * ); + * $json = json_encode(['name' => $encryptor('Alice')]); + * // That's simple! + * + * // Decrypt usage: + * $decryptor = new SensitiveInfoCrypto( + * null, + * PemUtil::loadPrivateKey('/merchant/key.pem') + * ); + * $decrypted = $decryptor->setStage('decrypt')( + * 'base64 encoding message was given by the payment plat' + * ); + * // That's simple too! + * + * // Working both Encrypt and Decrypt usages: + * $crypto = new SensitiveInfoCrypto( + * PemUtil::loadCertificate('/merchant/cert.pem'), + * PemUtil::loadPrivateKey('/merchant/key.pem') + * ); + * $encrypted = $crypto('Carol'); + * $decrypted = $crypto->setStage('decrypt')($encrypted); + * // Having fun with this! + * + * + * @package WechatPay + */ +class SensitiveInfoCrypto implements \JsonSerializable { + + /** + * @var resource|null $publicCert The public certificate + */ + private $publicCert; + + /** + * @var resource|null $privateKey The private key + */ + private $privateKey; + + /** + * @var string $message The encryped or decrypted content + */ + private $message = ''; + + /** + * @var string $stage The crypto working scenario, default is `encrypt`. + * Mention here: while toggle the scenario, + * the next stage is the previous one. + */ + private $stage = 'encrypt'; + + /** + * @var array $scenarios Methods that allowed. + */ + private static $scenarios = ['encrypt', 'decrypt']; + + /** + * Constructor + * + * @param resource|null $publicCert The public certificate resource + * @param resource|null $privateKey The private key resource + */ + public function __construct($publicCert, $privateKey = null) { + $this->publicCert = $publicCert; + $this->privateKey = $privateKey; + } + + /** + * Encrypt the string by the public certificate + * + * @param string $str The content shall be encrypted + * + * @return SensitiveInfoCrypto + */ + private function encrypt($str) { + if (!is_resource($this->publicCert)) { + throw new \InvalidArgumentException('The publicCert must be resource.'); + } + openssl_public_encrypt($str, $encrypted, + $this->publicCert, \OPENSSL_PKCS1_OAEP_PADDING); + $this->message = \base64_encode($encrypted); + + return $this; + } + + /** + * Decrypt the string by the private key certificate + * + * @param string $str The content shall be decrypted + * + * @return SensitiveInfoCrypto + */ + private function decrypt($str) { + if (!is_resource($this->privateKey)) { + throw new \InvalidArgumentException('The privateKey must be resource.'); + } + openssl_private_decrypt(\base64_decode($str), $decrypted, + $this->privateKey, \OPENSSL_PKCS1_OAEP_PADDING); + $this->message = $decrypted; + + return $this; + } + + /** + * Specify data which should be + * + * @return string + */ + public function jsonSerialize() { + return $this->message; + } + + /** + * Toggle the crypto instance onto `encrypt` or `decrypt` stage + * + * @param string $scenario Should be `encrypt` or `decrypt` + * + * @throws \InvalidArgumentException if the scenario is invalid. + * + * @return SensitiveInfoCrypto + */ + public function setStage($scenario) { + if (!in_array($scenario, self::$scenarios)) { + throw new \InvalidArgumentException(sprintf( + 'Cannot setStage `%s`, here is only allowed one of the %s.', + $scenario, + \implode(', ', self::$scenarios) + )); + } + $this->stage = $scenario; + + return $this; + } + + public function __invoke($str) { + $copy = clone $this; + return $copy->{$this->stage}($str); + } + + public function __toString() { + return $this->jsonSerialize(); + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Validator.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Validator.php new file mode 100644 index 00000000..35bfc138 --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/Validator.php @@ -0,0 +1,32 @@ +credentials = $credentials; + $this->validator = $validator; + } + + /** + * Use as Guzzle middleware + * + * @param callable $handler + * + * @return callable + */ + public function __invoke(callable $handler) + { + return function (RequestInterface $request, array $options) use ($handler) { + if (!self::isWechatPayApiUrl($request->getUri())) { + return $handler($request, $options); + } + if (!$request->getBody()->isSeekable() && \class_exists("\\GuzzleHttp\\Psr7\\CachingStream")) { + $request = $request->withBody(new \GuzzleHttp\Psr7\CachingStream($request->getBody())); + } + $schema = $this->credentials->getSchema(); + $token = $this->credentials->getToken($request); + $request = $request->withHeader("Authorization", $schema.' '.$token); + if (self::isUserAgentOverwritable($request)) { + $request = $request->withHeader('User-Agent', self::getUserAgent()); + } + + return $handler($request, $options)->then( + function (ResponseInterface $response) use ($request) { + $code = $response->getStatusCode(); + if ($code >= 200 && $code < 300) { + if (!$response->getBody()->isSeekable() && \class_exists("\\GuzzleHttp\\Psr7\\CachingStream")) { + $response = $response->withBody(new \GuzzleHttp\Psr7\CachingStream($response->getBody())); + } + if (!$this->validator->validate($response)) { + if (\class_exists('\\GuzzleHttp\\Exception\\ServerException')) { + throw new \GuzzleHttp\Exception\ServerException( + "应答的微信支付签名验证失败", $request, $response); + } else { + throw new \RuntimeException("应答的微信支付签名验证失败", $code); + } + } + } + return $response; + } + ); + }; + } + + /** + * Create a new builder + * + * @return WechatPayMiddlewareBuilder + */ + public static function builder() + { + return new WechatPayMiddlewareBuilder(); + } + + /** + * Check whether url is WechatPay API V3 url + */ + protected static function isWechatPayApiUrl(UriInterface $url) + { + if ($url->getScheme() !== 'https' || !\in_array($url->getHost(), self::$API_DOMAINS)) { + return false; + } + foreach (self::$BASE_URLS as $baseUrl) { + if (\substr($url->getPath(), 0, strlen($baseUrl)) === $baseUrl) { + return true; + } + } + return false; + } + + /** + * Get User Agent + * @return string + */ + protected static function getUserAgent() + { + static $userAgent = ''; + if (!$userAgent) { + $agent = 'WechatPay-Guzzle/'.self::VERSION; + if (\class_exists('\\GuzzleHttp\\Client')) { + $version = defined('\\GuzzleHttp\\Client::VERSION') ? \GuzzleHttp\Client::VERSION + : \GuzzleHttp\Client::MAJOR_VERSION; + $agent .= ' GuzzleHttp/'.$version; + } + if (extension_loaded('curl') && function_exists('curl_version')) { + $agent .= ' curl/'.\curl_version()['version']; + } + $agent .= \sprintf(" (%s/%s) PHP/%s", PHP_OS, \php_uname('r'), PHP_VERSION); + $userAgent = $agent; + } + return $userAgent; + } + + private static function isUserAgentOverwritable(RequestInterface $request) + { + if (!$request->hasHeader('User-Agent')) { + return true; + } + $headers = $request->getHeader('User-Agent'); + $userAgent = $headers[\count($headers) - 1]; + if (\function_exists('\\GuzzleHttp\\default_user_agent')) { + return $userAgent === \GuzzleHttp\default_user_agent(); + } + return false; + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/WechatPayMiddlewareBuilder.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/WechatPayMiddlewareBuilder.php new file mode 100644 index 00000000..696d87c9 --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/src/WechatPayMiddlewareBuilder.php @@ -0,0 +1,125 @@ +credentials = new WechatPay2Credentials($merchantId, + new PrivateKeySigner($serialNo, $privateKey)); + return $this; + } + + /** + * Set Merchant Credentials + * + * @param Credentials $credentials Merchant Certificate Credentials + * + * @return $this + */ + public function withCredentials(Credentials $credentials) + { + $this->credentials = $credentials; + return $this; + } + + /** + * Set WechatPay Certificates Infomation + * + * @param array of string|resource $certifcates WechatPay Certificates (string - PEM formatted \ + * certificate, or resource - X.509 certificate resource returned by openssl_x509_read) + * + * @return $this + */ + public function withWechatPay(array $certificates) + { + $this->validator = new WechatPay2Validator(new CertificateVerifier($certificates)); + return $this; + } + + /** + * Set WechatPay Validator + * + * @param Validator $Validator WechatPay Validator + * + * @return $this + */ + public function withValidator(Validator $validator) + { + $this->validator = $validator; + return $this; + } + + /** + * Build WechatPayMiddleware + * + * @return WechatPayMiddleware + */ + public function build() + { + if (!isset($this->credentials)) { + throw new \InvalidArgumentException('商户认证信息(credentials)未设置'); + } + if (!isset($this->validator)) { + throw new \InvalidArgumentException('微信支付平台签名验证(validator)未设置'); + } + + return new WechatPayMiddleware($this->credentials, $this->validator); + } +} diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/tool/CertificateDownloader.php b/common/vendor/wechatpay/wechatpay-guzzle-middleware/tool/CertificateDownloader.php new file mode 100755 index 00000000..cb4bed07 --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/tool/CertificateDownloader.php @@ -0,0 +1,218 @@ +#!/usr/bin/env php +parseOpts(); + if (!$opts) { + $this->printHelp(); + exit(1); + } + + if (isset($opts['help'])) { + $this->printHelp(); + exit(0); + } + if (isset($opts['version'])) { + echo self::VERSION . "\n"; + exit(0); + } + + $this->downloadCert($opts); + } + + private function downloadCert($opts) + { + try { + // 构造一个WechatPayMiddleware + $builder = WechatPayMiddleware::builder() + ->withMerchant($opts['mchid'], $opts['serialno'], PemUtil::loadPrivateKey($opts['privatekey'])); // 传入商户相关配置 + if (isset($opts['wechatpay-cert'])) { + $builder->withWechatPay([ PemUtil::loadCertificate($opts['wechatpay-cert']) ]); // 使用平台证书验证 + } + else { + $builder->withValidator(new NoopValidator); // 临时"跳过”应答签名的验证 + } + $wechatpayMiddleware = $builder->build(); + + // 将WechatPayMiddleware添加到Guzzle的HandlerStack中 + $stack = HandlerStack::create(); + $stack->push($wechatpayMiddleware, 'wechatpay'); + + // 创建Guzzle HTTP Client时,将HandlerStack传入 + $client = new GuzzleHttp\Client(['handler' => $stack]); + + // 接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签 + $resp = $client->request('GET', 'https://api.mch.weixin.qq.com/v3/certificates', [ + 'headers' => [ 'Accept' => 'application/json' ] + ]); + if ($resp->getStatusCode() < 200 || $resp->getStatusCode() > 299) { + echo "download failed, code={$resp->getStatusCode()}, body=[{$resp->getBody()}]\n"; + return; + } + + $list = json_decode($resp->getBody(), true); + + $plainCerts = []; + $x509Certs = []; + + $decrypter = new AesUtil($opts['key']); + foreach ($list['data'] as $item) { + $encCert = $item['encrypt_certificate']; + $plain = $decrypter->decryptToString($encCert['associated_data'], + $encCert['nonce'], $encCert['ciphertext']); + if (!$plain) { + echo "encrypted certificate decrypt fail!\n"; + exit(1); + } + // 通过加载对证书进行简单合法性检验 + $cert = \openssl_x509_read($plain); // 从字符串中加载证书 + if (!$cert) { + echo "downloaded certificate check fail!\n"; + exit(1); + } + $plainCerts[] = $plain; + $x509Certs[] = $cert; + } + // 使用下载的证书再来验证一次应答的签名 + $validator = new WechatPay2Validator(new CertificateVerifier($x509Certs)); + if (!$validator->validate($resp)) { + echo "validate response fail using downloaded certificates!"; + exit(1); + } + // 输出证书信息,并保存到文件 + foreach ($list['data'] as $index => $item) { + echo "Certificate {\n"; + echo " Serial Number: ".$item['serial_no']."\n"; + echo " Not Before: ".(new DateTime($item['effective_time']))->format('Y-m-d H:i:s')."\n"; + echo " Not After: ".(new DateTime($item['expire_time']))->format('Y-m-d H:i:s')."\n"; + echo " Text: \n ".str_replace("\n", "\n ", $plainCerts[$index])."\n"; + echo "}\n"; + + $outpath = $opts['output'].DIRECTORY_SEPARATOR.'wechatpay_'.$item['serial_no'].'.pem'; + file_put_contents($outpath, $plainCerts[$index]); + } + } + catch (RequestException $e) { + echo "download failed, message=[{$e->getMessage()}] "; + if ($e->hasResponse()) { + echo "code={$e->getResponse()->getStatusCode()}, body=[{$e->getResponse()->getBody()}]\n"; + } + exit(1); + } + catch (Exception $e) { + echo "download failed, message=[{$e->getMessage()}]\n"; + echo $e; + exit(1); + } + } + + private function parseOpts() + { + $opts = [ + [ 'key', 'k', true ], + [ 'mchid', 'm', true ], + [ 'privatekey', 'f', true ], + [ 'serialno', 's', true ], + [ 'output', 'o', true ], + [ 'wechatpay-cert', 'c', false ], + ]; + + $shortopts = 'hV'; + $longopts = [ 'help', 'version' ]; + foreach ($opts as $opt) { + $shortopts .= $opt[1].':'; + $longopts[] = $opt[0].':'; + } + $parsed = getopt($shortopts, $longopts); + if (!$parsed) { + return false; + } + + $args = []; + foreach ($opts as $opt) { + if (isset($parsed[$opt[0]])) { + $args[$opt[0]] = $parsed[$opt[0]]; + } + else if (isset($parsed[$opt[1]])) { + $args[$opt[0]] = $parsed[$opt[1]]; + } + else if ($opt[2]) { + return false; + } + } + + if (isset($parsed['h']) || isset($parsed['help'])) { + $args['help'] = true; + } + if (isset($parsed['V']) || isset($parsed['version'])) { + $args['version'] = true; + } + return $args; + } + + private function printHelp() + { + echo <<] + -f= -k= -m= + -o= -s= + -m, --mchid= 商户号 + -s, --serialno= 商户证书的序列号 + -f, --privatekey= + 商户的私钥文件 + -k, --key= ApiV3Key + -c, --wechatpay-cert= + 微信支付平台证书,验证签名 + -o, --output= + 下载成功后保存证书的路径 + -V, --version Print version information and exit. + -h, --help Show this help message and exit. + +EOD; + } +} + +class NoopValidator implements Validator +{ + public function validate(\Psr\Http\Message\ResponseInterface $response) + { + return true; + } +} + + +// main +(new CertificateDownloader())->run(); diff --git a/common/vendor/wechatpay/wechatpay-guzzle-middleware/tool/README.md b/common/vendor/wechatpay/wechatpay-guzzle-middleware/tool/README.md new file mode 100644 index 00000000..d3e53bfb --- /dev/null +++ b/common/vendor/wechatpay/wechatpay-guzzle-middleware/tool/README.md @@ -0,0 +1,45 @@ +# Certificate Downloader + +Certificate Downloader 是 PHP版 微信支付 APIv3 平台证书的命令行下载工具。该工具可从 `https://api.mch.weixin.qq.com/v3/certificates` 接口获取商户可用证书,并使用 [APIv3 密钥](https://wechatpay-api.gitbook.io/wechatpay-api-v3/ren-zheng/api-v3-mi-yao) 和 AES_256_GCM 算法进行解密,并把解密后证书下载到指定位置。 + +## 使用 +使用方法与 [Java版Certificate Downloader](https://github.com/wechatpay-apiv3/CertificateDownloader) 一致,参数与常见问题请参考[其文档](https://github.com/wechatpay-apiv3/CertificateDownloader/blob/master/README.md)。 + +```shell +> php tool/CertificateDownloader.php +Usage: 微信支付平台证书下载工具 [-hV] [-c=] + -f= -k= -m= + -o= -s= + -m, --mchid= 商户号 + -s, --serialno= 商户证书的序列号 + -f, --privatekey= + 商户的私钥文件 + -k, --key= ApiV3Key + -c, --wechatpay-cert= + 微信支付平台证书,验证签名 + -o, --output= + 下载成功后保存证书的路径 + -V, --version Print version information and exit. + -h, --help Show this help message and exit. +``` + +完整命令示例: + +```shell +php tool/CertificateDownloader.php -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath} -c ${wechatpayCertificateFilePath} +``` + + + +## 常见问题 + +### 如何保证证书正确 +请参见CertificateDownloader文档中[关于如何保证证书正确的说明](https://github.com/wechatpay-apiv3/CertificateDownloader#%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E8%AF%81%E4%B9%A6%E6%AD%A3%E7%A1%AE)。 + +### 如何使用信任链验证平台证书 +请参见CertificateDownloader文档中[关于如何使用信任链验证平台证书的说明](https://github.com/wechatpay-apiv3/CertificateDownloader#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E4%BF%A1%E4%BB%BB%E9%93%BE%E9%AA%8C%E8%AF%81%E5%B9%B3%E5%8F%B0%E8%AF%81%E4%B9%A6)。 + +### 第一次下载证书 + +请参见CertificateDownloader文档中[相关说明](https://github.com/wechatpay-apiv3/CertificateDownloader#%E7%AC%AC%E4%B8%80%E6%AC%A1%E4%B8%8B%E8%BD%BD%E8%AF%81%E4%B9%A6)。 + diff --git a/sql/order.sql b/sql/order.sql index 1e5aabfd..c9ccf221 100644 --- a/sql/order.sql +++ b/sql/order.sql @@ -3,7 +3,7 @@ -- Author:lcc -- Table:lc_order_purchase -- --------------------------- -drop table if exists hd_order_purchase; +drop table if exists lc_order_purchase; create table lc_order_purchase ( id int(10) unsigned not null auto_increment, app_id int(10) unsigned not null comment '小程序id', @@ -39,7 +39,7 @@ create table lc_order_purchase ( key idx_user_order (app_id,app_uid,status), key idx_sid (sid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='消费订单表'; - +alter table lc_order_purchase add mch_id varchar(30) not null default '' comment '支付商户号' after sid; -- ---------------------------- -- Title:订单总表 diff --git a/sql/receiver/order.sql b/sql/receiver/order.sql index c312e870..ee79a47d 100644 --- a/sql/receiver/order.sql +++ b/sql/receiver/order.sql @@ -153,6 +153,7 @@ create table lc_receiver_order_signs ( -- Title:订单表 -- Author:lcc -- Table:lc_receiver_orders +-- info_json entrust_name 代办人姓名 entrust_idcard 代办人身份证 -- --------------------------- drop table if exists lc_receiver_orders; create table lc_receiver_orders ( @@ -180,3 +181,6 @@ create table lc_receiver_orders ( u_time timestamp not null default current_timestamp on update current_timestamp, primary key (id) ) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 COLLATE=UTF8MB4_0900_AI_CI COMMENT='订单表'; +alter table lc_receiver_orders add pack_id int(10) unsigned not null default 0 comment '服务包id' after incor_id; +alter table lc_receiver_orders add main_type tinyint(1) unsigned not null default 0 comment '购车主体(0个人 1公司)' after admin_id; +alter table lc_receiver_orders add ifentrust tinyint(1) unsigned not null default 0 comment '是否委托代办' after main_type; diff --git a/sql/receiver/services.sql b/sql/receiver/services.sql new file mode 100644 index 00000000..8b69a4b4 --- /dev/null +++ b/sql/receiver/services.sql @@ -0,0 +1,28 @@ +-- ---------------------------- +-- Title:服务表 +-- Author:lcc +-- Table:lc_receiver_services +-- --------------------------- +create table lc_receiver_services ( + id int(10) unsigned not null auto_increment comment '自增id', + title varchar(50) not null default '' comment '服务名称', + field_name varchar(50) not null default '' comment '表名.字段名', + c_time int(10) unsigned not null default '0' comment '创建时间', + u_time timestamp not null default current_timestamp on update current_timestamp, + primary key (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='服务表'; + + +-- ---------------------------- +-- Title:服务包表 +-- Author:lcc +-- Table:lc_receiver_service_package +-- --------------------------- +create table lc_receiver_service_package ( + id int(10) unsigned not null auto_increment comment '自增id', + srv_ids varchar(50) not null default '' comment '服务包组合id', + status tinyint(2) not null default '1' comment '状态(-1删除 0禁用 1正常)', + c_time int(10) unsigned not null default '0' comment '创建时间', + u_time timestamp not null default current_timestamp on update current_timestamp, + primary key (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='服务包表' diff --git a/www/wxpay/AesUtil.php b/www/wxpay/AesUtil.php new file mode 100644 index 00000000..2ceb0850 --- /dev/null +++ b/www/wxpay/AesUtil.php @@ -0,0 +1,68 @@ +aesKey = $aesKey; + } + + /** + * Decrypt AEAD_AES_256_GCM ciphertext + * + * @param string $associatedData AES GCM additional authentication data + * @param string $nonceStr AES GCM nonce + * @param string $ciphertext AES GCM cipher text + * + * @return string|bool Decrypted string on success or FALSE on failure + */ + public function decryptToString($associatedData, $nonceStr, $ciphertext) + { + $ciphertext = \base64_decode($ciphertext); + if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) { + return false; + } + + // ext-sodium (default installed on >= PHP 7.2) + if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && + \sodium_crypto_aead_aes256gcm_is_available()) { + return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); + } + + // ext-libsodium (need install libsodium-php 1.x via pecl) + if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && + \Sodium\crypto_aead_aes256gcm_is_available()) { + return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); + } + + // openssl (PHP >= 7.1 support AEAD) + if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) { + $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE); + $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE); + + return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr, + $authTag, $associatedData); + } + + throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php'); + } +} diff --git a/www/wxpay/index.php b/www/wxpay/index.php new file mode 100644 index 00000000..42494a02 --- /dev/null +++ b/www/wxpay/index.php @@ -0,0 +1,29 @@ + 0,'msg'=> '']; +if(!$resource || !$nonceStr || !$associatedData || !$ciphertext){ + $req['msg'] = '参数错误'; + die(json_encode($req,JSON_UNESCAPED_UNICODE)); +} +$AesUtil = new AesUtil($aesKey); +try { + $result = $AesUtil->decryptToString($associatedData,$nonceStr,$ciphertext); + if($result){ + $req['code'] = 1; + $req['data'] = json_decode($result); + $req['msg'] = '解密成功'; + die(json_encode($req,JSON_UNESCAPED_UNICODE)); + }else{ + $req['msg'] = '解密失败'; + die(json_encode($req,JSON_UNESCAPED_UNICODE)); + } +} catch (Exception $e) { + $req['msg'] = $e->getMessage(); + die(json_encode($req,JSON_UNESCAPED_UNICODE)); +}