<?php
namespace app\common\lib\built;
use app\platform\model\PaymentConfig;
class WxPayment
{
protected $authorization = 'WECHATPAY2-SHA256-RSA2048';
protected $method = "POST";
protected $url;
protected $platform_id;
protected $mch_id;
protected $nonce_str;
protected $sign;
protected $timestamp;
protected $serial_no;
protected $apiclient_key;
protected $apiclient_cert;
protected $secret_key;
protected $token;
protected $data;
protected $response;
protected $image_type;
protected $boundary;
protected $suffix;
protected $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;
}
public function upload()
{
$this->url = 'https://api.mch.weixin.qq.com/v3/merchant/media/upload';
$result = $this->uploadRequestAction();
return $result;
}
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";
$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;
}
public function uploadHeaderParam()
{
$this->getUploadSign();
$this->getToken();
$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;
}
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();
$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();
$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;
}
protected function getHeaderParam()
{
$this->getSign();
$this->getToken();
$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);
}
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);
}
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();
$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;
}
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;
}
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);
}
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);
}
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');
}
}