需要一个小微商户的sdk,但是找了很久找不到合适的,要不就是太复杂
自己封装了一部分功能,没有封装完毕发现小微商户进件api停用了,共享出来共同学习进步
目录:vendor\wechat\wechat.php
<?php
class wechat {
public $sslCertPath="";
//证书
public $sslKeyPath="";
//密钥
public $publicKeyAddr="";
//平台证书地址(api获取的,不是微信后台配置证书)
public $saveserial_noddr='';
//证书编号地址(api获取的,不是微信后台配置的那个证书编号)
public $serial_no="";
//平台证书编号
public $publicKey="";
//平台证书解密文本(证书明文)
public $aes_key="";
function __construct($array) {
$this->sslCertPath=dirname(__FILE__)."/certificate/apiclient_cert.pem";
$this->sslKeyPath=dirname(__FILE__)."/certificate/apiclient_key.pem";
$this->publicKeyAddr=dirname(__FILE__)."/certificate/certificate.pem";
$this->saveserial_noddr=dirname(__FILE__)."/certificate/serial_no.pem";
}
/**
* 对参数进行sign签名
* @param undefined $args
* @param undefined $key
*
* @return
*/
public function getmd5Sign($args,$key) {
ksort($args);
$stringA = '';
$stringSignTemp = '';
foreach($args as $k => $v) {
$stringA .= $k . '=' . $v . '&';
}
$stringSignTemp = $stringA.'key='.$key;
$sign = strtoupper(md5($stringSignTemp));
return $sign;
}
/**
* Create the wechat pay sign with hmac-sha256
* @param $data
* @param $mach_key
* @return string
*/
public function getsha256Sign ($data, $mach_key) {
ksort($data);
$data = array_filter($data, function ($v, $k) {
if ($k == "sign" && $v == '' && is_array($v)) {
return false;
}
return true;
}
, ARRAY_FILTER_USE_BOTH);
$str = http_build_query($data)."&key=".$mach_key;
$str = urldecode($str);
//处理中文乱码
return strtoupper(hash_hmac("sha256", $str, $mach_key));
}
//生成随机数
public function randomkeys($length) {
$returnStr='';
$pattern = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for ($i = 0; $i < $length; $i ++) {
$returnStr .= $pattern {
mt_rand ( 0, 61 )
}
;
}
return $returnStr;
}
/**
* 提交http请求
* @param undefined $url 提交地址
* @param undefined $data 提交post数据
* @param undefined $header 提交协议头
* @param undefined $isusercert 开启ssl证书
*
* @return
*/
public function httpsRequest($url, $data = null, $header=array('Content-type: application/json;charset=UTF-8'),$isusercert=false) {
$curl = curl_init();
//初始化
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
//允许 cURL 函数执行的最长秒数。
/*if (!empty($port)) {
curl_setopt($curl, CURLOPT_PORT, $port);//可选的用来指定连接端口,默认80端口可不写
}*/
curl_setopt($curl, CURLOPT_HTTPHEADER,$header);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//设置请求目标url头部信息
if (!empty($data)) {
//$data不为空,发送post请求
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
//$data:数组
}
if($isusercert==true) {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
curl_setopt($curl, CURLOPT_SSLCERT,$this->sslCertPath);//请求证书加密
curl_setopt($curl, CURLOPT_SSLKEY, $this->sslKeyPath);//私钥
} else {
if(substr($url,0,5)=="https") {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
}
}
$output = curl_exec($curl);
//执行命令
$error = curl_error($curl);
//错误信息
if ($error || $output == FALSE) {
//报错信息
return 'ERROR:' . curl_error($curl);
}
curl_close($curl);
return $output;
}
/*xml格式转换数组格式函数*/
function xml_to_array($xml) {
$array = (array)(simplexml_load_string($xml));
foreach ($array as $key=>$item) {
$array[$key] = struct_to_array((array)$item);
}
return $array;
}
/**
* 将数组转换为xml
* @param array $data 要转换的数组
* @param bool $root 是否要根节点
* @return string xml字符串
* @author Dragondean
* @url http://www.cnblogs.com/dragondean
*/
public function arr_to_xml($data, $root = true) {
$str = "";
if ($root) {
$str .= "<xml>";
}
foreach ($data as $key => $val) {
if (is_array($val)) {
$child = arr_to_xml($val, false);
$str .= "<$key>$child</$key>";
} else {
$str .= "<$key><![CDATA[$val]]></$key>";
}
}
if ($root) {
$str .= "</xml>";
}
return $str;
}
public function errinfo($errcode) {
$errmsg="未知错误";
switch ($errcode) {
case 'NVALID_REQUEST':
$errmsg = "请使用post 方法,请检查后重新提交" ;
break;
case 'INVALID_REQUEST':
$errmsg = "xml参数格式错误,请检查后重新提交" ;
break;
case 'SIGNERROR':
$errmsg = "签名校验失败,请检查后重试";
break;
case 'INVALID_REQUEST':
$errmsg = "需要证书" ;
break;
case 'PARAM_ERROR':
$errmsg = "参数错误" ;
break;
case 'INVALID_REQUEST':
$errmsg = "暂无权限,请检查后重试" ;
break;
case 'FREQUENCY_LIMITED':
$errmsg = "操作过快,请稍后重试" ;
break;
case 'SYSTEMERROR':
$errmsg = "上传图片失败,请稍后重试";
break;
default:
$errmsg = "未知错误";
}
}
/**
* xml转换对象编程
* @param undefined $payInfo
*
* @return
*/
public function xmltoobj($payInfo) {
//$payInfo=htmlspecialchars($payInfo);
$payInfo=html_entity_decode((string)$payInfo);
$payInfo =json_decode(json_encode(simplexml_load_string($payInfo, 'SimpleXMLElement', LIBXML_NOCDATA)),false);
return $payInfo;
}
/**
* 获取平台证书
* @param undefined $mch_id
* @param undefined $mach_key
*
* @return
*/
public function getcertficates($mch_id,$mach_key,$aes_key) {
$data['mch_id']=$mch_id;
$data['nonce_str']=$this->randomkeys(32);
$data['sign_type']="HMAC-SHA256";
$data['sign']=$this->getsha256Sign($data,$mach_key);
$header = [
"content-type:multipart/form-data"
];
$res = $this->httpsRequest("https://api.mch.weixin.qq.com/risk/getcertficates", $this->arr_to_xml($data),$header,true);
if(strpos($res,"ERROR:")) {
die($res);
}
$res=$this->xmltoobj($res);
$certificates = json_decode($res->certificates);
$this->decryptCiphertext($certificates->data[0],$aes_key);
$this->serial_no=$this->saveserial_no($certificates->data[0]->serial_no);
if($this->serial_no) {
return true;
} else {
return false;
}
}
/**
* 提交小微商户申请
* @param undefined $data
*
* @return
*/
public function microsubmit($data) {
$header = [
"content-type:multipart/form-data",
];
$res = $this->httpsRequest("https://api.mch.weixin.qq.com/applyment/micro/submit", $this->arr_to_xml($data),$header,true);
if(strstr($res,"ERROR:")) {
die($res);
}
return $this->xmltoobj($res);
}
/**
* 查询申请状态
* @param undefined $data
*
* @return
*/
public function microgetstate($data) {
$header = [
"content-type:multipart/form-data",
];
$res = $this->httpsRequest("https://api.mch.weixin.qq.com/applyment/micro/getstate", $this->arr_to_xml($data),$header,true);
if(strstr($res,"ERROR:")) {
die($res);
}
return $this->xmltoobj($res);
}
//region 敏感字段加密start
public function getEncrypt($str) {
//$str是待加密字符串
$public_key = file_get_contents($this->publicKeyAddr);
$encrypted = '';
openssl_public_encrypt($str,$encrypted,$public_key);
//base64编码
$sign = base64_encode($encrypted);
return $sign;
}
/**
* publicKeyEncrypt 对身份证等敏感信息加密
* @param string $string
* @return string
* @throws WxException
*/
public function publicKeyEncrypt(string $string)
{
$crypted = '';
$publicKey = $this->getPublicKey();
if ($publicKey) {
$publicKeyResource = openssl_get_publickey($publicKey);
$f = openssl_public_encrypt($string, $crypted, $publicKeyResource, OPENSSL_PKCS1_PADDING);
openssl_free_key($publicKeyResource);
if ($f) {
return base64_encode($crypted);
}
}
}
/**
* savePublicKey 保存公共证书编号
* @param $plaintext
*/
public function saveserial_no($plaintext) {
$this->serial_no = $plaintext;
file_put_contents($this->saveserial_noddr, $plaintext);
return $plaintext;
}
/**
* getserial_no 获取上一次保存的证书编号
* @return bool|string
*/
public function getserial_no() {
if (file_exists($this->publicKeyAddr))
return $this->serial_no ? : $this->serial_no = file_get_contents($this->saveserial_noddr);
return '';
}
/**
* getPublicKey 获取上一次本地保存的公钥
* @return bool|string
*/
public function getPublicKey() {
if (file_exists($this->publicKeyAddr))
return $this->publicKey ? : $this->publicKey = file_get_contents($this->publicKeyAddr);
return '';
}
/**
* savePublicKey 保存解密后的明文
* @param $plaintext
*/
public function savePublicKey($plaintext) {
$this->publicKey = $plaintext;
file_put_contents($this->publicKeyAddr, $plaintext);
return $plaintext;
}
/**
* decryptCiphertext AEAD_AES_256_GCM 解密加密后的证书内容得到平台证书的明文
* @param $ciphertext
* @param $ad
* @param $nonce
* @return string
*/
public function decryptCiphertext($data,$aes_key) {
$encryptCertificate = $data->encrypt_certificate;
$ciphertext = base64_decode($encryptCertificate->ciphertext);
$associated_data = $encryptCertificate->associated_data;
$nonce = $encryptCertificate->nonce;
// sodium_crypto_aead_aes256gcm_decrypt >=7.2版本,去php.ini里面开启下libsodium扩展就可以,之前版本需要安装libsodium扩展,具体查看php.net(ps.使用这个函数对扩展的版本也有要求哦,扩展版本 >=1.08)
$plaintext = sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associated_data, $nonce, $aes_key);
$this->savePublicKey($plaintext);
return true;
}
/**
* 设置apiv3 key
* @param undefined $aes_key
*
* @return
*/
public function setaes_key($aes_key) {
if($aes_key) {
$this->aes_key=$aes_key;
} else {
die("setaes_key调用出错,aes_key为空");
}
}
}
?>
<?php
namespace app\admin\controller;
use app\admin\controller\Common;
use app\service\smallbusinessService;
use think\Db;
use Think\Log;
use wei\Wechat;
class Smallbusiness extends Common {
public function add() {
return $this->fetch('add');
}
//微信图片资料上传
public function uploadmedia() {
$re=array('code'=>'0','info'=>[],'msg'=>"上传成功");
vendor('wechat.wechat');
$wx=new \wechat(array());
// 获取表单上传文件 例如上传了001.jpg
$file = request()->file('file');
// 移动到框架应用根目录/public/uploads/ 目录下
if($file) {
$info = $file->move(ROOT_PATH . 'public' . DS . 'uploads');
if($info) {
$filename=$info->getSaveName();
$re['info']['path']=$filename;
$res=$this->uploadImage($filename);
if($res->result_code=='SUCCESS')
{
$re['info']['media_id']=$res->media_id;
return $re;
}else{
$re['code']=-1;
$re['msg']=$wx->errinfo($errcode);
return $re;
}
$re['code']=-1;
$re['msg']="wx图片上传api调用异常";
return json($re);
} else {
// 上传失败获取错误信息
echo $file->getError();
}
}
}
//上传至微信服务器并获取media_id
public function uploadImage($file) {
$mch_id=config('wxwechatsp.mch_id');
$key=config('wxwechatsp.key');
vendor('wechat.wechat');
$path = ROOT_PATH . 'public' . DS . 'uploads/'.$file;
$data = [
'mch_id' => $mch_id,
'sign_type' => "HMAC-SHA256",
'media_hash' => strtolower(md5_file($path))
];
$wx=new \wechat(array());
$data['sign_type'] = "HMAC-SHA256";
$data['sign'] = $wx->getsha256Sign($data,$key);
$data['media'] = new \CURLFile($path);
//$data['media'] = '@'.$file;
//$data['media'] = $args['media'] = fopen($path, 'r');
//var_dump($data);
$header = [
"content-type:multipart/form-data",
"content-type:xml"
];
$res = $wx->httpsRequest("https://api.mch.weixin.qq.com/secapi/mch/uploadmedia", $data,$header,true);
if(strstr($res,"ERROR"))
{
die($res);
}
//var_dump((string)$res);exit();
return $wx->xmltoobj($res);
}
//提交小微商户申请资料
public function microsubmit()
{
//文档查阅:https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=19_2
$id = request()->param('id');
if(!isset($id) && empty($id)){
$this->error('参数id未传递');
}
$mch_id=config('wxwechatsp.mch_id');
$key=config('wxwechatsp.key');
$apiv3sky=config('wxwechatsp.apiv3sky');
vendor('wechat.wechat');
$wx=new \wechat(array());
$info = (new smallbusinessService())->smallbusinessIdInfo($id);
if(!$info['id_card_valid_time_end'])
{
$info['id_card_valid_time_end']="长期";
}
$wx->getcertficates($mch_id,$key,$apiv3sky);//更新证书信息
$data=array(
'version'=>'3.0',
'cert_sn'=>$wx->getserial_no(),
'mch_id'=>$mch_id,
'nonce_str'=>$wx->randomkeys(32),
'sign_type'=>'HMAC-SHA256',
'business_code'=>$id,
'id_card_copy'=>$info['id_card_copy'],
'id_card_national'=>$info['id_card_national'],
'id_card_name'=>$wx->publicKeyEncrypt($info['id_card_name']),
'id_card_number'=>$wx->publicKeyEncrypt($info['id_card_number']),
'id_card_valid_time'=>'["'.$info['id_card_valid_time_start'].'","'.$info['id_card_valid_time_end'].'"]',
'account_name'=>$wx->publicKeyEncrypt($info['account_name']),
'account_bank'=>$info['account_bank'],
'bank_address_code'=>$info['bank_address_code'],
'bank_name'=>$info['bank_name'],
'account_number'=>$wx->publicKeyEncrypt($info['account_number']),
'store_name'=>$info['store_name'],
'store_address_code'=>$info['store_address_code'],
'store_street'=>$info['store_street'],
'store_longitude'=>$info['store_longitude'],
'store_latitude'=>$info['store_latitude'],
'store_entrance_pic'=>$info['store_entrance_pic'],
'indoor_pic'=>$info['indoor_pic'],
'address_certification'=>$info['address_certification'],
'merchant_shortname'=>$info['merchant_shortname'],
'service_phone'=>$info['service_phone'],
'product_desc'=>$info['product_desc'],
'rate'=>$info['rate'],
'business_addition_desc'=>$info['business_addition_desc'],
'contact'=>$wx->publicKeyEncrypt($info['contact']),
'contact_phone'=>$wx->publicKeyEncrypt($info['contact_phone']),
'contact_email'=>$wx->publicKeyEncrypt($info['contact_email']),
);
$data['sign']=$wx->getsha256Sign($data,$key);
$res=$wx->microsubmit($data);
if($res->return_code=="SUCCESS" and $res->result_code=='SUCCESS')
{
$applyment_id=$res->applyment_id;
Db::table('think_user')->where('id',1)->setField('applyment_id',$applyment_id);//提交成功后写入商户申请单号
Db::table('think_user')->where('id',1)->setField('state',1);//设置状态未审核中
$this->success('提交成功,请等待5分钟左右后继续查看',url('list'));
}else
{
if($res->return_code!="SUCCESS")
{
$this->error($res->return_msg,url('list'));
}
if($res->result_code!="SUCCESS")
{
if($res->err_code=='PARAM_ERROR')
{
if(!$res->err_param){
die("以下字段填写错误".$res->err_param);
}else
{
$this->error($res->err_code_des,url('list'));
}
}
}
}
}
//查询申请进度
public function microgetstate()
{
//https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=19_3
$id = request()->param('id');
if(!isset($id) && empty($id)){
$this->error('参数id未传递');
}
$mch_id=config('wxwechatsp.mch_id');
$key=config('wxwechatsp.key');
vendor('wechat.wechat');
$wx=new \wechat(array());
$info = (new smallbusinessService())->smallbusinessIdInfo($id);
if(!$info['applyment_id'])
{
$this->error('尚未提交审核,无法查看进度');
}
$data=array(
'version'=>'1.0',
'mch_id'=>$mch_id,
'nonce_str'=>$wx->randomkeys(32),
'sign_type'=>'HMAC-SHA256',
'applyment_id'=>$info['applyment_id'],
'business_code'=>$id,
);
$data['sign']=$wx->getsha256Sign($data,$key);
$res=$wx->microgetstate($data);
if($res->return_code=="SUCCESS" and $res->result_code=='SUCCESS')
{
if($res->applyment_state=="AUDITING")
{
$this->success('提交仍在审核中,请稍后查看',url('list'));
}else if($res->applyment_state=="REJECTED")
{
die("审核资料被驳回,请检查如下提示继续提交:".$res->audit_detail);
}else if($res->applyment_state=="FROZEN")
{
die("api提交审核资料时提示已冻结,请联系微信客服");
}else if($res->applyment_state=="TO_BE_SIGNED")
{
//待签约
Db::table('smallbusiness')->where('id',$id)->setField('sub_mch_id',$res->sub_mch_id);//提交成功后写入商户申请单号
Db::table('smallbusiness')->where('id',$id)->setField('state',4);//设置状态未审核中
Db::table('smallbusiness')->where('id',$id)->setField('sign_url',$res->sign_url);
$this->success('审核成功,请商户进行签约',url('erweima')."?id=".$id);
}else if($res->applyment_state=="FINISH")
{
//完成
Db::table('smallbusiness')->where('id',$id)->setField('sub_mch_id',$res->sub_mch_id);//提交成功后写入商户申请单号
Db::table('smallbusiness')->where('id',$id)->setField('state',5);
$this->success('签约成功',url('list'));
}
}else
{
if($res->return_code!="SUCCESS")
{
$this->error($res->return_msg,url('list'));
}
if($res->result_code!="SUCCESS")
{
if($res->err_code=='PARAM_ERROR')
{
die("以下字段填写错误".$res->err_param);
}
$this->error($res->err_code_des,url('list'));
}
}
}
//生成二维码,邀请商户扫码签约
public function erweima()
{
$id = request()->param('id');
if(!isset($id) && empty($id)){
$this->error('参数id未传递');
}
vendor("phpqrcode.phpqrcode");
$info = (new smallbusinessService())->smallbusinessIdInfo($id);//获取申请成功后的签约链接
$value =$info['sign_url']; //二维码内容
$qrc=new \QRcode();//php二维码生成库网上有
$dir=ROOT_PATH . 'public' . DS . 'qrcode/'.date('Ymd')."/";
$dir1= DS . 'qrcode/'.date('Ymd')."/";
$filename=date('His').rand(100000,999999).'.png';
@mkdir($dir,0777,true);
$path = $dir.$filename;
$qrc->png($value,$path);
$this->assign('url',$dir1.$filename );
$this->assign('info',$info);
return $this->fetch('erweima');
}
}
以上程序包含了调用微信接口的主要功能,对开发不具备sdk的微信功能有参考意义,
|