File: /www/wwwroot/bs.kntsleep.com/system/basic/class/wechatpay.class.php
<?php
/*
* Copyright (c) Huyin Information Technology Co., Ltd. All Rights Reserved.
* BOSSCMS Content Management System (https://www.bosscms.net/)
*/
defined('IS_OK') or exit('Access Forbidden');
if(is_file($file = ROOT_PATH.'system/extend/wechatpay/autoload.php')){
require_once $file;
}
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use WechatPay\GuzzleMiddleware\Validator;
use WechatPay\GuzzleMiddleware\Util\PemUtil;
use WechatPay\GuzzleMiddleware\Util\AesUtil;
use WechatPay\GuzzleMiddleware\Auth\CertificateVerifier;
use WechatPay\GuzzleMiddleware\Auth\WechatPay2Validator;
class wechatpay
{
public static $config = array();
public static function init()
{
global $G;
self::$config = array(
'appid' => $G['config']['pay4_appid'],
'appsecret' => $G['config']['pay4_appsecret'],
'mchid' => $G['config']['pay4_mchid'],
'serialno' => $G['config']['pay4_serialno'],
'apiv3_key' => $G['config']['pay4_apiv3_key'],
'apiclient_key' => $G['config']['pay4_apiclient_key'],
'certificate' => $G['config']['pay4_certificate']
);
}
public static function client($cert=true)
{
global $G;
if($G['config']['pay4_open'] && is_file(ROOT_PATH.'system/extend/wechatpay/autoload.php')){
if($cert && !self::$config['certificate']){
if($client = self::client(false)){
$resp = $client->request('GET', 'https://api.mch.weixin.qq.com/v3/certificates', array('headers'=>array('Accept'=>'application/json')));
if($resp->getStatusCode() == 200){
$list = json_decode($resp->getBody(), true);
$plainCerts = [];
$x509Certs = [];
$decrypter = new AesUtil(self::$config['apiv3_key']);
foreach($list['data'] as $item){
$encCert = $item['encrypt_certificate'];
if(!$plain = $decrypter->decryptToString($encCert['associated_data'], $encCert['nonce'], $encCert['ciphertext'])){
return;
}
if(!$cert = \openssl_x509_read($plain)){
return;
}
$plainCerts[] = $plain;
$x509Certs[] = $cert;
}
$validator = new WechatPay2Validator(new CertificateVerifier($x509Certs));
if($validator->validate($resp)){
foreach($list['data'] as $index=>$item){
$G['config']['pay4_certificate'] = self::$config['certificate'] = $plainCerts[$index];
mysql::select_set(array('name'=>'pay4_certificate','value'=>$plainCerts[$index],'parent'=>'0','type'=>'0'),'config',array('value'));
}
}
}
}
}
$wechatpayMiddleware = WechatPayMiddleware::builder();
$wechatpayMiddleware = $wechatpayMiddleware->withMerchant(self::$config['mchid'], self::$config['serialno'], PemUtil::loadPrivateKeyFromString(self::$config['apiclient_key']));
if($cert){
$wechatpayMiddleware = $wechatpayMiddleware->withWechatPay([ PemUtil::loadCertificateFromString(self::$config['certificate']) ]);
}else{
$wechatpayMiddleware = $wechatpayMiddleware->withValidator(new NoopValidator);
}
$wechatpayMiddleware = $wechatpayMiddleware->build();
$stack = GuzzleHttp\HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
return new GuzzleHttp\Client(['handler' => $stack]);
}
}
public static function pay($data, $info=null, $output=true)
{
global $G;
if(!isset($info)){
if(isMobile()){
if(stripos($_SERVER['HTTP_USER_AGENT'],'MicroMessenger') === false){
$info = 'wap';
}else{
$info = 'weixin';
}
}else{
$info = 'pc';
}
}
if($info == 'pc'){
if($data['pc_url']){ //电脑端跳转链接
if($output){
location($data['pc_url']);
}else{
return array('info'=>'pc','pc_url'=>$data['pc_url']);
}
}
}else if($info == 'weixin'){
if(!$data['openid'] && !$data['code'] && !session::get('wechatpay_openid') && $data['wxauth_url']){
$url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='.self::$config['appid'].'&redirect_uri='.urlencode($data['wxauth_url']).'&response_type=code&scope=snsapi_base#wechat_redirect';
if($output){
location($url);
}else{
return array('info'=>'weixin','url'=>$url);
}
}
}
if($client = self::client()){
$json = array(
'appid' => self::$config['appid'],
'mchid' => self::$config['mchid'],
'description' => $data['name'], //商品描述
'out_trade_no' => $data['num'], //商户订单号
'attach' => setDefault($data['attach'],''), //传递信息
'notify_url' => $data['notify_url'],
'amount' => array(
'total' => floor($data['price']*100), //付款金额(单位:分)
'currency' => 'CNY'
),
'scene_info' => array(
'payer_client_ip' => getIP()
)
);
if($info == 'pc'){
$json['time_expire'] = $data['etime'];
$resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/pay/transactions/native', array('json'=>$json,'headers'=>array('Accept'=>'application/json')));
$url = arrExist(json_decode($resp->getBody(),true),'code_url');
if($output){
location($url);
}else{
return array('info'=>'pc','url'=>$url);
}
}else if($info == 'wap'){
$json['scene_info']['h5_info'] = array('type'=>'Wap');
$resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/pay/transactions/h5', array('json'=>$json,'headers'=>array('Accept'=>'application/json')));
$url = arrExist(json_decode($resp->getBody(),true),'h5_url');
if($output){
location($url);
}else{
return array('info'=>'wap','url'=>$url);
}
}else if($info == 'weixin'){
$json['time_expire'] = $data['etime'];
$json['payer']['openid'] = $data['openid']?$data['openid']:(setDefault(session::get('wechatpay_openid'),self::sns($data['code'])));
$resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi', array('json'=>$json,'headers'=>array('Accept'=>'application/json')));
$pid = arrExist(json_decode($resp->getBody(),true),'prepay_id');
$html = '<script>'.self::bridge($pid,$data['return_url']).'</script>';
if($output){
echo $html;
}else{
return array('info'=>'weixin','pid'=>$pid,'html'=>$html);
}
}
}
}
public static function receive()
{
global $G;
if($G['config']['pay4_open'] && is_file(ROOT_PATH.'system/extend/wechatpay/autoload.php')){
if($post = json::decode(file_get_contents('php://input'))){
if($post['event_type'] == 'TRANSACTION.SUCCESS'){
$decrypter = new AesUtil(self::$config['apiv3_key']);
$encCert = $post['resource'];
if($plain = json::decode($decrypter->decryptToString($encCert['associated_data'],$encCert['nonce'], $encCert['ciphertext']))){
return array(
'notify' => true,
'price' => $plain['amount']['total']/100,
'num' => $plain['out_trade_no'],
'no' => $plain['transaction_id'],
'plain' => $plain
);
}
}
}
}
}
public static function sns($code, $key='openid')
{
into::basic_class('curl');
$res = json::decode(curl::request("https://api.weixin.qq.com/sns/oauth2/access_token?".
"appid=".self::$config['appid']."&secret=".self::$config['appsecret']."&code={$code}&grant_type=authorization_code"));
if($res['openid']){
session::set('wechatpay_openid',$res['openid']);
}
return isset($key)?$res[$key]:$res;
}
public static function bridge($pid, $url)
{
$nonceStr = strRand(32);
openssl_sign(self::$config['appid']."\n".TIME."\n".$nonceStr."\n".'prepay_id='.$pid."\n", $paySign, self::$config['apiclient_key'], OPENSSL_ALGO_SHA256);
return "function onBridgeReady(){
WeixinJSBridge.invoke('getBrandWCPayRequest',{
'appId': '".self::$config['appid']."',
'timeStamp': '".TIME."',
'nonceStr': '{$nonceStr}',
'package': 'prepay_id={$pid}',
'signType': 'RSA',
'paySign': '".base64_encode($paySign)."'
},
function(res){
if(res.err_msg=='get_brand_wcpay_request:ok') {
window.location.href='{$url}';
}
});
}
if(typeof WeixinJSBridge == 'undefined') {
if(document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if(document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}";
}
}
class NoopValidator implements Validator
{
public function validate(\Psr\Http\Message\ResponseInterface $response)
{
return true;
}
}
?>