<?php
namespace app\common\lib\built;
use app\platform\model\PaymentConfig;
/**
* 微信支付
* Class WxPayment
* @package app\common\lib\built
*/
class WxPayment
{
protected $authorization = 'WECHATPAY2-SHA256-RSA2048'; //认证类型
protected $method = "POST"; //请求方式
protected $url; //请求链接
protected $platform_id; //平台id
protected $mch_id; //商户号
protected $nonce_str; //随机字符串
protected $sign; //签名
protected $timestamp; //时间
protected $serial_no; //商户Api证书序列号
protected $apiclient_key; //私钥地址
protected $apiclient_cert; //公钥地址
protected $secret_key; //V3密钥
protected $token; //Token
protected $data; //发送参数
protected $response; //返回信息
protected $image_type; //图片类型
protected $boundary; //边界符
protected $suffix; //图片后缀
protected $openid; //发放的openid
protected $file; //图片信息
protected $platform_certificate; //平台证书地址
protected $platform_serial_no; //平台证书序列号
public function __construct($param)
{
if (!empty($param['platform_id'])) {
$PaymentConfigModel = new PaymentConfig();
$config = $PaymentConfigModel->getFind([
'platform_id' => $param['platform_id']
]);
if (empty($config)) {
echo json_encode(callBack('error', 'param_error', '未配置支付参数'), JSON_UNESCAPED_UNICODE);
die();
}
$this->mch_id = $config['wx_mchid'];
$this->serial_no = $config['wx_serial_no'];
$this->apiclient_key = $config['wx_apiclient_key'];
$this->secret_key = $config['wx_secret_key'];
$this->platform_certificate = $config['wx_platform_certificate'];
$this->platform_serial_no = $config['wx_platform_serial_no'];
$this->apiclient_cert = $config['wx_apiclient_cert'];
} else {
$this->mch_id = $param['mch_id'] ?? '';
$this->serial_no = $param['serial_no'] ?? '';
$this->apiclient_key = $param['apiclient_key'] ?? '';
$this->secret_key = $param['secret_key'] ?? '';
$this->platform_certificate = $param['platform_certificate'] ?? '';
$this->platform_serial_no = $param['platform_serial_no'] ?? '';
$this->apiclient_cert = $param['apiclient_cert'] ?? '';
}
$this->platform_id = $param['platform_id'] ?? '';
$this->method = $param['method'] ?? $this->method;
$this->data = $param['data'] ?? [];
$this->image_type = $param['image_type'] ?? '';
$this->boundary = $param['boundary'] ?? '';
$this->openid = $param['openid'] ?? '';
$this->file = $param['file'] ?? '';
}
//微信小程序发起退款请求
public function appletLaunchCombineRefund()
{
$this->url = 'https://api.mch.weixin.qq.com/v3/ecommerce/refunds/apply';
$result = $this->requestAction();
return $result;
}
/**
* 上传图片
* @return bool|string
*/
public function upload()
{
$this->url = 'https://api.mch.weixin.qq.com/v3/merchant/media/upload';
$result = $this->uploadRequestAction();
return $result;
}
/**
* 图片上传请求
* @return bool|string
*/
public function uploadRequestAction()
{
if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
}
$headerParam = $this->uploadHeaderParam(); //获取头部信息
$boundarystr = "--{$this->boundary}\r\n";// $out是post的内容
$str = $boundarystr;
$str .= 'Content-Disposition: form-data; name="meta"' . "\r\n";
$str .= 'Content-Type: application/json' . "\r\n";
$str .= "\r\n";
$str .= json_encode($this->data['meta']) . "\r\n";
$str .= $boundarystr;
$str .= 'Content-Disposition: form-data; name="file"; filename="' . $this->data['meta']['filename'] . '"' . "\r\n";
$str .= 'Content-Type: ' . $this->image_type . ";\r\n";
$str .= "\r\n";
$str .= $this->data['file'] . "\r\n";
$str .= $boundarystr . "--\r\n";
$ThirdClass = new ThirdRequest();
$this->response = $ThirdClass->curlPost($this->url, $str, $headerParam);
return $this->response;
}
/**
* 图片上传头部参数
* @return array
*/
public function uploadHeaderParam()
{
$this->getUploadSign(); //生成签名
$this->getToken(); //生成Token
$header = [
"Content-Type: multipart/form-data;name='meta'",
"Content-Type: application/json",
"User-Agent:" . $_SERVER['HTTP_USER_AGENT'],
'Authorization: ' . $this->authorization . ' ' . $this->token,
"Content-Type: multipart/form-data;boundary=" . $this->boundary
];
return $header;
}
/**
* 图片生成签名
*/
protected function getUploadSign()
{
$url_parts = parse_url($this->url); //链接
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$this->timestamp = time();
$this->nonce_str = randomStr(32); //随机字符串
$message = $this->method . "\n" .
$canonical_url . "\n" .
$this->timestamp . "\n" .
$this->nonce_str . "\n" .
json_encode($this->data['meta']) . "\n";
openssl_sign($message, $raw_sign, openssl_get_privatekey(file_get_contents($this->apiclient_key)), 'sha256WithRSAEncryption');
$this->sign = base64_encode($raw_sign);
}
//微信小程序发起合单支付请求
public function appletLaunchCombinePayment()
{
$this->url = 'https://api.mch.weixin.qq.com/v3/combine-transactions/jsapi';
$result = $this->requestAction();
return $result;
}
/**
* 进件状态
* @param $out_request_no 业务申请编号
* @return bool|string
*/
public function applymentsStatus($out_request_no)
{
$this->url = 'https://api.mch.weixin.qq.com/v3/ecommerce/applyments/out-request-no/' . $out_request_no;
$this->data = [];
$this->getSign(); //生成签名
$this->getToken(); //生成Token
$headerParam = [
"Content-type: application/json;charset='utf-8'",
"Accept:application/json",
"User-Agent:" . $this->mch_id,
'Authorization: ' . $this->authorization . ' ' . $this->token,
'Wechatpay-Serial:' . $this->platform_serial_no
];
$ThirdClass = new ThirdRequest();
$this->response = $ThirdClass->curlGet($this->url, $headerParam);
return $this->response;
}
//供应商进件
public function applyments()
{
$this->url = 'https://api.mch.weixin.qq.com/v3/ecommerce/applyments/';
$this->getSign(); //生成签名
$this->getToken(); //生成Token
$headerParam = [
"Content-type: application/json;charset='utf-8'",
"Accept:application/json",
"User-Agent:*/*",
'Authorization: ' . $this->authorization . ' ' . $this->token,
'Wechatpay-Serial:' . $this->platform_serial_no
];
$ThirdClass = new ThirdRequest();
$this->response = $ThirdClass->curlPost($this->url, json_encode($this->data), $headerParam);
return $this->response;
}
//对私银行列表
public function personalBanking()
{
$this->url = 'https://api.mch.weixin.qq.com/v3/capital/capitallhh/banks/personal-banking?offset=' . $this->data['offset'] . '&limit=' . $this->data['limit'];
$this->data = [];
$headerParam = $this->getHeaderParam(); //获取头部信息
$ThirdClass = new ThirdRequest();
$this->response = $ThirdClass->curlGet($this->url, $headerParam);
return $this->response;
}
//对公银行列表
public function corporateBanking()
{
$this->url = 'https://api.mch.weixin.qq.com/v3/capital/capitallhh/banks/corporate-banking?offset=' . $this->data['offset'] . '&limit=' . $this->data['limit'];
$this->data = [];
$headerParam = $this->getHeaderParam(); //获取头部信息
$ThirdClass = new ThirdRequest();
$this->response = $ThirdClass->curlGet($this->url, $headerParam);
return $this->response;
}
/**
* 发送post请求
* @return bool|string
*/
protected function requestAction()
{
$headerParam = $this->getHeaderParam(); //获取头部信息
$ThirdClass = new ThirdRequest();
$this->response = $ThirdClass->curlPost($this->url, json_encode($this->data), $headerParam);
return $this->response;
}
/**
* 获取请求头部参数
* @return array
*/
protected function getHeaderParam()
{
$this->getSign(); //生成签名
$this->getToken(); //生成Token
$header = [
"Content-type: application/json;charset='utf-8'",
"Accept:application/json",
"User-Agent:*/*",
'Authorization: ' . $this->authorization . ' ' . $this->token,
];
return $header;
}
/**
* 生成签名
*/
protected function getSign()
{
$url_parts = parse_url($this->url); //链接
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$this->timestamp = time();
$this->nonce_str = randomStr(32); //随机字符串
if (!empty($this->data)) {
$message = $this->method . "\n" .
$canonical_url . "\n" .
$this->timestamp . "\n" .
$this->nonce_str . "\n" .
json_encode($this->data) . "\n";
} else {
$message = $this->method . "\n" .
$canonical_url . "\n" .
$this->timestamp . "\n" .
$this->nonce_str . "\n" .
'' . "\n";
}
openssl_sign($message, $raw_sign, openssl_get_privatekey(file_get_contents($this->apiclient_key)), 'sha256WithRSAEncryption');
$this->sign = base64_encode($raw_sign);
}
/**
* 生成Token
* @return string
*/
protected function getToken()
{
$this->token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
$this->mch_id, $this->nonce_str, $this->timestamp, $this->serial_no, $this->sign);
}
/**
* 回调验签
* @param $platform_id 平台id
* @param $timeStamp 时间戳
* @param $nonce 随机字符串
* @param $body 内同
* @param $wx_sign 签名
* @param $serial_no 回调返回的平台证书序列号
* @return bool
*/
public function checkSign($timeStamp, $nonce, $body, $wx_sign, $serial_no)
{
if (!$serial_no) {
return false;
}
//证书序列号不一致获取新的证书
if ($serial_no != $this->platform_serial_no) {
$this->getCertificates();
}
$message = $timeStamp . "\n" .
$nonce . "\n" .
$body . "\n";
$wx_sign = base64_decode($wx_sign);
$platform_certificate = file_get_contents($this->platform_certificate);//平台证书
$res = openssl_verify($message, $wx_sign, $platform_certificate, OPENSSL_ALGO_SHA256);
if ($res == 1) {
return true;
}
return false;
}
//获取平台证书
public function getCertificates()
{
$this->method = 'GET';
$this->url = 'https://api.mch.weixin.qq.com/v3/certificates';
$this->getSign(); //生成签名
$this->getToken(); //生成Token
$headerParam = [
"Content-type: application/json;charset='utf-8'",
"Accept:application/json",
"User-Agent:" . $this->mch_id,
'Authorization: ' . $this->authorization . ' ' . $this->token,
];
$ThirdClass = new ThirdRequest();
$result = $ThirdClass->curlGet($this->url, $headerParam);
$list = json_decode($result, true)['data'];
//查询本地证书序列号
$str = md5(randomStr(32));
$file_path = '../certificate/payments/platform_certificate/' . $this->platform_id;
$FileHandleClass = new FileHandle();
$FileHandleClass->createDir($file_path, 2);
$file_path = $file_path . '/' . $str . '.pem';
$PaymentConfigModel = new PaymentConfig();
foreach ($list as $val) {
if ($this->platform_serial_no != $val['serial_no']) {
$serial_no = $val['serial_no'];//获取的平台证书序列号
$encrypt_certificate = $val['encrypt_certificate'];
$result = $this->decryptResource($encrypt_certificate['ciphertext'], $encrypt_certificate['associated_data'], $encrypt_certificate['nonce']);
if (empty($result)) {
return callBack('error', 'param_error', '解密失败');
}
$PaymentConfigModel->updateInfo([
['id', '=', $this->platform_id]
], [
'wx_platform_serial_no' => $serial_no,
'wx_platform_certificate' => $file_path
]);
file_put_contents($file_path, $result);//获取的文件临时保存到服务器
}
}
return callBack('success', 'success', '获取平台证书成功');
}
//加密敏感字符串
public function getEncrypts($str = "张三")
{
$public_key = file_get_contents($this->platform_certificate);
$encrypted = '';
if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
$sign = base64_encode($encrypted);
} else {
return false;
}
return $sign;//返回加密的敏感字符串
}
//解密敏感字符串
public function decryptToString($str)
{
$encrypted = '';
$public_key = file_get_contents($this->apiclient_key);
openssl_private_decrypt(base64_decode($str), $encrypted, $public_key);
return $encrypted;//返回解密的字符串
}
/**
* 解密resource对象
* @param $ciphertext 数据密文
* @param $associated_data 附加数据
* @param $nonce 随机串
* @return false|mixed|string
*/
public function decryptResource($ciphertext, $associated_data, $nonce)
{
if (strlen($this->secret_key) != 32) {
return false;
}
$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, $associated_data, $nonce, $this->secret_key);
}
// 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, $associated_data, $nonce, $this->secret_key);
}
// 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',
$this->secret_key,
\OPENSSL_RAW_DATA,
$nonce,
$authTag,
$associated_data);
}
throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
}
}
微信收付通V3支付
最新推荐文章于 2025-04-11 13:45:00 发布