edit-api-licheb-opt
This commit is contained in:
@@ -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']]);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by Vim
|
||||
* User: lcc
|
||||
* Desc: 微信支付配置
|
||||
* Date: 2021/8/12
|
||||
* Time: 14:12
|
||||
*/
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
$config['default'] = [ //服务商信息配置
|
||||
'appid' => '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
|
||||
];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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'] : "",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: lcc
|
||||
* Date: 2021/08/11
|
||||
* Time: 20:17
|
||||
*/
|
||||
//微信支付回调V3
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
class Wxnotify_v3 extends CI_Controller{
|
||||
private $app_id = 1;
|
||||
private $log_file = 'liche_pay.log';
|
||||
private $log_dir = "wxpay";
|
||||
private $notify_data;
|
||||
private $desc_url = 'http://p7.liche.cn'; //微信服务解码地址
|
||||
|
||||
public function __construct(){
|
||||
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,$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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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'])
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
/**
|
||||
* Created by Vim
|
||||
* User: lcc
|
||||
* Date: 2021/08/09
|
||||
* Time: 11:08
|
||||
*/
|
||||
require_once APPPATH.'controllers/wxapp/Wxapp.php';
|
||||
class Services extends Wxapp{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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 = '';
|
||||
|
||||
|
||||
@@ -31,23 +31,21 @@
|
||||
|
||||
<div style="font-size:14px;">
|
||||
<div style="text-align:center;font-weight:bold;font-size:16px;">委托服务协议</div>
|
||||
<div style="text-align:right;">协议编号:(右对齐,6位地区编码+合同类型id+yymmdd+6位随机数)</div>
|
||||
<div><b>甲方(出卖人):</b>(根据门店所属公司)</div>
|
||||
<div><b>统一社会信用代码:</b>(根据门店所属公司)</div>
|
||||
<div><b>乙方(买受人):</b>(用户真实姓名)</div>
|
||||
<div><b>(证件类型):</b>证件号码</div>
|
||||
<div><b>联系电话:</b>(用户手机号码)</div>
|
||||
<div>#假如有委托人#</div>
|
||||
<div><b>委托代理人:</b>(委托人真实姓名)</div>
|
||||
<div><b>(证件类型):</b>委托人证件号码</div>
|
||||
<div><b>联系电话:</b>(委托人手机号码)</div>
|
||||
<div style="text-align:right;">协议编号:<?=$cid?></div>
|
||||
<div><b>甲方(出卖人):</b><?=$company['title']?></div>
|
||||
<div><b>统一社会信用代码:</b><?=$company['credit_code']?></div>
|
||||
<div><b>乙方(买受人):</b><?=$name?></div>
|
||||
<div><b>联系电话:</b><?=$mobile?></div>
|
||||
<?if($ifentrust){?>
|
||||
<div><b>委托代理人:</b><?=$info_json['entrust_name']?></div>
|
||||
<div><b>委托人身份证:</b><?=$info_json['entrust_idcard']?></div>
|
||||
<?}?>
|
||||
<div><b>乙方为节省时间和精力,就甲乙双方《车辆买卖合同》标的的车辆,自愿委托甲方办理下列委托事项,并达成如下协议:</b></div>
|
||||
<div><b>一、委托代办服务事项(乙方授权甲方代办下列事项)</b></div>
|
||||
<div>代办保险服务 每个车一个价格</div>
|
||||
<div>代办上牌服务 每个地级市一个价格</div>
|
||||
<div>代办金融服务 (公式)</div>
|
||||
<div>购买精品包</div>
|
||||
<div>代办服务总价:(计算出合同总价,含保险)元, 壹千贰百叁十个肆玩万元整,保险费用由乙方直接支付给保险公司。</div>
|
||||
<?foreach($services as $val){?>
|
||||
<div><?=$val['title']?></div>
|
||||
<?}?>
|
||||
<div>代办服务总价:<?=$total_price?>元, <?=num_to_rmb($total_price)?>,保险费用由乙方直接支付给保险公司。</div>
|
||||
<div><b>二、双方特别约定</b></div>
|
||||
<div>1、车牌选号若为代选号牌时,均由甲方电话通知乙方,因乙方未能及时选号等其它原因,甲方有权代理决定并不对最终选号结果负责;若为自选号牌,甲方可配合乙方上牌,但不对最终选号结果负责。</div>
|
||||
<div>2、乙方委托甲方代办车辆挂牌服务时,乙方应事先办妥机动车车辆保险,投保险种包括但不限于车辆损失险和第三者责任险。甲方在代办服务过程中造成车辆毁损、灭失的,乙方应当先向保险公司索赔,赔付不足部分由甲方予以修复或赔偿。</div>
|
||||
|
||||
@@ -9,36 +9,37 @@
|
||||
<div style="font-size:14px;">
|
||||
<div style="text-align:center;font-weight:bold;font-size:16px;">车辆买卖合同</div>
|
||||
<div style="text-align:right;">合同编号:<?=$cid?></div>
|
||||
<div><b>甲方(出卖人):</b>(根据门店所属公司)</div>
|
||||
<div><b>统一社会信用代码:</b>(根据门店所属公司)</div>
|
||||
<div><b>乙方(买受人):</b>(用户真实姓名)</div>
|
||||
<div><b>联系电话:</b>(用户手机号码)</div>
|
||||
<div>#假如有委托人#</div>
|
||||
<div><b>委托代理人:</b>(委托人真实姓名)</div>
|
||||
<div><b>联系电话:</b>(委托人手机号码)</div>
|
||||
<div><b>甲方(出卖人):</b><?=$company['title']?></div>
|
||||
<div><b>统一社会信用代码:</b><?=$company['credit_code']?></div>
|
||||
<div><b>乙方(买受人):</b><?=$name?></div>
|
||||
<div><b>联系电话:</b><?=$mobile?></div>
|
||||
<?if($ifentrust){?>
|
||||
<div><b>委托代理人:</b><?=$info_json['entrust_name']?></div>
|
||||
<div><b>委托人身份证:</b><?=$info_json['entrust_idcard']?></div>
|
||||
<?}?>
|
||||
<div><b>甲、乙双方依据《中华人民共和国合同法》及其他有关法律、法规的规定,在平等、自愿、协商一致的基础上,就买卖汽车事宜,订立本合同。</b></div>
|
||||
<div><b>第一条 车辆基本情况</b></div>
|
||||
<div>
|
||||
<table style="width:100%;">
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
品牌:
|
||||
品牌:<?=$brand_name?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车系:
|
||||
车系:<?=$series_name?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
车型:
|
||||
车型:<?=$version?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车身颜色:
|
||||
车身颜色:<?=$color?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
内饰颜色:
|
||||
内饰颜色:<?=$incolor?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
|
||||
@@ -68,14 +69,16 @@
|
||||
</div>
|
||||
<div>车辆单价为车身单价,续航里程为300KM以上的车辆已按扣减国家补贴后的价格结算。不包含车辆购置税、车辆保险费、挂牌杂费等其他费用。乙方如需委托甲方代理上牌、委托运输等服务的,双方应另外签订《委托服务协议》。</div>
|
||||
<div><b>第三条付款方式</b></div>
|
||||
<div>乙方在签订合同时即付定金人民币: 元,定金在结算时冲抵车款。</div>
|
||||
<div>乙方选择一次性付款方式 / 分期付款方式</div>
|
||||
<div>乙方在签订合同时即付定金人民币:<?=$dep_price?>元,定金在结算时冲抵车款。</div>
|
||||
<div>乙方选择<?=$row['payway']?'一次性付款方式':'分期付款方式'?></div>
|
||||
<div><b>第四条 车辆交付</b></div>
|
||||
<div>1、交车时间:合同签订之日起 XX天内。</div>
|
||||
<div>1、交车时间:合同签订之日起 <?=$give_time?>天内。</div>
|
||||
<!--
|
||||
<div>(EX1 4个服务30天,3个服务 45天 2个服务60天</div>
|
||||
<div>雷丁 4个服务30天,3个服务 45天 2个服务60天。)</div>
|
||||
-->
|
||||
<div>2、提车方式:乙方自提</div>
|
||||
<div>3、交车地点 : XX市XX区/县 XXXX店 先按当前门店 。</div>
|
||||
<div>3、交车地点 :<?=$address?>。</div>
|
||||
<div>4、交车时里程表记录小于100公里</div>
|
||||
<div>5、备注:如乙方购买续航里程为300KM以上车辆,上牌性质是公户或营运车辆需确保购买车辆自行驶证注册日期起1年之内行驶2万公里,且至车辆注册之日起2年内不得过户,如因违反此条约使甲方在申领国补过程中造成的损失,一切责任及后果由乙方承担。</div>
|
||||
<div><b>第五条 车辆确认</b></div>
|
||||
|
||||
@@ -8,15 +8,14 @@
|
||||
|
||||
<div style="font-size:14px;">
|
||||
<div style="text-align:center;font-weight:bold;font-size:16px;">车辆信息确认单</div>
|
||||
<div><b>甲方(出卖人):</b>(根据门店所属公司)</div>
|
||||
<div><b>统一社会信用代码:</b>(根据门店所属公司)</div>
|
||||
<div><b>乙方(买受人):</b>(用户真实姓名)</div>
|
||||
<div><b>(证件类型):</b>证件号码</div>
|
||||
<div><b>联系电话:</b>(用户手机号码)</div>
|
||||
<div>#假如有委托人#</div>
|
||||
<div><b>委托代理人:</b>(委托人真实姓名)</div>
|
||||
<div><b>(证件类型):</b>委托人证件号码</div>
|
||||
<div><b>联系电话:</b>(委托人手机号码)</div>
|
||||
<div><b>甲方(出卖人):</b><?=$company['title']?></div>
|
||||
<div><b>统一社会信用代码:</b><?=$company['credit_code']?></div>
|
||||
<div><b>乙方(买受人):</b><?=$name?></div>
|
||||
<div><b>联系电话:</b><?=$mobile?></div>
|
||||
<?if($ifentrust){?>
|
||||
<div><b>委托代理人:</b><?=$info_json['entrust_name']?></div>
|
||||
<div><b>委托人身份证:</b><?=$info_json['entrust_idcard']?></div>
|
||||
<?}?>
|
||||
<div>乙方于<?=date('Y年m月d日',$c_time)?>在甲方购买 <?=$brand_name?> 车辆 1 台,并确认车架号为:<?=$vin?>。乙方已对上述车辆的厂牌型号、配置等进行认真检查并验收合格无异议。</div>
|
||||
<div style="margin-top:50px;">
|
||||
<table style="width:100%;">
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
|
||||
<div style="font-size:14px;">
|
||||
<div style="text-align:center;font-weight:bold;font-size:16px;">车辆交付表</div>
|
||||
<div><b>甲方(出卖人):</b>(根据门店所属公司)</div>
|
||||
<div><b>统一社会信用代码:</b>(根据门店所属公司)</div>
|
||||
<div><b>乙方(买受人):</b>(用户真实姓名)</div>
|
||||
<div><b>(证件类型):</b>证件号码</div>
|
||||
<div><b>联系电话:</b>(用户手机号码)</div>
|
||||
<div>#假如有委托人#</div>
|
||||
<div><b>委托代理人:</b>(委托人真实姓名)</div>
|
||||
<div><b>(证件类型):</b>委托人证件号码</div>
|
||||
<div><b>联系电话:</b>(委托人手机号码)</div>
|
||||
<div><b>甲方(出卖人):</b><?=$company['title']?></div>
|
||||
<div><b>统一社会信用代码:</b><?=$company['credit_code']?></div>
|
||||
<div><b>乙方(买受人):</b><?=$name?></div>
|
||||
<div><b>身份证号码:</b><?=$cardid?></div>
|
||||
<div><b>联系电话:</b><?=$mobile?></div>
|
||||
<?if($ifentrust){?>
|
||||
<div><b>委托代理人:</b><?=$info_json['entrust_name']?></div>
|
||||
<div><b>委托人身份证:</b><?=$info_json['entrust_idcard']?></div>
|
||||
<?}?>
|
||||
<div><b>一、车辆信息</b></div>
|
||||
<div>
|
||||
<table style="width:100%;">
|
||||
@@ -25,12 +25,12 @@
|
||||
品牌:<?=$brand_name?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车系:
|
||||
车系:<?=$series_name?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
车型:
|
||||
车型:<?=$version?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车身颜色:<?=$color?>
|
||||
@@ -38,15 +38,15 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
内饰颜色:
|
||||
内饰颜色:<?=$incolor?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车架号:XW111111111111
|
||||
车架号:<?=$vin?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
车牌号:闽D232333
|
||||
车牌号:<?=$agent['car_num']?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
</td>
|
||||
@@ -54,8 +54,8 @@
|
||||
</table>
|
||||
</div>
|
||||
<div><b>二、结算信息</b></div>
|
||||
<div>应付款合计 50100 ,其中包含:车辆价款 50000,委托代办款项 3000</div>
|
||||
<div>已付款合计 50000</div>
|
||||
<div>应付款合计 <?=number_format($price+$total_price,2)?> ,其中包含:车辆价款 <?=number_format($price,2)?>,委托代办款项 <?=number_format($total_price,2)?></div>
|
||||
<div>已付款合计 <?=number_format($pay_price,2)?></div>
|
||||
<div><b>三、证件资料交接信息</b></div>
|
||||
<div>
|
||||
<div><b>证件信息由后台配置,销售交付时钩选才显示:</b></div>
|
||||
|
||||
@@ -12,23 +12,21 @@
|
||||
|
||||
<div class="inner40 line-height-17 font-28 color-666 text-break">
|
||||
<div class="pt10 pb20 text-center text-bold font-40">委托代理服务协议</div>
|
||||
<div class="text-right">协议编号:(右对齐,6位地区编码+合同类型id+yymmdd+6位随机数)</div>
|
||||
<div><b>甲方(出卖人):</b>(根据门店所属公司)</div>
|
||||
<div><b>统一社会信用代码:</b>(根据门店所属公司)</div>
|
||||
<div><b>乙方(买受人):</b>(用户真实姓名)</div>
|
||||
<div><b>(证件类型):</b>证件号码</div>
|
||||
<div><b>联系电话:</b>(用户手机号码)</div>
|
||||
<div>#假如有委托人#</div>
|
||||
<div><b>委托代理人:</b>(委托人真实姓名)</div>
|
||||
<div><b>(证件类型):</b>委托人证件号码</div>
|
||||
<div><b>联系电话:</b>(委托人手机号码)</div>
|
||||
<div class="text-right">协议编号:<?=$cid?></div>
|
||||
<div><b>甲方(出卖人):</b><?=$company['title']?></div>
|
||||
<div><b>统一社会信用代码:</b><?=$company['credit_code']?></div>
|
||||
<div><b>乙方(买受人):</b><?=$name?></div>
|
||||
<div><b>联系电话:</b><?=$mobile?></div>
|
||||
<?if($ifentrust){?>
|
||||
<div><b>委托代理人:</b><?=$info_json['entrust_name']?></div>
|
||||
<div><b>委托人身份证:</b><?=$info_json['entrust_idcard']?></div>
|
||||
<?}?>
|
||||
<div><b>乙方为节省时间和精力,就甲乙双方《车辆买卖合同》标的的车辆,自愿委托甲方办理下列委托事项,并达成如下协议:</b></div>
|
||||
<div><b>一、委托代办服务事项(乙方授权甲方代办下列事项)</b></div>
|
||||
<div>代办保险服务 每个车一个价格</div>
|
||||
<div>代办上牌服务 每个地级市一个价格</div>
|
||||
<div>代办金融服务 (公式)</div>
|
||||
<div>购买精品包</div>
|
||||
<div>代办服务总价:(计算出合同总价,含保险)元, 壹千贰百叁十个肆玩万元整,保险费用由乙方直接支付给保险公司。</div>
|
||||
<?foreach($services as $val){?>
|
||||
<div><?=$val['title']?></div>
|
||||
<?}?>
|
||||
<div>代办服务总价:<?=$total_price?>元,<?=num_to_rmb($total_price)?>,保险费用由乙方直接支付给保险公司。</div>
|
||||
<div><b>二、双方特别约定</b></div>
|
||||
<div>1、车牌选号若为代选号牌时,均由甲方电话通知乙方,因乙方未能及时选号等其它原因,甲方有权代理决定并不对最终选号结果负责;若为自选号牌,甲方可配合乙方上牌,但不对最终选号结果负责。</div>
|
||||
<div>2、乙方委托甲方代办车辆挂牌服务时,乙方应事先办妥机动车车辆保险,投保险种包括但不限于车辆损失险和第三者责任险。甲方在代办服务过程中造成车辆毁损、灭失的,乙方应当先向保险公司索赔,赔付不足部分由甲方予以修复或赔偿。</div>
|
||||
|
||||
@@ -13,36 +13,37 @@
|
||||
<div class="inner40 line-height-17 font-28 color-666 text-break">
|
||||
<div class="pt10 pb20 text-center text-bold font-40">车辆买卖合同</div>
|
||||
<div class="text-right">合同编号:<?=$cid?></div>
|
||||
<div><b>甲方(出卖人):</b>(根据门店所属公司)</div>
|
||||
<div><b>统一社会信用代码:</b>(根据门店所属公司)</div>
|
||||
<div><b>乙方(买受人):</b>(用户真实姓名)</div>
|
||||
<div><b>联系电话:</b>(用户手机号码)</div>
|
||||
<div>#假如有委托人#</div>
|
||||
<div><b>委托代理人:</b>(委托人真实姓名)</div>
|
||||
<div><b>联系电话:</b>(委托人手机号码)</div>
|
||||
<div><b>甲方(出卖人):</b><?=$company['title']?></div>
|
||||
<div><b>统一社会信用代码:</b><?=$company['credit_code']?></div>
|
||||
<div><b>乙方(买受人):</b><?=$name?></div>
|
||||
<div><b>联系电话:</b><?=$mobile?></div>
|
||||
<?if($ifentrust){?>
|
||||
<div><b>委托代理人:</b><?=$info_json['entrust_name']?></div>
|
||||
<div><b>委托人身份证:</b><?=$info_json['entrust_idcard']?></div>
|
||||
<?}?>
|
||||
<div><b>甲、乙双方依据《中华人民共和国合同法》及其他有关法律、法规的规定,在平等、自愿、协商一致的基础上,就买卖汽车事宜,订立本合同。</b></div>
|
||||
<div><b>第一条 车辆基本情况</b></div>
|
||||
<div>
|
||||
<table style="width:100%;">
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
品牌:
|
||||
品牌:<?=$brand_name?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车系:
|
||||
车系:<?=$series_name?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
车型:
|
||||
车型:<?=$version?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车身颜色:
|
||||
车身颜色:<?=$color?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
内饰颜色:
|
||||
内饰颜色:<?=$incolor?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
|
||||
@@ -72,14 +73,16 @@
|
||||
</div>
|
||||
<div>车辆单价为车身单价,续航里程为300KM以上的车辆已按扣减国家补贴后的价格结算。不包含车辆购置税、车辆保险费、挂牌杂费等其他费用。乙方如需委托甲方代理上牌、委托运输等服务的,双方应另外签订《委托服务协议》。</div>
|
||||
<div><b>第三条付款方式</b></div>
|
||||
<div>乙方在签订合同时即付定金人民币: 元,定金在结算时冲抵车款。</div>
|
||||
<div>乙方选择一次性付款方式 / 分期付款方式</div>
|
||||
<div>乙方在签订合同时即付定金人民币:<?=$dep_price?>元,定金在结算时冲抵车款。</div>
|
||||
<div>乙方选择<?=$row['payway']?'一次性付款方式':'分期付款方式'?></div>
|
||||
<div><b>第四条 车辆交付</b></div>
|
||||
<div>1、交车时间:合同签订之日起 XX天内。</div>
|
||||
<div>1、交车时间:合同签订之日起 <?=$give_time?>天内。</div>
|
||||
<!--
|
||||
<div>(EX1 4个服务30天,3个服务 45天 2个服务60天</div>
|
||||
<div>雷丁 4个服务30天,3个服务 45天 2个服务60天。)</div>
|
||||
-->
|
||||
<div>2、提车方式:乙方自提</div>
|
||||
<div>3、交车地点 : XX市XX区/县 XXXX店 先按当前门店 。</div>
|
||||
<div>3、交车地点 :<?=$address?>。</div>
|
||||
<div>4、交车时里程表记录小于100公里</div>
|
||||
<div>5、备注:如乙方购买续航里程为300KM以上车辆,上牌性质是公户或营运车辆需确保购买车辆自行驶证注册日期起1年之内行驶2万公里,且至车辆注册之日起2年内不得过户,如因违反此条约使甲方在申领国补过程中造成的损失,一切责任及后果由乙方承担。</div>
|
||||
<div><b>第五条 车辆确认</b></div>
|
||||
|
||||
@@ -12,15 +12,14 @@
|
||||
|
||||
<div class="inner40 line-height-17 font-28 color-666 text-break">
|
||||
<div class="pt10 pb20 text-center text-bold font-40">车辆信息确认单</div>
|
||||
<div><b>甲方(出卖人):</b>(根据门店所属公司)</div>
|
||||
<div><b>统一社会信用代码:</b>(根据门店所属公司)</div>
|
||||
<div><b>乙方(买受人):</b>(用户真实姓名)</div>
|
||||
<div><b>(证件类型):</b>证件号码</div>
|
||||
<div><b>联系电话:</b>(用户手机号码)</div>
|
||||
<div>#假如有委托人#</div>
|
||||
<div><b>委托代理人:</b>(委托人真实姓名)</div>
|
||||
<div><b>(证件类型):</b>委托人证件号码</div>
|
||||
<div><b>联系电话:</b>(委托人手机号码)</div>
|
||||
<div><b>甲方(出卖人):</b><?=$company['title']?></div>
|
||||
<div><b>统一社会信用代码:</b><?=$company['credit_code']?></div>
|
||||
<div><b>乙方(买受人):</b><?=$name?></div>
|
||||
<div><b>联系电话:</b><?=$mobile?></div>
|
||||
<?if($ifentrust){?>
|
||||
<div><b>委托代理人:</b><?=$info_json['entrust_name']?></div>
|
||||
<div><b>委托人身份证:</b><?=$info_json['entrust_idcard']?></div>
|
||||
<?}?>
|
||||
<div>乙方于<?=date('Y年m月d日',$c_time)?>在甲方购买 <?=$brand_name?> 车辆 1 台,并确认车架号为:<?=$vin?>。乙方已对上述车辆的厂牌型号、配置等进行认真检查并验收合格无异议。</div>
|
||||
<div class="mt50">
|
||||
<table style="width:100%;">
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
|
||||
<div class="inner40 line-height-17 font-28 color-666 text-break">
|
||||
<div class="pt10 pb20 text-center text-bold font-40">车辆交付表</div>
|
||||
<div><b>甲方(出卖人):</b>(根据门店所属公司)</div>
|
||||
<div><b>统一社会信用代码:</b>(根据门店所属公司)</div>
|
||||
<div><b>乙方(买受人):</b>(用户真实姓名)</div>
|
||||
<div><b>(证件类型):</b>证件号码</div>
|
||||
<div><b>联系电话:</b>(用户手机号码)</div>
|
||||
<div>#假如有委托人#</div>
|
||||
<div><b>委托代理人:</b>(委托人真实姓名)</div>
|
||||
<div><b>(证件类型):</b>委托人证件号码</div>
|
||||
<div><b>联系电话:</b>(委托人手机号码)</div>
|
||||
<div><b>甲方(出卖人):</b><?=$company['title']?></div>
|
||||
<div><b>统一社会信用代码:</b><?=$company['credit_code']?></div>
|
||||
<div><b>乙方(买受人):</b><?=$name?></div>
|
||||
<div><b>身份证号码:</b><?=$cardid?></div>
|
||||
<div><b>联系电话:</b><?=$mobile?></div>
|
||||
<?if($ifentrust){?>
|
||||
<div><b>委托代理人:</b><?=$info_json['entrust_name']?></div>
|
||||
<div><b>委托人身份证:</b><?=$info_json['entrust_idcard']?></div>
|
||||
<?}?>
|
||||
<div><b>一、车辆信息</b></div>
|
||||
<div>
|
||||
<table style="width:100%;">
|
||||
@@ -29,12 +29,12 @@
|
||||
品牌:<?=$brand_name?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车系:
|
||||
车系:<?=$series_name?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
车型:
|
||||
车型:<?=$version?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车身颜色:<?=$color?>
|
||||
@@ -42,15 +42,15 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
内饰颜色:
|
||||
内饰颜色:<?=$incolor?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
车架号:XW111111111111
|
||||
车架号:<?=$vin?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">
|
||||
车牌号:闽D232333
|
||||
车牌号:<?=$agent['car_num']?>
|
||||
</td>
|
||||
<td style="width:50%;">
|
||||
</td>
|
||||
@@ -58,8 +58,8 @@
|
||||
</table>
|
||||
</div>
|
||||
<div><b>二、结算信息</b></div>
|
||||
<div>应付款合计 50100 ,其中包含:车辆价款 50000,委托代办款项 3000</div>
|
||||
<div>已付款合计 50000</div>
|
||||
<div>应付款合计 <?=number_format($price+$total_price,2)?> ,其中包含:车辆价款 <?=number_format($price,2)?>,委托代办款项 <?=number_format($total_price,2)?></div>
|
||||
<div>已付款合计 <?=number_format($pay_price,2)?></div>
|
||||
<div><b>三、证件资料交接信息</b></div>
|
||||
<div>
|
||||
<div><b>证件信息由后台配置,销售交付时钩选才显示:</b></div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"require": {
|
||||
"gregwar/image": "^2.1",
|
||||
"tencentcloud/ocr": "^3.0"
|
||||
"tencentcloud/ocr": "^3.0",
|
||||
"wechatpay/wechatpay-guzzle-middleware": "^0.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+52
-1
@@ -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": [],
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by Vim
|
||||
* User: lcc
|
||||
* Date: 2021-08-11
|
||||
* Time: 18:54
|
||||
* Desc: 微信支付V3
|
||||
*/
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
|
||||
use WechatPay\GuzzleMiddleware\Util\PemUtil;
|
||||
class WechatPayV3{
|
||||
private $merchantId;
|
||||
private $merchantSerialNumber;
|
||||
private $merchantPrivateKey;
|
||||
|
||||
public function __construct($config){
|
||||
$this->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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Receiver_service_package_model extends HD_Model
|
||||
{
|
||||
private $table_name = 'lc_receiver_service_package';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct($this->table_name, 'default');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Receiver_services_model extends HD_Model
|
||||
{
|
||||
private $table_name = 'lc_receiver_services';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct($this->table_name, 'default');
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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'=>'未知商品类型');
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
../wechatpay/wechatpay-guzzle-middleware/tool/CertificateDownloader.php
|
||||
+1
@@ -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'),
|
||||
|
||||
+8
@@ -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',
|
||||
|
||||
+53
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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)。
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* CertificateVerifier
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Auth;
|
||||
|
||||
use WechatPay\GuzzleMiddleware\Auth\Verifier;
|
||||
use WechatPay\GuzzleMiddleware\Util\PemUtil;
|
||||
|
||||
/**
|
||||
* CertificateVerifier
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
class CertificateVerifier implements Verifier
|
||||
{
|
||||
/**
|
||||
* WechatPay Certificates Public Keys
|
||||
*
|
||||
* @var array (serialNo => 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');
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* PrivateKeySigner
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Auth;
|
||||
|
||||
use WechatPay\GuzzleMiddleware\Auth\Signer;
|
||||
use WechatPay\GuzzleMiddleware\Auth\SignatureResult;
|
||||
|
||||
/**
|
||||
* PrivateKeySigner
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
class PrivateKeySigner implements Signer
|
||||
{
|
||||
/**
|
||||
* Certificate Serial Number
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $certificateSerialNumber;
|
||||
|
||||
/**
|
||||
* Merchant certificate private key
|
||||
*
|
||||
* @var string|resource
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $serialNo Merchant Certificate Serial Number
|
||||
* @param string|resource $privateKey Merchant Certificate Private Key \
|
||||
* (string - PEM formatted key, or resource - key returned by openssl_get_privatekey)
|
||||
*/
|
||||
public function __construct($serialNumber, $privateKey)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* SignatureResult
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Auth;
|
||||
|
||||
/**
|
||||
* SignatureResult
|
||||
*
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
*/
|
||||
class SignatureResult
|
||||
{
|
||||
/**
|
||||
* Signature
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $sign;
|
||||
|
||||
/**
|
||||
* Certificate Serial Number
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $certificateSerialNumber;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($sign, $serialNumber)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Signer
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Auth;
|
||||
|
||||
use WechatPay\GuzzleMiddleware\Auth\SignatureResult;
|
||||
|
||||
/**
|
||||
* Interface abstracting Signer.
|
||||
*
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
*/
|
||||
interface Signer
|
||||
{
|
||||
|
||||
/**
|
||||
* Sign Message
|
||||
*
|
||||
* @param string $message Message to sign
|
||||
*
|
||||
* @return SignatureResult
|
||||
*/
|
||||
public function sign($message);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Verifier
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Auth;
|
||||
|
||||
/**
|
||||
* Interface abstracting Verifier.
|
||||
*
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
*/
|
||||
interface Verifier
|
||||
{
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* WechatPay2Credentials
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Auth;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use WechatPay\GuzzleMiddleware\Credentials;
|
||||
use WechatPay\GuzzleMiddleware\Auth\Signer;
|
||||
|
||||
/**
|
||||
* WechatPay2Credentials
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay\GuzzleMiddleware\Auth
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
class WechatPay2Credentials implements Credentials
|
||||
{
|
||||
|
||||
/**
|
||||
* Merchant Id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $merchantId;
|
||||
|
||||
/**
|
||||
* signer
|
||||
*
|
||||
* @var Signer
|
||||
*/
|
||||
protected $signer;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($merchantId, Signer $signer)
|
||||
{
|
||||
$this->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";
|
||||
}
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/**
|
||||
* WechatPay2Validator
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Auth;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use WechatPay\GuzzleMiddleware\Validator;
|
||||
use WechatPay\GuzzleMiddleware\Auth\Verifier;
|
||||
|
||||
/**
|
||||
* WechatPay2Validator
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay\GuzzleMiddleware\Auth
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
class WechatPay2Validator implements Validator
|
||||
{
|
||||
/**
|
||||
* sign verifier
|
||||
*
|
||||
* @var Verifier
|
||||
*/
|
||||
protected $verifier;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct(Verifier $verifier)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Credentials
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Interface abstracting Credentials.
|
||||
*
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
*/
|
||||
interface Credentials
|
||||
{
|
||||
/**
|
||||
* Get schema of credentials
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSchema();
|
||||
|
||||
/**
|
||||
* Get token of credentials
|
||||
*
|
||||
* @param RequestInterface $request Api request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getToken(RequestInterface $request);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* AesUtil
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChatPay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Util;
|
||||
|
||||
/**
|
||||
* Util for AEAD_AES_256_GCM.
|
||||
*
|
||||
* @package WechatPay
|
||||
* @author WeChatPay Team
|
||||
*/
|
||||
class AesUtil
|
||||
{
|
||||
/**
|
||||
* AES key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $aesKey;
|
||||
|
||||
const KEY_LENGTH_BYTE = 32;
|
||||
const AUTH_TAG_LENGTH_BYTE = 16;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $aesKey API V3 Key
|
||||
*
|
||||
*/
|
||||
public function __construct($aesKey)
|
||||
{
|
||||
if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {
|
||||
throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节');
|
||||
}
|
||||
$this->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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/**
|
||||
* MediaUtil
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChatPay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Util;
|
||||
|
||||
use GuzzleHttp\Psr7\LazyOpenStream;
|
||||
use GuzzleHttp\Psr7\MultipartStream;
|
||||
use GuzzleHttp\Psr7\FnStream;
|
||||
use GuzzleHttp\Psr7\CachingStream;
|
||||
|
||||
/**
|
||||
* Util for Media(image or video) uploading.
|
||||
*
|
||||
* @package WechatPay
|
||||
*/
|
||||
class MediaUtil {
|
||||
|
||||
/**
|
||||
* local file path
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $filepath;
|
||||
|
||||
/**
|
||||
* file content stream to upload
|
||||
* @var string
|
||||
*/
|
||||
private $fileStream;
|
||||
|
||||
/**
|
||||
* upload meta json
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $json;
|
||||
|
||||
/**
|
||||
* upload contents stream
|
||||
*
|
||||
* @var MultipartStream
|
||||
*/
|
||||
private $multipart;
|
||||
|
||||
|
||||
/**
|
||||
* multipart stream wrapper
|
||||
*
|
||||
* @var FnStream
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $filepath The media file path or file name,
|
||||
* should be one of the
|
||||
* images(jpg|bmp|png)
|
||||
* or
|
||||
* video(avi|wmv|mpeg|mp4|mov|mkv|flv|f4v|m4v|rmvb)
|
||||
* @param StreamInterface $fileStream File content stream, optional
|
||||
*/
|
||||
public function __construct($filepath, $fileStream = null)
|
||||
{
|
||||
$this->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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* AuthUtil
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChatPay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware\Util;
|
||||
|
||||
/**
|
||||
* Util for read private key and certificate.
|
||||
*
|
||||
* @package WechatPay
|
||||
* @author WeChatPay Team
|
||||
*/
|
||||
class PemUtil
|
||||
{
|
||||
/**
|
||||
* Read private key from file
|
||||
*
|
||||
* @param string $filepath PEM encoded private key file path
|
||||
*
|
||||
* @return resource|bool Private key resource identifier on success, or FALSE on error
|
||||
*/
|
||||
public static function loadPrivateKey($filepath)
|
||||
{
|
||||
return \openssl_get_privatekey(\file_get_contents($filepath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read certificate from file
|
||||
*
|
||||
* @param string $filepath PEM encoded X.509 certificate file path
|
||||
*
|
||||
* @return resource|bool X.509 certificate resource identifier on success or FALSE on failure
|
||||
*/
|
||||
public static function loadCertificate($filepath)
|
||||
{
|
||||
return \openssl_x509_read(\file_get_contents($filepath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read private key from string
|
||||
*
|
||||
* @param string $content PEM encoded private key string content
|
||||
*
|
||||
* @return resource|bool Private key resource identifier on success, or FALSE on error
|
||||
*/
|
||||
public static function loadPrivateKeyFromString($content)
|
||||
{
|
||||
return \openssl_get_privatekey($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read certificate from string
|
||||
*
|
||||
* @param string $content PEM encoded X.509 certificate string content
|
||||
*
|
||||
* @return resource|bool X.509 certificate resource identifier on success or FALSE on failure
|
||||
*/
|
||||
public static function loadCertificateFromString($content)
|
||||
{
|
||||
return \openssl_x509_read($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Serial Number from Certificate
|
||||
*
|
||||
* @param string|resource $certifcates Certificates (string - PEM formatted certificate, \
|
||||
* or resource - X.509 certificate resource returned by loadCertificate or openssl_x509_read)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function parseCertificateSerialNo($certificate)
|
||||
{
|
||||
$info = \openssl_x509_parse($certificate);
|
||||
if (!isset($info['serialNumber']) && !isset($info['serialNumberHex'])) {
|
||||
throw new \InvalidArgumentException('证书格式错误');
|
||||
}
|
||||
|
||||
$serialNo = '';
|
||||
// PHP 7.0+ provides serialNumberHex field
|
||||
if (isset($info['serialNumberHex'])) {
|
||||
$serialNo = $info['serialNumberHex'];
|
||||
} else {
|
||||
// PHP use i2s_ASN1_INTEGER in openssl to convert serial number to string,
|
||||
// i2s_ASN1_INTEGER may produce decimal or hexadecimal format,
|
||||
// depending on the version of openssl and length of data.
|
||||
if (\strtolower(\substr($info['serialNumber'], 0, 2)) == '0x') { // HEX format
|
||||
$serialNo = \substr($info['serialNumber'], 2);
|
||||
} else { // DEC format
|
||||
$value = $info['serialNumber'];
|
||||
$hexvalues = ['0','1','2','3','4','5','6','7',
|
||||
'8','9','A','B','C','D','E','F'];
|
||||
while ($value != '0') {
|
||||
$serialNo = $hexvalues[\bcmod($value, '16')].$serialNo;
|
||||
$value = \bcdiv($value, '16', 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return \strtoupper($serialNo);
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/**
|
||||
* SensitiveInfoCrypto
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChatPay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
namespace WechatPay\GuzzleMiddleware\Util;
|
||||
|
||||
/**
|
||||
* Encrypt/Decrypt the sensitive information by the certificates pair.
|
||||
*
|
||||
* <code>
|
||||
* // 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!
|
||||
* </code>
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* Validator
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Interface abstracting Validator.
|
||||
*
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
*/
|
||||
interface Validator
|
||||
{
|
||||
/**
|
||||
* Validate Response
|
||||
*
|
||||
* @param ResponseInterface $response Api response to validate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(ResponseInterface $response);
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
/**
|
||||
* WechatPayMiddleware
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use WechatPay\GuzzleMiddleware\Credentials;
|
||||
use WechatPay\GuzzleMiddleware\Validator;
|
||||
use WechatPay\GuzzleMiddleware\WechatPayMiddlewareBuilder;
|
||||
|
||||
/**
|
||||
* WechatPayMiddleware
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
class WechatPayMiddleware
|
||||
{
|
||||
/**
|
||||
* WechatPayMiddleware version
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = "0.2.0";
|
||||
|
||||
/**
|
||||
* WechatPay API domain
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $API_DOMAINS = [
|
||||
'api.mch.weixin.qq.com',
|
||||
'api2.mch.weixin.qq.com',
|
||||
'apius.mch.weixin.qq.com',
|
||||
'apihk.mch.weixin.qq.com'
|
||||
];
|
||||
|
||||
/**
|
||||
* WechatPay API base urls
|
||||
*
|
||||
* @var array of string
|
||||
*/
|
||||
protected static $BASE_URLS = [
|
||||
'/v3/',
|
||||
'/sandbox/v3/',
|
||||
'/hk/v3/'
|
||||
];
|
||||
|
||||
/**
|
||||
* Merchant credentials
|
||||
*
|
||||
* @var Credentials
|
||||
*/
|
||||
protected $credentials;
|
||||
|
||||
/**
|
||||
* Response Validator
|
||||
*
|
||||
* @var Validator
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct(Credentials $credentials, Validator $validator)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* WechatPayMiddlewareBuilder
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
|
||||
namespace WechatPay\GuzzleMiddleware;
|
||||
|
||||
use WechatPay\GuzzleMiddleware\Credentials;
|
||||
use WechatPay\GuzzleMiddleware\Validator;
|
||||
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
|
||||
use WechatPay\GuzzleMiddleware\Auth\PrivateKeySigner;
|
||||
use WechatPay\GuzzleMiddleware\Auth\CertificateVerifier;
|
||||
use WechatPay\GuzzleMiddleware\Auth\WechatPay2Credentials;
|
||||
use WechatPay\GuzzleMiddleware\Auth\WechatPay2Validator;
|
||||
|
||||
/**
|
||||
* WechatPayMiddlewareBuilder
|
||||
*
|
||||
* @category Class
|
||||
* @package WechatPay
|
||||
* @author WeChat Pay Team
|
||||
* @link https://pay.weixin.qq.com
|
||||
*/
|
||||
class WechatPayMiddlewareBuilder
|
||||
{
|
||||
/**
|
||||
* Merchant credentials
|
||||
*
|
||||
* @var Credentials
|
||||
*/
|
||||
protected $credentials;
|
||||
|
||||
/**
|
||||
* Response Validator
|
||||
*
|
||||
* @var Validator
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Merchant Infomation
|
||||
*
|
||||
* @param string $merchantId Merchant Id
|
||||
* @param string $serialNo Merchant Certificate Serial Number
|
||||
* @param string|resource $privateKey Merchant Certificate Private Key (string - PEM formatted key, or resource - key returned by openssl_get_privatekey)
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withMerchant($merchantId, $serialNo, $privateKey)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+218
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
// load autoload.php
|
||||
$possibleFiles = [__DIR__.'/../vendor/autoload.php', __DIR__.'/../../../autoload.php', __DIR__.'/../../autoload.php'];
|
||||
$file = null;
|
||||
foreach ($possibleFiles as $possibleFile) {
|
||||
if (file_exists($possibleFile)) {
|
||||
$file = $possibleFile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null === $file) {
|
||||
throw new RuntimeException('Unable to locate autoload.php file.');
|
||||
}
|
||||
|
||||
require_once $file;
|
||||
unset($possibleFiles, $possibleFile, $file);
|
||||
|
||||
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Handler\CurlHandler;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
|
||||
use WechatPay\GuzzleMiddleware\Validator;
|
||||
use WechatPay\GuzzleMiddleware\Util\PemUtil;
|
||||
use WechatPay\GuzzleMiddleware\Util\AesUtil;
|
||||
use WechatPay\GuzzleMiddleware\Auth\CertificateVerifier;
|
||||
use WechatPay\GuzzleMiddleware\Auth\WechatPay2Validator;
|
||||
|
||||
class CertificateDownloader
|
||||
{
|
||||
const VERSION = '0.1.0';
|
||||
|
||||
public function run()
|
||||
{
|
||||
$opts = $this->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 <<<EOD
|
||||
Usage: 微信支付平台证书下载工具 [-hV] [-c=<wechatpayCertificatePath>]
|
||||
-f=<privateKeyFilePath> -k=<apiV3key> -m=<merchantId>
|
||||
-o=<outputFilePath> -s=<serialNo>
|
||||
-m, --mchid=<merchantId> 商户号
|
||||
-s, --serialno=<serialNo> 商户证书的序列号
|
||||
-f, --privatekey=<privateKeyFilePath>
|
||||
商户的私钥文件
|
||||
-k, --key=<apiV3key> ApiV3Key
|
||||
-c, --wechatpay-cert=<wechatpayCertificatePath>
|
||||
微信支付平台证书,验证签名
|
||||
-o, --output=<outputFilePath>
|
||||
下载成功后保存证书的路径
|
||||
-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();
|
||||
@@ -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=<wechatpayCertificatePath>]
|
||||
-f=<privateKeyFilePath> -k=<apiV3key> -m=<merchantId>
|
||||
-o=<outputFilePath> -s=<serialNo>
|
||||
-m, --mchid=<merchantId> 商户号
|
||||
-s, --serialno=<serialNo> 商户证书的序列号
|
||||
-f, --privatekey=<privateKeyFilePath>
|
||||
商户的私钥文件
|
||||
-k, --key=<apiV3key> ApiV3Key
|
||||
-c, --wechatpay-cert=<wechatpayCertificatePath>
|
||||
微信支付平台证书,验证签名
|
||||
-o, --output=<outputFilePath>
|
||||
下载成功后保存证书的路径
|
||||
-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)。
|
||||
|
||||
+2
-2
@@ -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:订单总表
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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='服务包表'
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* 微信v3 推送数据解析 php7.1以上 才能使用
|
||||
* 官方解密文件地址 https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi#jie-mi
|
||||
*/
|
||||
class AesUtil
|
||||
{
|
||||
/**
|
||||
* AES key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $aesKey;
|
||||
|
||||
const KEY_LENGTH_BYTE = 32;
|
||||
const AUTH_TAG_LENGTH_BYTE = 16;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($aesKey)
|
||||
{
|
||||
if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {
|
||||
throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节');
|
||||
}
|
||||
$this->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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
require_once "AesUtil.php";
|
||||
$data = isset($_POST) ? $_POST : ''; //原样推送微信数据过来
|
||||
$resource = isset($data['resource']) ? $data['resource'] : '';
|
||||
$nonceStr = isset($resource['nonce']) ? $resource['nonce'] : '';
|
||||
$associatedData = isset($resource['associated_data']) ? $resource['associated_data'] : '';
|
||||
$ciphertext = isset($resource['ciphertext']) ? $resource['ciphertext'] : '';
|
||||
$aesKey = '056afb568e28468defe86dd0102f8d33';
|
||||
$req = ['code'=> 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));
|
||||
}
|
||||
Reference in New Issue
Block a user