1、微信服务商进件,首先我们先来查看微信的官方文档,地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/tool/applyment4sub/chapter3_1.shtml
2、商户上送敏感信息时使用微信支付平台公钥加密,证书序列号包含在请求HTTP头部的Wechatpay-Serial,详见接口规则。
接下来我们就要写我们的程序了,我是在tp5下写的,如果你是在别的框架下,稍微改动下就可以。
/**
*进件
*/
public function add(){
/*判断请求类型(GET、POST、PUT、DELETE)*/
switch (strtolower($this->request->method())){
case "post":
/*接收请求参数*/
$param = $this->request->post();
$param['subject_info']['identity_info']['owner'] = true;
$param['contact_info']['contact_name'] = $this->getEncryptS($param['contact_info']['contact_name']);
$param['contact_info']['mobile_phone'] = $this->getEncryptS($param['contact_info']['mobile_phone']);
$param['contact_info']['contact_email'] = $this->getEncryptS($param['contact_info']['contact_email']);
$param['contact_info']['contact_id_number'] = $this->getEncryptS($param['contact_info']['contact_id_number']);
$param['subject_info']['identity_info']['id_card_info']['id_card_name'] = $this->getEncryptS($param['subject_info']['identity_info']['id_card_info']['id_card_name']);
$param['subject_info']['identity_info']['id_card_info']['id_card_number'] = $this->getEncryptS($param['subject_info']['identity_info']['id_card_info']['id_card_number']);
$url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/' ;
$merchant_id = '******'; //服务商商户号
$serial_no = '********'; //服务商证书序列号
$mch_private_key = $this->getPrivateKey('./cert/apiclient_key.pem');
$timestamp = time();
$nonce = $this->nonce_str();
$sign =$this->sign($url,'POST',$timestamp,$nonce,json_encode($param),$mch_private_key,$merchant_id,$serial_no);//进行签名操作
//设置header
$header =[//设置发送的头信息
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $sign,//签名
'Accept:application/json',
'User-Agent:' . $merchant_id,//服务商的商户号
'Content-Type:application/json',
'Wechatpay-Serial:' . $this->getzhengshu()//获取平台证书的序列号,注意:是平台证书,不是上文的商户证书序列号
];
$result=$this->curl($url,json_encode($param),$header);//curl方法见下文
$result = json_decode($result, true);
$this->ret_data = $result;
break;
default:
$this->code = 1;
$this->msg = "请求方式错误";
break;
}
/*定义接口返回数据*/
$this->success($this->msg,$this->ret_data,$this->code,$this->total);
}
/*
*查询
*/
public function query()
{
/*判断请求类型(GET、POST、PUT、DELETE)*/
switch (strtolower($this->request->method())){
case "post":
//echo 1111;die;
/*接收请求参数*/
$no = input('param.no') ? input('param.no') : '';
/*判断必要参数是否存在*/
if($no){
$url = 'https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/applyment_id/'.$no;
$merchant_id = '******'; //服务商商户号
$serial_no = '*********'; //服务商证书序列号
$mch_private_key = $this->getPrivateKey('./cert/apiclient_key.pem');
$timestamp =time();
$nonce = $this->nonce_str();
$body="";
$sign=$this->sign($url,'GET',$timestamp,$nonce,$body,$mch_private_key,$merchant_id,$serial_no);//签名
$header = [
'Authorization: WECHATPAY2-SHA256-RSA2048 ' .$sign,
'Accept: application/json',
'User-Agent:' .$merchant_id,
'Content-Type :application/json',
'Wechatpay-Serial:' .$this->getzhengshu()
];
$result = $this->curl($url,'',$header, 'GET');
$result = json_decode($result, true);
$this->ret_data = $result;
}else{
/*定义返回参数*/
$this->code = 1;
$this->msg = "请求参数错误!!";
}
break;
default:
$this->code = 1;
$this->msg = "请求方式错误";
break;
}
/*定义接口返回数据*/
$this->success($this->msg,$this->ret_data,$this->code,$this->total);
}
/**
*图片上传
**/
public function uploadMedia()
{
/*判断请求类型(GET、POST、PUT、DELETE)*/
switch (strtolower($this->request->method())){
case "post":
//echo 1111;die;
/*接收请求参数*/
$file = $this->request->file('file');
/*判断必要参数是否存在*/
if($file){
$fileInfo = $file->getInfo();
$url="https://api.mch.weixin.qq.com/v3/merchant/media/upload";//微信的上传地址
$merchant_id = '**********'; //服务商商户号
$serial_no = '***********'; //服务商证书序列号
$mch_private_key = $this->getPrivateKey('./cert/apiclient_key.pem');//读取商户api证书公钥 getPublicKey()获取方法
$timestamp=time();//时间戳
$nonce=$this->nonce_str();//随机字符串
$mime_type=$fileInfo['type'];
$meta=[
'filename'=>$fileInfo['name'],
'sha256'=>hash_file('sha256',$fileInfo['tmp_name'])
];
$sign=$this->sign($url,'POST',$timestamp,$nonce,json_encode($meta),$mch_private_key,$merchant_id,$serial_no);
$boundary=uniqid();
$header=[
'Content-Type:multipart/form-data;boundary=' . $boundary,
'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $sign,
'Accept:application/json',
'User-Agent:' . $merchant_id
];
$body='--' . $boundary . "\r\n";
$body.='Content-Disposition:form-data; name="meta"' . "\r\n";
$body.='Content-Type:application/json' . "\r\n\r\n";
$body.=json_encode($meta)."\r\n";
$body.='--' . $boundary . "\r\n";
$body.='Content-Disposition:form-data;name="file";filename="' . $meta['filename'] . '"' . "\r\n";
$body.='Content-Type:' .$mime_type."\r\n\r\n";
$body.=file_get_contents($fileInfo['tmp_name'])."\r\n";
$body.='--'.$boundary .'--'."\r\n";
$result=$this->curl($url,$body,$header);
var_dump($result);die;
$result=json_decode($result,true);
return $result['media_id'];//返回微信返回来的图片标识
}else{
/*定义返回参数*/
$this->code = 1;
$this->msg = "请求参数错误!!";
}
break;
default:
$this->code = 1;
$this->msg = "请求方式错误";
break;
}
/*定义接口返回数据*/
$this->success($this->msg,$this->ret_data,$this->code,$this->total);
}
/**
*加密字符串
*/
private function getEncryptS($str) {
//$str是要加密的字符串
$public_key_path = './cert/cert.pem' ; //这是获取证书时存起的平台公钥
$public_key = file_get_contents($public_key_path);
$encrypted = '';
if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)){
//base64
$sign = base64_encode($encrypted);
}else{
throw new \Exception('encrypt failed');
}
return $sign;
}
/**
*获取证书
**/
public function getzhengshu(){
$url="https://api.mch.weixin.qq.com/v3/certificates";//获取地址
$timestamp=time();//时间戳
$nonce=$this->nonce_str();//获取一个随机字符串
$body="";
$mch_private_key = $this->getPrivateKey('./cert/apiclient_key.pem'); //调用获取商户私钥方法传证书文件路径进去
$merchant_id = '******'; //服务商商户号
$serial_no = '*********'; //服务商证书序列号
$sign=$this->sign($url,'GET',$timestamp,$nonce,$body,$mch_private_key,$merchant_id,$serial_no);
$header=[
'Authorization:WECHATPAY2-SHA256-RSA2048 '.$sign,
'Accept:application/json',
'User-Agent:'.$merchant_id
];
$result=$this->curl($url,'',$header,'GET');
$result=json_decode($result,true);
$serial_no=$result['data'][0]['serial_no'];//获取的平台证书序列号
$encrypt_certificate=$result['data'][0]['encrypt_certificate'];
$sign_key="JeO9MgDcxUOLOD1ZpvVXIwQaGVWyTPvu"; //APIv3密钥,商户平台API安全中获取
$result=$this->decryptToString($encrypt_certificate['associated_data'],$encrypt_certificate['nonce'],$encrypt_certificate['ciphertext'],$sign_key);
file_put_contents('./cert/cert.pem',$result);//获取的文件临时保存到服务器
return $serial_no;//返回平台证书序列号
}
/**
*解密方法
*/
public function decryptToString($associatedData,$nonceStr,$ciphertext,$aesKey){
$ciphertext=\base64_decode($ciphertext);
if(function_exists('\sodium_crypto_aead_aes256gcm_is_available')&& \sodium_crypto_aead_aes256gcm_is_available()){
return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext,$associatedData,$nonceStr,$aesKey);
}
if(PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())){
$ctext=substr($ciphertext,0,-16);
$authTag=substr($ciphertext,-16);
return \openssl_decrypt(
$ctext,
'aes-256-gcm',
$aesKey,
\OPENSSL_RAW_DATA,
$nonceStr,
$authTag,
$associatedData
);
}
throw new \RuntimeException('php7.1');
}
/**
*curl请求方法
**/
public function curl($url,$data=[],$header,$method='POST'){
$curl=curl_init();
curl_setopt($curl,CURLOPT_URL,$url);
curl_setopt($curl,CURLOPT_HTTPHEADER,$header);
curl_setopt($curl,CURLOPT_HEADER,false);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,false);
if($method=="POST"){
curl_setopt($curl,CURLOPT_POST,TRUE);
curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
}
$result=curl_exec($curl);
curl_close($curl);
return $result;
}
/**
*生成一个随机字符串
*/
private function nonce_str()
{
return date('YmdHis', time()) . rand(18080,99999);
}
/**
*获取商户私钥
*/
public static function getPrivateKey($path)
{
return openssl_get_privatekey(file_get_contents($path));
}
public function sign($url,$http_method,$timestamp,$nonce,$body,$mch_private_key,$merchant_id,$serial_no){
$url_parts=parse_url($url);
$canonical_url=($url_parts['path'].(!empty($url_parts['query'])?"?${url_parts['query']}":""));
$message=
$http_method . "\n".
$canonical_url . "\n".
$timestamp . "\n".
$nonce . "\n".
$body . "\n";
openssl_sign($message,$raw_sign,$mch_private_key,'sha256WithRSAEncryption');
$sign=base64_encode($raw_sign);
$schema='WECHATPAY2-SHA256-RSA2048';
$token=sprintf(
'mchid="%s",nonce_str="%s",signature="%s",timestamp="%d",serial_no="%s"',
$merchant_id,
$nonce,
$sign,
$timestamp,
$serial_no
);
return $token;
}
这里我给一个进件的json示例
{
"business_code":"121a2w32w1q1ew22e",
"contact_info":{
"contact_name":"aa",
"contact_id_number":"2807071996102200313",
"mobile_phone":"18345851000",
"contact_email":"123456@qq.com"
},
"subject_info":{
"subject_type":"SUBJECT_TYPE_INDIVIDUAL",
"business_license_info":{
"license_copy":"iIY9dw0Ra_Y2JBqZFABLeZ1fmaav-d1xIRfzQ4q5ZU-aZejvaF6hNhJt7344iFF531GoMJarKs-8mv8VEBGsSQuJ7eFh3BKzvG3NWM3Y_Ss",
"license_number":"123456789012345678",
"merchant_name":"腾讯科技",
"legal_person":"张三"
},
"identity_info":{
"id_doc_type":"IDENTIFICATION_TYPE_IDCARD",
"id_card_info":{
"id_card_copy":"iIY9dw0Ra_Y2JBqZFABLeZ1fmaav-d1xIRfzQ4q5ZU-aZejvaF6hNhJt7344iFF531GoMJarKs-8mv8VEBGsSQuJ7eFh3BKzvG3NWM3Y_Ss",
"id_card_national":"iIY9dw0Ra_Y2JBqZFABLeXAdngbRQS9zLFMxFmMRPwCvkRkk1597gkU43l3NMx8j-ICo4Ua4tCWm7evA14LtUKy1PfimEkjUGn4Vdapa4-o",
"id_card_name":"aq",
"id_card_number":"2807071996102200313",
"card_period_begin":"2026-06-06",
"card_period_end":"2026-06-06"
},
"owner": "0"
}
},
"business_info":{
"merchant_shortname":"大开口说",
"service_phone":"121231",
"sales_info":{
"sales_scenes_type":["SALES_SCENES_STORE"],
"biz_store_info":{
"biz_store_name":"大郎烧饼",
"biz_address_code":"440305",
"biz_store_address":"南山区xx大厦x层xxxx室",
"store_entrance_pic":["iIY9dw0Ra_Y2JBqZFABLeU4jmfgvCj02z9vP_8mNKfYtFCEybjgpxeGqif6f5GqmdjNLs3v2w57_-M8G_zI98HM2WJehAS1Z6BUZxBEK2lM"],
"indoor_pic":["iIY9dw0Ra_Y2JBqZFABLeXAdngbRQS9zLFMxFmMRPwCvkRkk1597gkU43l3NMx8j-ICo4Ua4tCWm7evA14LtUKy1PfimEkjUGn4Vdapa4-o"]
}
}
},
"settlement_info":{
"settlement_id":"719",
"qualification_type":"餐饮"
}
}
注意进件接口中需要脱敏加密的字段,使用加密方法getEncryptS()进行加密处理
图片上传接口Postman示例
这就大功告成了 自己在完善一下代码的参数验证和逻辑判断就可以了。