API使用大致流程
要使用这个API就要知道准备什么?怎么做?参数从哪里获取?流程是怎么样的?先简单说明一下流程,然后每一步都会有说明
1.准备工作: 服务商id APIv3密钥 服务商证书序列号 服务商证书私钥 微信平台证书 微信平台证书序列号
2.流程说明:
第一步是要先获取微信平台证书和微信平台证书序列号,这两个数据都是要从微信的另外一个接口获取的,为什么要这两个参数呢?因为在传输的过程中数据可能会泄露,因此微信那边就要求对敏感的信息进行加密,比如身份证号码 银行卡号这些信息,而提交申请单这个API也是要用到这些信息,因此要对数据进行加密,所以就要获取这两个参数。
第二步就是图片的media_id,这个media_id是怎么来的?就是通过另外一个接口将我们的身份证图片之类的上传到微信的服务器,接口就会返回这media_id,到时候提交的时候就用media_id。
第三步对这些数据进行签名处理,设置对应的头信息,因为使用了敏感字段加密因此要在请求头加入
Wechatpay-Serial:微信平台证书序列号
第四步发起请求得到结果。
参数的获取
登陆后到这链接 https://pay.weixin.qq.com/index.php/extend/employee
1.服务商id
2.APIv3密钥 这密钥 不能查看,如果忘了或者没有记录只能重置,对线上使用旧密钥的业务有影响 (重置慎重)
3.服务商证书序列号
4.服务商的证书只能下载一次,丢失了只能重置,重置的话会影响,对线上使用旧证书的业务有影响 (重置慎重)
5.微信平台证书 微信平台证书序列号
官方文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/wechatpay/wechatpay5_1.shtml
示范代码 这证书应该是对同一个服务商每次都一样的,可以请求一次后把这两个信息保存起来用,但是不排除会变,因此每次重新获取的话会保险一些。从接口返回的的数据是加密过的,需要解密后才能获取微信平台的证书
<?php
/*
* 获取微信证书和微信平台证书序列号
* 这个微信平台证书和微信平台证书序列号的作用是要来加密提交敏感的数据
* 如身份证姓名 银行卡等这些内容 就要用到这证书来进行加密 序列号要放在请求头里面
* param mch_id string 商户id 在https://pay.weixin.qq.com/index.php/core/cert/api_cert#/ 查看
* param serial_no string 商户证书序列号 在https://pay.weixin.qq.com/index.php/core/cert/api_cert#/ 查看设置
* param v3_key string 商户APIv3密钥 在https://pay.weixin.qq.com/index.php/core/cert/api_cert#/ 设置(这个没的查看不记得了只能重置了,如果线上有在用重置了会有影响)
* param privcate_key string 商户证书密钥
* param wx_serial_no string 微信平台证书序列号
* param certificate string 微信平台证书内容
* param string string 错误信息
* return true or false
*
* privcate_key 商户私钥的路径,文件的内容如下 下面的是简化过的实际获取的比这个长 , 不要选了其他的搞错了
* -----BEGIN PRIVATE KEY-----
*ob8ecfEJ2XifwKrAoe7IbsNXGbcCgYEAxfGMwCO2wIPZtwPYWmYLu5Evo9MaD4vw
*fuZE4l3aKvrndq+flvrFiJn3JnnzUmG9dK34yCN/VFoF9RDxMM/Z89eo89frlHEc
*cRiniY7Yur2OHNCh4BAfm7ObX+72Er6u5ShHCGtlXbojavWXtrhxlgC7UvufxdZg
* -----END PRIVATE KEY-----
*
*
* ertificate 证书内容 下面的是简化过的实际获取的比这个长
* -----BEGIN CERTIFICATE-----
*MIID3DCCAsSgAwIBAgIUJf4xvb+pU36QS1D4caSBMt9RD+cwDQYJKoZIhvcNAQEL
*BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
*FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
* -----END CERTIFICATE-----
*/
function getCertificate($mch_id, $serial_no, $v3_key, $private_key, &$wx_serial_no = '', &$certificate = '', &$msg = '')
{
$url = "https://api.mch.weixin.qq.com/v3/certificates";
$nonce = md5(rand(1000,9999).time().rand(1000,9999));
$sign = sign($url, 'GET', time(), $nonce, '', openssl_get_privatekey(file_get_contents($private_key)), $mch_id, $serial_no);
if (empty($sign)) {
$msg = "签名错误,请核对商户id 商户证书序列号 商户APIv3密钥 商户证书密钥是否正确";
return false;
}
$header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36';
$header[] = 'Accept:application/json';
$header[] = 'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $sign;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);//发送http请求获取密文
curl_close($ch);
$response_data = json_decode($response, true);
print_r($response_data);
if (empty($response_data['data'])) {
$msg = $response_data['message'];
return false;
}
$wx_serial_no = $response_data['data'][0]['serial_no'];
$ciphertext = base64_decode($response_data['data'][0]['encrypt_certificate']['ciphertext']);
$ctext = substr($ciphertext, 0, -16);
$authTag = substr($ciphertext, -16);
$certificate = openssl_decrypt($ctext, 'aes-256-gcm', $v3_key, OPENSSL_RAW_DATA, $response_data['data'][0]['encrypt_certificate']['nonce'], $authTag, $response_data['data'][0]['encrypt_certificate']['associated_data']);
if (empty($certificate)) {
$msg = "解密证书数据失败";
return false;
}
return true;
}
//签名
function sign($url, $http_method, $timestamp, $nonce, $body, $mch_private_key, $mch_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);
if (empty($sign)) return '';
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $mch_id, $nonce, $timestamp, $serial_no, $sign);
return $token;
}
$mch_id = "11111111111";//服务商id 填你自己的
$serial_no = "11111111111";//服务商证书序列号
$v3_key = "11111111111";//服务商VIP3密钥
$private_key = "/web/xcx_interface/Xcxpay/Certificate/Fws/apiclient_key.pem";
getCertificate($mch_id, $serial_no, $v3_key, $private_key, $wx_serial_no, $certificate, $msg);
print_r($wx_serial_no);//微信平台证书序列号
echo "\n";
print_r($certificate);//微信平台证书
print_r($msg);
返回的结果
media_id 的获取
如到时候需要提上传到服务器的图片再上传到微信服务自己看着来封装 修改
<?php
/*
* 上传图片 获取media_id
* param mch_id string 商户id 149851111111111
* param serial_no string 证书序列号 4E2185C22E6BABB9F21XXXXXXXXXX
* param img string 图片 可本地 可网络图片 https://photo.tuchong.com/1887933/f/341365844.jpg or /web/Fws/209116258.jpeg
* param key string 商户私钥 本地文件路径 /web/Fws/apiclient_key.pem
* return
*/
function getMediaId($mch_id, $serial_no, $filename, $key)
{
if (empty($mch_id) || empty($serial_no) || empty($filename) || empty($key)) {
return array();
}
$url = "https://api.mch.weixin.qq.com/v3/merchant/media/upload";
$mch_private_key = openssl_get_privatekey(file_get_contents($key)); //商户私钥
// $fi = new \finfo(FILEINFO_MIME_TYPE);
// $mime_type = $fi->file($filename);
$img_info = getimagesize($filename);
$mime_type = $img_info['mime'];
$data['filename'] = basename($filename);
$meta['filename'] = basename($filename);
$meta['sha256'] = hash_file('sha256', $filename);
$boundary = uniqid(); //分割符号
$date = time();
$nonce = md5(rand(10000, 99999) . date('YmdHis', time()) . rand(10000, 99999));
$sign = sign($url, 'POST', $date, $nonce, json_encode($meta), $mch_private_key, $mch_id, $serial_no);//$http_method要大写
$header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36';
$header[] = 'Accept:application/json';
$header[] = 'Authorization:WECHATPAY2-SHA256-RSA2048 ' . $sign;
$header[] = 'Content-Type:multipart/form-data;boundary=' . $boundary;
$boundaryStr = "--{$boundary}\r\n";
$out = $boundaryStr;
$out .= 'Content-Disposition: form-data; name="meta"' . "\r\n";
$out .= 'Content-Type: application/json' . "\r\n";
$out .= "\r\n";
$out .= json_encode($meta) . "\r\n";
$out .= $boundaryStr;
$out .= 'Content-Disposition: form-data; name="file"; filename="' . $data['filename'] . '"' . "\r\n";
$out .= 'Content-Type: ' . $mime_type . ';' . "\r\n";
$out .= "\r\n";
$out .= file_get_contents($filename) . "\r\n";
$out .= "--{$boundary}--\r\n";
$r = sendCurl($url, $out, $header);
$data = json_decode($r, true);
return $data;
}
function sendCurl($url, $data, $header = array(), $referer = '', $timeout = 30)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_REFERER, $referer);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
//签名
function sign($url, $http_method, $timestamp, $nonce, $body, $mch_private_key, $mch_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);
if (empty($sign)) return '';
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $mch_id, $nonce, $timestamp, $serial_no, $sign);
return $token;
}
$mch_id = "1111111111111";
$serial_no = "1111111111111111";
$private_key = "/web/Certificate/Fws/apiclient_key.pem";
$file = "https://photo.tuchong.com/1887933/f/341365844.jpg";
//$file = "/web/xcx_interface/Xcxpay/Certificate/Fws/209116258.jpeg";
$result = getMediaId($mch_id, $serial_no, $file, $private_key);
print_r($result);
提交数据
<?php
class Fws
{
public $Authorization = 'WECHATPAY2-SHA256-RSA2048';
public $apply_url = "https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/"; //api
public $media_url = "https://api.mch.weixin.qq.com/v3/merchant/media/upload"; //上传图片
public $zs_url = "https://api.mch.weixin.qq.com/v3/certificates"; //获取微信平台证书和微信平台证书序列号
public $wx_serial_no = '';//微信平台证书序列号
public $wx_certificate = '';//获取微信平台证书
public function sendApply($data, $mch_id, $serial_no, $key)
{
$date = time();
$nonce = $this->nonce();
$mch_private_key = $this->getPrivateKey($key);
$sign = $this->sign($this->apply_url, 'POST', $date, $nonce, json_encode($data), $mch_private_key, $mch_id, $serial_no);//$http_method要大写
$header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36';
$header[] = 'Authorization:' . $this->Authorization . ' ' . $sign;
$header[] = 'Wechatpay-Serial:' . $this->wx_serial_no;
$header[] = 'Content-Type: application/json;';
$ch = curl_init($this->apply_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$res = curl_exec($ch);
curl_close($ch);
print_r($res);
exit;
}
/*
* 获取微信证书和微信平台证书序列号
* 这个微信平台证书和微信平台证书序列号的作用是要来加密提交敏感的数据
* 如身份证姓名 银行卡等这些内容 就要用到这证书来进行加密 序列号要放在请求头里面
* param mch_id string 商户id 在https://pay.weixin.qq.com/index.php/core/cert/api_cert#/ 查看
* param serial_no string 商户证书序列号 在https://pay.weixin.qq.com/index.php/core/cert/api_cert#/ 查看设置
* param v3_key string 商户APIv3密钥 在https://pay.weixin.qq.com/index.php/core/cert/api_cert#/ 设置(这个没的查看不记得了只能重置了,如果线上有在用重置了会有影响)
* param privcate_key string 商户证书密钥
* param wx_serial_no string 微信平台证书序列号 引用返回的
* param certificate string 证书内容 要来返回的 如下
* param string string 错误信息
* return true or false
*
* privcate_key 商户私钥的路径,文件的内容如下 下面的是简化过的实际获取的比这个长 , 不要选了其他的搞错了
* -----BEGIN PRIVATE KEY-----
*ob8ecfEJ2XifwKrAoe7IbsNXGbcCgYEAxfGMwCO2wIPZtwPYWmYLu5Evo9MaD4vw
*fuZE4l3aKvrndq+flvrFiJn3JnnzUmG9dK34yCN/VFoF9RDxMM/Z89eo89frlHEc
*cRiniY7Yur2OHNCh4BAfm7ObX+72Er6u5ShHCGtlXbojavWXtrhxlgC7UvufxdZg
* -----END PRIVATE KEY-----
*
*
* ertificate 证书内容 下面的是简化过的实际获取的比这个长
* -----BEGIN CERTIFICATE-----
*MIID3DCCAsSgAwIBAgIUJf4xvb+pU36QS1D4caSBMt9RD+cwDQYJKoZIhvcNAQEL
*BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
*FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
* -----END CERTIFICATE-----
*/
public function getCertificate($mch_id, $serial_no, $v3_key, $private_key, &$wx_serial_no = '', &$certificate = '', &$msg = '')
{
if ($this->wx_serial_no && $this->wx_certificate) {
$wx_serial_no = $this->wx_serial_no;
$certificate = $this->wx_certificate;
return true;
}
$nonce = md5(rand(1000, 9999) . time() . rand(1000, 9999));
$sign = $this->sign($this->zs_url, 'GET', time(), $nonce, '', $this->getPrivateKey($private_key), $mch_id, $serial_no);
if (empty($sign)) {
$msg = "签名错误,请核对商户id 商户证书序列号 商户APIv3密钥 商户证书密钥是否正确";
return false;
}
$header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36';
$header[] = 'Accept:application/json';
$header[] = 'Authorization:' . $this->Authorization . ' ' . $sign;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->zs_url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);//发送http请求获取密文
curl_close($ch);
$response_data = json_decode($response, true);
if (empty($response_data['data'])) {
$msg = $response_data['message'];
return false;
}
$wx_serial_no = $response_data['data'][0]['serial_no'];
$ciphertext = base64_decode($response_data['data'][0]['encrypt_certificate']['ciphertext']);
$ctext = substr($ciphertext, 0, -16);
$authTag = substr($ciphertext, -16);
$certificate = openssl_decrypt($ctext, 'aes-256-gcm', $v3_key, OPENSSL_RAW_DATA, $response_data['data'][0]['encrypt_certificate']['nonce'], $authTag, $response_data['data'][0]['encrypt_certificate']['associated_data']);
if (empty($certificate)) {
$msg = "解密证书数据失败";
return false;
}
$this->wx_serial_no = $wx_serial_no;
$this->wx_certificate = $certificate;
return true;
}
//随机字符串
private function nonce()
{
return md5(rand(10000, 99999) . date('YmdHis', time()) . rand(10000, 99999));
}
private function sendCurl($url, $data, $header = array(), $referer = '', $timeout = 30)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
//避免https 的ssl验证
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
// 模拟来源
curl_setopt($ch, CURLOPT_REFERER, $referer);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
//获取私钥
private function getPrivateKey($filepath = '')
{
$filepath = $filepath ? $filepath : $this->private_key_path;
return openssl_get_privatekey(file_get_contents($filepath));
}
//签名
private function sign($url, $http_method, $timestamp, $nonce, $body, $mch_private_key, $mch_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);
if (empty($sign)) return '';
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $mch_id, $nonce, $timestamp, $serial_no, $sign);
return $token;
}
public function getEncrypt($str)
{
if (empty($this->wx_certificate)) {
return '';
}
$encrypted = '';
if (openssl_public_encrypt($str, $encrypted, $this->wx_certificate, OPENSSL_PKCS1_OAEP_PADDING)) {
//base64编码
$sign = base64_encode($encrypted);
} else {
$sign = '';
}
return $sign;
}
}
$mch_id = 1111111111;
$serial_no = '22222222';
$v3_key = '33333333';
$private_key = "/web/apiclient_key.pem";
$fws = new Fws();
$fws->getCertificate($mch_id, $serial_no, $v3_key, $private_key);//获取微信平台证书和微信平台证书序列号 要来给敏感信息加密
//$data要提交的参数
//$fws->getEncrypt() 这个方法就是要来加密敏感信息的
//做测试可以用一个固定的媒体id
$data = array(
'business_code' => 'asadasdaasd11212',
'contact_info' => array(
'contact_name' => $fws->getEncrypt('张三'),
'contact_id_number' => $fws->getEncrypt('440222299207031111'),
'mobile_phone' => $fws->getEncrypt('13211112222'),
'contact_email' => $fws->getEncrypt('888888888@qq.com'),
),
'subject_info' => array(
'subject_type' => 'SUBJECT_TYPE_INDIVIDUAL',
'business_license_info' => array(
'license_copy' => 'sJzx1qoGqnpgF46SoyGH10cIzaPueeRz6V_WV4-xzChImj6wg2Y0V9yGb8BbsrLX-k_unXmwNtITwJcVXs_lm21x4F79SVTkLAMdU7Ai30M',
'license_number' => '123456789012345678',
'merchant_name' => '连续五年',
'legal_person' => '展示',
),
'identity_info' => array(
'id_doc_type' => 'IDENTIFICATION_TYPE_IDCARD',
'id_card_info' => array(
'id_card_copy' => 'sJzx1qoGqnpgF46SoyGH166lltwx9d0KASsWO2hmFoc2SUKt8Iqw5ChM4wxL76MiXji3No2yiSxI5maJC55_mtbafqcMEPFFJNkg7Ub_ixE',
'id_card_national' => 'sJzx1qoGqnpgF46SoyGH166lltwx9d0KASsWO2hmFoc2SUKt8Iqw5ChM4wxL76MiXji3No2yiSxI5maJC55_mtbafqcMEPFFJNkg7Ub_ixE',
'id_card_name' => $fws->getEncrypt('张三'),
'id_card_number' => $fws->getEncrypt('440222299207031111'),
'card_period_begin' => '2026-06-06',
'card_period_end' => '2026-06-06',
),
'owner' => true,
)
),
'business_info' => array(
'merchant_shortname' => '付完成页向买家',
'service_phone' => '02077778888',
'sales_info' => array(
'sales_scenes_type' => array('SALES_SCENES_MINI_PROGRAM'),
'mini_program_info' => array(
'mini_program_sub_appid' => '111111111111'
)
),
),
'settlement_info' => array(
'settlement_id' => '719',
'qualification_type' => '餐饮',
),
'bank_account_info' => array(
'bank_account_type' => 'BANK_ACCOUNT_TYPE_PERSONAL',
'account_name' => $fws->getEncrypt('张三'),
'account_bank' => '工商银行',
'bank_address_code' => '110000',
'account_number' => $fws->getEncrypt('6214831111146606'),
),
);
$result = $fws->sendApply($data, $mch_id, $serial_no, $private_key);
print_r($result);
exit;
成功的话就会返回一个applyment_id
做了些调整
<?php
class Fws
{
public $apply_url = "https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/"; //特约商户-提交申请单
public $get_apply_url = "https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/applyment_id/"; //特约商户-提交申请单
public $media_url = "https://api.mch.weixin.qq.com/v3/merchant/media/upload"; //图片上传
public $zs_url = "https://api.mch.weixin.qq.com/v3/certificates"; //获取微信平台证书和微信平台证书序列号
public $Authorization = 'WECHATPAY2-SHA256-RSA2048';
public $public_key_path = '';//商户证书公匙
public $private_key_path= '';//商户证书密匙
public $wx_serial_no = '';//微信平台证书序列号
public $wx_certificate = '';//微信平台证书
public $mch_id = '';//商户id
public $serial_no = '';//商户证书序列号
public $v3_key = '';//商户VIP3密钥
/*
* 初始化设置参数
* param mch_id string 商户id 1498511111
* param serial_no string 证书序列号 4E2185C22E6BABB9F21XXXXXXXXXX
* param v3_key string 商户VIP3密钥 4E2185C22E6BABB9F21XXXXXXXXXX
* param private_key_path string 商户私钥 本地文件路径 /web/Fws/apiclient_key.pem
* return media_id
*/
public function __construct($mch_id='', $serial_no='', $v3_key='', $private_key_path='')
{
$this->mch_id = $mch_id;
$this->serial_no = $serial_no;
$this->v3_key = $v3_key;
$this->private_key_path = $private_key_path;
}
/*
* 上传图片 获取media_id
* param img string 图片 可本地 可网络图片 https://photo.tuchong.com/1887933/f/341365844.jpg or /web/Fws/209116258.jpeg
* return media_id
*/
public function getMediaId($filename)
{
if (empty($filename)) {
return array();
}
$file_content = file_get_contents($filename);
$img_info = getimagesize($filename);
$mime_type = $img_info['mime'];
$data['filename'] = basename($filename);
$meta['filename'] = basename($filename);
$meta['sha256'] = hash_file('sha256', $filename);
$boundary = uniqid(); //分割符号
$sign = $this->sign($this->media_url, 'POST', json_encode($meta));
$header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36';
$header[] = 'Authorization:'.$this->Authorization.' ' . $sign;
$header[] = 'Content-Type:multipart/form-data;boundary=' . $boundary;
$boundaryStr = "--{$boundary}\r\n";
$out = $boundaryStr;
$out .= 'Content-Disposition: form-data; name="meta"' . "\r\n";
$out .= 'Content-Type: application/json' . "\r\n";
$out .= "\r\n";
$out .= json_encode($meta) . "\r\n";
$out .= $boundaryStr;
$out .= 'Content-Disposition: form-data; name="file"; filename="' . $data['filename'] . '"' . "\r\n";
$out .= 'Content-Type: ' . $mime_type . ';' . "\r\n";
$out .= "\r\n";
$out .= $file_content . "\r\n";
$out .= "--{$boundary}--\r\n";
$result = $this->sendCurl($this->media_url, $out, $header, "POST");
$result = json_decode($result, true);
return $result;
//$result返回结果 // array('media_id'=>'5wbw9R1qxREEdtEJyx1zqtc-BPqdVzidh8ZzBDDNhpdXbHr9_EYcCUy1dHWxMM');
}
//提交特约商户进件申请单
public function sendApply($data)
{
$sign = $this->sign($this->apply_url, 'POST', json_encode($data));
$header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36';
$header[] = 'Authorization:'.$this->Authorization.' ' . $sign;
$header[] = 'Wechatpay-Serial:'.$this->wx_serial_no;
$header[] = 'Content-Type: application/json;';
$result = $this->sendCurl($this->apply_url, json_encode($data), $header, "POST");
$result = json_decode($result, true);
return $result;
//$result返回结果// array('applyment_id'=>'1212121212');
}
//查询特约商户进件申请单
public function getApply($order_no='', &$msg='')
{
if (empty($order_no)) {
$msg = "参数缺少";
return array();
}
$this->get_apply_url= $this->get_apply_url.$order_no;
print_r($this->get_apply_url);
$sign = $this->sign($this->get_apply_url, 'GET', '');
if (empty($sign)) {
$msg = "签名失败";
return array();
}
$header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36';
$header[] = 'Content-Type:application/json';
$header[] = 'Authorization:'.$this->Authorization.' ' . $sign;
$response = $this->sendCurl($this->get_apply_url, '', $header);
$response_data = json_decode($response, true);
return $response_data;
//$response_data返回内容
//array(
// 'applyment_id'=>'2000002194245177',
// 'applyment_state'=>'APPLYMENT_STATE_CANCELED',
// 'applyment_state_msg'=>'申请单已被撤销',
// 'audit_detail'=>array(),
// 'business_code'=>'asddfghjkl12345678901',
// 'sign_url'=>'https://pay.weixin.qq.com/index.php/extend/applyment4sub_weapp/showQRCode?applyment_id=2000002194245177&sign=1581a2b8ad8d44d972bc96cd85793286'
//);
}
//使用微信商户平台证书对敏感信息加密
public function getEncrypt($str){
$result = $this->getCertificate();
if (!$result) {
return '';
}
$encrypted = '';
if (openssl_public_encrypt($str,$encrypted, $this->wx_certificate,OPENSSL_PKCS1_OAEP_PADDING)) {
//base64编码
$encrypt = base64_encode($encrypted);
} else {
$encrypt = '';
}
return $encrypt;
}
/*
* 获取微信证书和微信平台证书序列号
* 这个微信平台证书和微信平台证书序列号的作用是要来加密提交敏感的数据
* 如身份证姓名 银行卡等这些内容 就要用到这证书来进行加密 序列号要放在请求头里面
* param wx_serial_no string 微信平台证书序列号 引用返回的
* param certificate string 证书内容 引用返回的 如下
* param string string 错误信息
* return true or false
* certificate 证书内容 下面的是简化过的实际获取的比这个长
* -----BEGIN CERTIFICATE-----
*MIID3DCCAsSgAwIBAgIUJf4xvb+pU36QS1D4caSBMt9RD+cwDQYJKoZIhvcNAQEL
*BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
*FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
* -----END CERTIFICATE-----
*/
public function getCertificate(&$wx_serial_no = '', &$certificate = '', &$msg = '')
{
if ($this->wx_serial_no && $this->wx_certificate) {
$wx_serial_no = $this->wx_serial_no;
$certificate = $this->wx_certificate;
return true;
}
$sign = $this->sign($this->zs_url, 'GET', '');
if (empty($sign)) {
$msg = "签名错误,请核对商户id 商户证书序列号 商户APIv3密钥 商户证书密钥是否正确";
return false;
}
$header[] = 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36';
$header[] = 'Content-Type:application/json';
$header[] = 'Authorization:'.$this->Authorization.' ' . $sign;
$response = $this->sendCurl($this->zs_url, '', $header);
$response_data = json_decode($response, true);
if (empty($response_data['data'])) {
$msg = $response_data['message'];
return false;
}
$wx_serial_no = $response_data['data'][0]['serial_no'];
$ciphertext = base64_decode($response_data['data'][0]['encrypt_certificate']['ciphertext']);
$ctext = substr($ciphertext, 0, -16);
$authTag = substr($ciphertext, -16);
$certificate = openssl_decrypt($ctext, 'aes-256-gcm', $this->v3_key, OPENSSL_RAW_DATA, $response_data['data'][0]['encrypt_certificate']['nonce'], $authTag, $response_data['data'][0]['encrypt_certificate']['associated_data']);
if (empty($certificate)) {
$msg = "解密证书数据失败";
return false;
}
$this->wx_serial_no = $wx_serial_no;
$this->wx_certificate= $certificate;
return true;
//$response_data返回内容
//array(
// 'data' => array(
// array(
// 'effective_time'=>'2020-05-12T17:17:33+08:00',
// 'encrypt_certificate'=>array(
// 'algorithm' => 'AEAD_AES_256_GCM',
// 'associated_data' => 'certificate',
// 'ciphertext' => 'c8++4NZhnh0H12rTj5YqdThulvmCHmLG+4Wbd+Tvs070z+yWN+E5ZHh2g8R36/qlUC',//证书密文需要解密
// 'nonce' => 'ce69c01d6d56',
// ),
// 'expire_time' => '2025-05-11T17:17:33+08:00',
// 'serial_no' => '25FE31BDBFA9537E904Basdassadasdas'//微信平台证书序列号
// )
// )
//);
//$certificate 解密后的数据 中间内容省略
//-----BEGIN CERTIFICATE-----
//MIID3DCCAsSgAwIBAgIUJf4xvb+pU36QS1D4caSBMt9RD+cwDQYJKoZIhvcNAQEL
//...
//sdORoHD6jnv3if3cESz+1kDb3thBRUvrmgvV5jEAa84=
//-----END CERTIFICATE-----
}
private function sendCurl($url, $body, $header = array(), $method="GET", $referer = '', $timeout = 30)
{
$method = strtoupper($method);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_REFERER, $referer);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
//签名
private function sign($url, $http_method, $body)
{
$http_method = strtoupper($http_method);
$mch_private_key= file_get_contents($this->private_key_path);
$url_parts = parse_url($url);
$timestamp = time();
$nonce = md5(rand(1000,9999).time().rand(1000,99999));
$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);
if (empty($sign)) return '';
$token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $this->mch_id, $nonce, $timestamp, $this->serial_no, $sign);
return $token;
}
}
$mch_id = 111111111;//商户id
$serial_no = '2222222';//商户证书序列号
$v3_key = '33333333';//商户VIP3密钥
$private_key= "/web/apiclient_key.pem";//商户证书密匙
$fws = new Fws($mch_id, $serial_no, $v3_key, $private_key);
//$result = $fws->getMediaId('https://photo.tuchong.com/1887933/f/341365844.jpg');//上传图片获取媒体文件
//$result = $fws->getCertificate($wx_serial_no, $certificate, $msg);//获取微信平台证书和微信平台证书序列号 $this->wx_serial_no $this->wx_certificate
//$result = $fws->getApply('2000002194245177');
//print_r(($fws->getMediaId('/web/xcx_interface/Xcxpay/Certificate/Fws/2222222.png'))['media_id']);exit;
//print_r(($fws->getMediaId('https://photo.tuchong.com/1887933/f/341365844.jpg'))['media_id']);exit;
//print_r($result);exit;
//$data = array(
// 'business_code' => 'asddfghjkl12345678901',
// 'contact_info' => array(
// 'contact_name' => $fws->getEncrypt('张三'),//敏感信息要加密 接口参数有说明 哪些参数是敏感参数需要加密
// 'contact_id_number' => $fws->getEncrypt('440222299217131111'),//敏感信息要加密 接口参数有说明 哪些参数是敏感参数需要加密
// 'mobile_phone' => $fws->getEncrypt('13222221111'),//敏感信息要加密 接口参数有说明 哪些参数是敏感参数需要加密
// 'contact_email' => $fws->getEncrypt('777777777@qq.com'),//敏感信息要加密 接口参数有说明 哪些参数是敏感参数需要加密
// ),
// 'subject_info' => array(
// 'subject_type' => 'SUBJECT_TYPE_INDIVIDUAL',
// 'business_license_info' => array(
// 'license_copy' => ($fws->getMediaId('https://photo.tuchong.com/1887933/f/341365844.jpg'))['media_id'],//获取图片媒体ID 换成自己对应参数得的图片
// 'license_number' => '123456789012345678',
// 'merchant_name' => '连续五年',
// 'legal_person' => '展示',
// ),
// 'identity_info' => array(
// 'id_doc_type' => 'IDENTIFICATION_TYPE_IDCARD',
// 'id_card_info' => array(
// 'id_card_copy' => ($fws->getMediaId('https://photo.tuchong.com/1887933/f/341365844.jpg'))['media_id'],//获取图片媒体ID 换成自己对应参数得的图片
// 'id_card_national' => ($fws->getMediaId('https://photo.tuchong.com/1887933/f/341365844.jpg'))['media_id'],//获取图片媒体ID 换成自己对应参数得的图片
// 'id_card_name' => $fws->getEncrypt('张三'),
// 'id_card_number' => $fws->getEncrypt('440222299217131111'),
// 'card_period_begin' => '2026-06-06',
// 'card_period_end' => '2026-06-06',
// ),
// 'owner' => true,
// )
// ),
// 'business_info' => array(
// 'merchant_shortname' => '付完成页向买家',
// 'service_phone' => '02077778888',
// 'sales_info' => array(
// 'sales_scenes_type' => array('SALES_SCENES_MINI_PROGRAM'),
// 'mini_program_info' => array(
// 'mini_program_sub_appid' => '111111111111'
// )
// ),
// ),
// 'settlement_info' => array(
// 'settlement_id' => '719',
// 'qualification_type' => '餐饮',
// ),
// 'bank_account_info' => array(
// 'bank_account_type' => 'BANK_ACCOUNT_TYPE_PERSONAL',
// 'account_name' => $fws->getEncrypt('张三'),
// 'account_bank' => '工商银行',
// 'bank_address_code' => '110000',
// 'account_number' => $fws->getEncrypt('440222299217131111'),
// ),
//
//);
//$result = $fws->sendApply($data);
//print_r($result);exit;