PHP开发微信消费者投诉2.0接口,实现在商城管理后台查看并处理消费者投诉。
基本逻辑:
1.调用【创建回调地址】接口创建通知回调,当有新的投诉事件发生、投诉状态发生变化时,商户会收到通知回调;
2.调用【回调报文解密】解密通知回调报文,获取通知参数,可以根据获取到的投诉单号和动作类型判断该通知回调是否已接收,因为同样的通知可能会多次发送;
3.根据获取到投诉单号调用【查询投诉单详情】接口获取投诉单详情,里面的投诉人联系方式是加密的,如果需要用到可以调用【敏感信息解密】接口解密获取联系方式;
4.....目前需求只到这里,后续会继续更新
下面是使用到的相关代码:
protected array $config = [];//配置信息
public function __construct()
{
$this->config = ...;//获取配置信息
}
/**
* @notes 生成签名
* @param $url //请求URL
* @param $http_method //请求方式
* @param $data //请求参数
* @param $apiclient_cert //微信支付证书
* @param $apiclient_key //微信支付证书密钥
* @param $mch_id //微信支付商户号
* @return string
*/
public function token($url,$http_method,$data,$apiclient_cert,$apiclient_key,$mch_id)
{
$url_parts = parse_url($url);//获取请求的绝对URL
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$timestamp = time();//系统当前时间戳
$nonce = $timestamp.rand('10000','99999');//请求随机串
$body = empty($data) ? '' : json_encode((object)$data);//请求报文主体
$apiclient_cert_arr = openssl_x509_parse(file_get_contents($apiclient_cert));
$serial_no = $apiclient_cert_arr['serialNumberHex'];//证书序列号
$mch_private_key = file_get_contents($apiclient_key);//密钥
$merchant_id = $mch_id;//商户id
$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",timestamp="%d",serial_no="%s",signature="%s"',
$merchant_id, $nonce, $timestamp, $serial_no, $sign);//签名信息
return $schema.' '.$token;
}
/**
* @notes 发送请求
* @param $url //请求URL
* @param $http_method //请求方式
* @param $data //请求参数
* @param $token //签名
* @return bool|string
*/
public function request($url,$http_method,$data,$token)
{
$curl = curl_init();//初始化
curl_setopt($curl, CURLOPT_URL, (string)$url);//需要获取的URL地址
if ($http_method == 'POST'){
curl_setopt($curl, CURLOPT_POST, true);//启用时会发送一个常规的POST请求,类型为:application/x-www-form-urlencoded,就像表单提交的一样。
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);//全部数据使用HTTP协议中的"POST"操作来发送。
}
if ($http_method == 'PUT'){
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");//使用一个自定义的请求信息来代替"GET"或"HEAD"作为HTTP请求。
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);//全部数据使用HTTP协议中的"POST"操作来发送。
}
if ($http_method == 'DELETE'){
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');//使用一个自定义的请求信息来代替"GET"或"HEAD"作为HTTP请求。
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);//将curl_exec()获取的信息以文件流的形式返回,而不是直接输出。
//添加请求头
//微信支付API v3使用JSON作为消息体的数据交换格式。请求须设置HTTP头部:
$headers = [
'Authorization:'.$token,
'Accept: application/json',
'Content-Type: application/json',
'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
];
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);//设置HTTP头字段
$output = curl_exec($curl);//抓取URL并把它传递给浏览器
curl_close($curl);//关闭cURL资源,并且释放系统资源
return $output;
}
/**
* @notes 回调报文解密
* @param $associatedData //附加身份验证数据
* @param $nonceStr //随机数
* @param $ciphertext //密文
* @return false|string
* @throws \SodiumException
*/
public function decryptToString($associatedData,$nonceStr,$ciphertext)
{
$apiv3Key = $this->config['apiv3_key'];//Apiv3密钥
$ciphertext = \base64_decode($ciphertext);
if (strlen($ciphertext) <= 16) {
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, $apiv3Key);
}
// 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, $apiv3Key);
}
// openssl (PHP >= 7.1 support AEAD)
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', $apiv3Key, \OPENSSL_RAW_DATA, $nonceStr,
$authTag, $associatedData);
}
throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
}
/**
* @notes 查询投诉单详情
* @param $complaint_id //投诉单对应的投诉单号
* @return mixed
*/
public function complaintDetail($complaint_id)
{
//请求URL
$url = 'https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/'.$complaint_id;
//请求方式
$http_method = 'GET';
//请求参数
$data = [];
$token = self::token($url,$http_method,$data,$this->config['cert_path'],$this->config['key_path'],$this->config['mch_id']);//获取签名
$result = self::request($url,$http_method,json_encode($data),$token);//发送请求
return json_decode($result,true);
}
/**
* @notes 创建回调地址
* @param $token_url//回调地址
* @return mixed
*/
public function createTokenUrl($token_url)
{
//请求URL
$url = 'https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications';
//请求方式
$http_method = 'POST';
//请求参数
$data = [
'url' => $token_url,//通知地址
];
$token = self::token($url,$http_method,$data,$this->config['cert_path'],$this->config['key_path'],$this->config['mch_id']);//获取签名
$result = self::request($url,$http_method,json_encode($data),$token);//发送请求
return json_decode($result,true);
}
/**
* @notes 查询回调地址
* @return mixed
*/
public function queryTokenUrl()
{
//请求URL
$url = 'https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications';
//请求方式
$http_method = 'GET';
//请求参数
$data = [];
$token = self::token($url,$http_method,$data,$this->config['cert_path'],$this->config['key_path'],$this->config['mch_id']);//获取签名
$result = self::request($url,$http_method,json_encode($data),$token);//发送请求
return json_decode($result,true);
}
/**
* @notes 更新回调地址
* @param $token_url//回调地址
* @return mixed
*/
public function updateTokenUrl($token_url)
{
//请求URL
$url = 'https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications';
//请求方式
$http_method = 'PUT';
//请求参数
$data = [
'url' => $token_url,//通知地址
];
$token = self::token($url,$http_method,$data,$this->config['cert_path'],$this->config['key_path'],$this->config['mch_id']);//获取签名
$result = self::request($url,$http_method,json_encode($data),$token);//发送请求
return json_decode($result,true);
}
/**
* @notes 删除回调地址
* @return mixed
*/
public function deleteTokenUrl()
{
//请求URL
$url = 'https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications';
//请求方式
$http_method = 'DELETE';
//请求参数
$data = [];
$token = self::token($url,$http_method,$data,$this->config['cert_path'],$this->config['key_path'],$this->config['mch_id']);//获取签名
$result = self::request($url,$http_method,json_encode($data),$token);//发送请求
return json_decode($result,true);
}
/**
* @notes 敏感信息解密
* @param $waitDecrypt //待解密信息
* @return mixed
*/
public function getDecrypt($waitDecrypt)
{
$apiclient_key = $this->config['key_path'];//微信支付证书密钥
$privateKey = openssl_get_privatekey(file_get_contents($apiclient_key));
openssl_private_decrypt(base64_decode($waitDecrypt),$decrypted,$privateKey, OPENSSL_PKCS1_OAEP_PADDING);
return $decrypted;
}
注意:
1.通知回调地址,仅支持https;
2.同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
微信开发文档链接:微信支付-开发者文档