<?php
namespace app\common\model;
use think\Db;
use think\Model;
use think\Config;
use fast\Http;
/**
*
*/
class Wxpay extends Model
{
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
// 追加属性
protected $append = [
'url',
];
//商家转账到零钱
public static function cash2($body,$out_trade_no,$money,$openid,$dif,$uname=''){
$money = intval(strval($money * 100));
$url = 'https://api.mch.weixin.qq.com/v3/transfer/batches';
$wechat = Config::get('site.wechat');
$wechatapp = Config::get('site.wechatapp');
$wxpay = Config::get('site.wxpay');
$appid = $dif == 1 ? $wechat['app_id'] : $wechatapp['app_id'];
$pars = [];
$pars['appid'] = $appid;//直连商户的appid
$pars['out_batch_no'] = 'sjzz'.$out_trade_no;//商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
$pars['batch_name'] = $body;//该笔批量转账的名称
$pars['batch_remark'] = $body;//转账说明,UTF8编码,最多允许32个字符
$pars['total_amount'] = $money;//转账总金额 单位为“分”
$pars['total_num'] = 1;//转账总笔数
//转账明细列表
if($money >= 200000){
//>>GET获取平台证书列表
$certificates_url = 'https://api.mch.weixin.qq.com/v3/certificates';//GET获取平台证书url
$certificates_sign = self::getSign('GET','',$wxpay['mchid'],$certificates_url);//获取签名
$certificate = self::https_get($certificates_url,$certificates_sign);//发送请求;
$certificate = json_decode($certificate, true);
//判断是否存在相关证书数据
if (!isset($certificate['data'])) {
$error_msg = isset($certificate['message'])?$certificate['message']:'平台证书下载失败!';
throw new HttpException('500',$error_msg);
}
$certificate_serial_no = $certificate['data'][0]['serial_no'];//平台证书序列号
$ciphertext = $certificate['data'][0]['encrypt_certificate']['ciphertext'];
$associatedData = $certificate['data'][0]['encrypt_certificate']['associated_data'];
$nonceStr = $certificate['data'][0]['encrypt_certificate']['nonce'];
$data = self::decryptToString($ciphertext, $associatedData, $nonceStr);
if (!$data) {
throw new HttpException('500','获取证书解密失败');
}
$dir = ROOT_PATH . '/public/public_cert';
!is_dir($dir) && @mkdir($dir,0755,true);
if(!file_exists($dir.'/wx_public_cert.pem')){
//保存平台证书 (https://myssl.com/cert_decode.html)获取证书序列号
file_put_contents(ROOT_PATH . '/public/public_cert/wx_public_cert.pem', $data);
}
if($uname == ''){
return array('code'=>0,'msg'=>'用户姓名不能为空!');
}
$user_name = self::getEncrypt($uname);//姓名加密
$pars['transfer_detail_list'][0] = [
'out_detail_no'=>'Dh'.$out_trade_no,//商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成
'transfer_amount'=>$money,//转账金额单位为分
'transfer_remark'=>$body,//单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符
'openid'=>$openid,//openid是微信用户在公众号appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户
'user_name'=>$user_name//明细转账金额 >= 2,000元,收款用户姓名必填
];
}else{
$certificate_serial_no = '';
$pars['transfer_detail_list'][0] = [
'out_detail_no'=>'Dh'.$out_trade_no,//商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成
'transfer_amount'=>$money,//转账金额单位为分
'transfer_remark'=>$body,//单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符
'openid'=>$openid//openid是微信用户在公众号appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户
];
}
$batches_sign = self::getSign('POST',$pars,$wxpay['mchid'],$url);//获取签名
$res = self::https_request($url,json_encode($pars),$batches_sign,$certificate_serial_no);//发送请求
$resArr = json_decode($res,true);
if(isset($resArr['code'])){
return array('code'=>0,'msg'=>$resArr['message']);
}else{
return array('code'=>1,'msg'=>'success');
}
}
//
public static function https_request($url,$data = null,$sign,$certificate_serial_no=''){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, (string)$url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
if (!empty($data)){
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
if($certificate_serial_no == ''){
//添加请求头
$headers = [
'Authorization:WECHATPAY2-SHA256-RSA2048 '.$sign,
'Accept: application/json',
'Content-Type: application/json; charset=utf-8',
'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
];
}else{
//添加请求头
$headers = [
'Authorization:WECHATPAY2-SHA256-RSA2048 '.$sign,
'Accept: application/json',
'Content-Type: application/json; charset=utf-8',
'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
'Wechatpay-Serial: '.$certificate_serial_no,
];
}
if(!empty($headers)){
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
}
$output = curl_exec($curl);
curl_close($curl);
return $output;
}
public static function https_get($url,$sign){
$curl = curl_init();
//添加请求头
$headers = [
'Authorization:WECHATPAY2-SHA256-RSA2048 '.$sign,
'Accept: 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_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, 3);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
// 执行操作
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
//获取签名(签名生成)
public static function getSign($http_method,$pars,$mchid,$url){
if($http_method == 'GET'){
$body = '';//请求报文主体
}else{
$body = json_encode((object)$pars);//请求报文主体
}
$timestamp = time();//请求时间戳
$url_parts = parse_url($url);//获取请求的绝对URL
$nonce = $timestamp.rand('10000','99999');//请求随机串
$stream_opts = [
"ssl" => [
"verify_peer"=>false,
"verify_peer_name"=>false,
]
];
$apiclient_cert_path = self::headerurl().Config::get('site.apiclient_cert');
$apiclient_key_path = self::headerurl().Config::get('site.apiclient_key');
$apiclient_cert_arr = openssl_x509_parse(file_get_contents($apiclient_cert_path,false, stream_context_create($stream_opts)));
//证书序列号
$serial_no = $apiclient_cert_arr['serialNumberHex'];
//密钥
$mch_private_key = file_get_contents($apiclient_key_path,false, stream_context_create($stream_opts));
$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",timestamp="%d",serial_no="%s",signature="%s"',
$mchid, $nonce, $timestamp, $serial_no, $sign);//微信返回token
return $token;
}
//加密示例
public static function getEncrypt($str) {
//$str是待加密字符串
$public_key_path = ROOT_PATH . '/public/public_cert/wx_public_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;
}
/**
* 解密证书
* @param $ciphertext
* @param $associatedData
* @param $nonceStr
* @return false|string
* @throws SodiumException
*/
public static function decryptToString($ciphertext, $associatedData, $nonceStr){
$apiV3Key = db('config')->where('name','apiV3Key')->value('value');
$str = base64_decode($ciphertext);
if (strlen($str) <= 16) {
return '';
}
// ext-sodium (default installed on >= PHP 7.2) 如果没有该函数需要安装sodium扩展
return sodium_crypto_aead_aes256gcm_decrypt($str, $associatedData, $nonceStr, $apiV3Key);
}
public static function headerurl(){
$http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
return $http_type . $_SERVER['HTTP_HOST'];
}
}
微信商家转账到零钱超过2000对敏感信息加密获取平台证书列表
于 2022-11-04 16:57:39 首次发布