ThinkPay最新微信支付公众号支付驱动分享
下面分享个ThinkPay的微信支付驱动,便于大家使用,如有疑问加QQ群讨论105108204<?php <br />
// +----------------------------------------------------------------------<br>
// | 零云 [ 简单 高效 卓越 ]<br>
// +----------------------------------------------------------------------<br>
// | Copyright (c) 2016 http://www.lingyun.net All rights reserved.<br>
// +----------------------------------------------------------------------<br>
// | Author: jry <br>
// +----------------------------------------------------------------------<br>
namespace Addons\Pay\ThinkPay\Pay\Driver;<br>
<br>
/**<br>
* 微信支付驱动<br>
*/<br>
class Wxpay extends \Addons\Pay\ThinkPay\Pay\Pay<br>
{<br>
protected $gateway = 'https://api.mch.weixin.qq.com/pay/unifiedorder';<br>
protected $orderquery = 'https://api.mch.weixin.qq.com/pay/orderquery';<br>
protected $config = array(<br>
'appid' => '',<br>
'appsecret' => '',<br>
'mchid' => '',<br>
'key' => '',<br>
);<br>
<br>
public function check()<br>
{<br>
if (!$this->config['appid'] || !$this->config['appsecret'] || !$this->config['mchid'] || !$this->config['key']) {<br>
E("微信支付设置有误!");<br>
}<br>
return true;<br>
}<br>
<br>
public function buildRequestForm($pay_data)<br>
{<br>
// APP支付<br>
if (C('IS_API')) {<br>
$param = array(<br>
'appid' => $this->config['appid'],<br>
'mch_id' => $this->config['mchid'],<br>
'nonce_str' => $this->getNonceStr(),<br>
'body' => $pay_data['body'],<br>
'out_trade_no' => $pay_data['out_trade_no'],<br>
'total_fee' => $pay_data['money'] * 100,<br>
'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],<br>
'notify_url' => $this->config['notify_url'],<br>
'trade_type' => 'APP',<br>
);<br>
<br>
// 签名<br>
$param['sign'] = $this->MakeSign($param);<br>
$xml_param = $this->ToXml($param);<br>
$result = $this->FromXml($this->postXmlCurl($xml_param, $this->gateway));<br>
if ($result['return_code'] === 'SUCCESS') {<br>
if ($this->CheckSign($result)) {<br>
// 统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。<br>
// 参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay<br>
$prepayparams = array();<br>
$prepayparams['appid'] = $result['appid'];<br>
$prepayparams['partnerid'] = $result['mch_id'];<br>
$prepayparams['prepayid'] = $result['prepay_id'];<br>
$prepayparams['noncestr'] = $result['nonce_str'];<br>
$prepayparams['package'] = 'Sign=WXPay';<br>
$prepayparams['timestamp'] = time();<br>
$prepayparams['sign'] = $this->MakeSign($prepayparams); // 签名<br>
$return['json'] = json_encode($prepayparams);<br>
return $return;<br>
}<br>
}<br>
} else {<br>
// 获取用户openId,微信公众号JSAPI支付必须<br>
$openId = $this->GetOpenid();<br>
$param = array(<br>
'appid' => $this->config['appid'],<br>
'mch_id' => $this->config['mchid'],<br>
'nonce_str' => $this->getNonceStr(),<br>
'body' => $pay_data['body'],<br>
'out_trade_no' => $pay_data['out_trade_no'],<br>
'total_fee' => $pay_data['money'] * 100,<br>
'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],<br>
'notify_url' => $this->config['notify_url'],<br>
'trade_type' => 'JSAPI',<br>
'openid' => $openId,<br>
);<br>
<br>
// 签名<br>
$param['sign'] = $this->MakeSign($param);<br>
$xml_param = $this->ToXml($param);<br>
$result = $this->FromXml($this->postXmlCurl($xml_param, $this->gateway));<br>
if ($result['return_code'] === 'SUCCESS') {<br>
if ($this->CheckSign($result)) {<br>
// 获取JSAPI所需参数<br>
$jsApiParameters = $this->GetJsApiParameters($result);<br>
$pay_page =
<br>
<br>
<meta>
<br>
<meta>
<br>
<title>微信支付</title>
<br>
<script><br />
//调用微信JS api 支付<br />
function jsApiCall()<br />
{<br />
WeixinJSBridge.invoke(<br />
'getBrandWCPayRequest',<br />
{$jsApiParameters},<br />
function(res){<br />
WeixinJSBridge.log(res.err_msg);<br />
//alert(res.err_code+res.err_desc+res.err_msg);<br />
}<br />
);<br />
}<br />
<br />
function callpay()<br />
{<br />
if (typeof WeixinJSBridge == "undefined"){<br />
if( document.addEventListener ){<br />
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);<br />
}else if (document.attachEvent){<br />
document.attachEvent('WeixinJSBridgeReady', jsApiCall);<br />
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);<br />
}<br />
}else{<br />
jsApiCall();<br />
}<br />
}<br />
</script><br>
<br>
<br>
<br><br>
<font><b>该笔订单支付金额为<span>{$pay_data['money']}</span>元</b></font><br><br><br>
<div>
<br>
<button>立即支付</button><br>
</div>
<br>
<br>
<br>
EOF;<br>
return $pay_page;<br>
}<br>
} else {<br>
E("微信订单错误!" . $result['return_msg']);<br>
}<br>
}<br>
}<br>
<br>
/**<br>
* 异步通知验证<br>
*/<br>
public function verifyNotify($notify)<br>
{<br>
//获取通知的数据<br>
if ($notify['return_code'] === 'SUCCESS') {<br>
if (!array_key_exists("transaction_id", $notify)) {<br>
E("输入参数不正确!");<br>
}<br>
$param['transaction_id'] = $notify["transaction_id"];<br>
$param['appid'] = $this->config['appid'];<br>
$param['mch_id'] = $this->config['mchid'];<br>
$param['nonce_str'] = $this->getNonceStr();<br>
$param['sign'] = $this->MakeSign($param);<br>
$xml_param = $this->ToXml($param);<br>
$result = $this->FromXml($this->postXmlCurl($xml_param, $this->orderquery));<br>
if ($this->CheckSign($result)) {<br>
$result['status'] = ($result['result_code'] == 'SUCCESS') ? true : false;<br>
$result['money'] = $result['total_fee'] / 100;<br>
$this->info = $result;<br>
return true;<br>
}<br>
} else {<br>
E('通知错误');<br>
}<br>
}<br>
<br>
/**<br>
*<br>
* 获取jsapi支付的参数<br>
* @param array $UnifiedOrderResult 统一支付接口返回的数据<br>
* @throws WxPayException<br>
*<br>
* @return json数据,可直接填入js函数作为参数<br>
*/<br>
public function GetJsApiParameters($UnifiedOrderResult)<br>
{<br>
if (!array_key_exists("appid", $UnifiedOrderResult)<br>
|| !array_key_exists("prepay_id", $UnifiedOrderResult)<br>
|| $UnifiedOrderResult['prepay_id'] == "") {<br>
E("参数错误");<br>
}<br>
$jsapi['appId'] = $UnifiedOrderResult["appid"];<br>
$jsapi['timeStamp'] = (string) time();<br>
$jsapi['nonceStr'] = $this->getNonceStr();<br>
$jsapi['appId'] = $UnifiedOrderResult["appid"];<br>
$jsapi['package'] = "prepay_id=" . $UnifiedOrderResult['prepay_id'];<br>
$jsapi['signType'] = 'MD5';<br>
$jsapi['paySign'] = $this->MakeSign($jsapi);<br>
$parameters = json_encode($jsapi);<br>
return $parameters;<br>
}<br>
<br>
/**<br>
* 以post方式提交xml到对应的接口url<br>
*<br>
* @param string $xml 需要post的xml数据<br>
* @param string $url url<br>
* @param bool $useCert 是否需要证书,默认不需要<br>
* @param int $second url执行超时时间,默认30s<br>
* @throws WxPayException<br>
*/<br>
private static function postXmlCurl($xml, $url, $useCert = false, $second = 30)<br>
{<br>
$ch = curl_init();<br>
//设置超时<br>
curl_setopt($ch, CURLOPT_TIMEOUT, $second);<br>
<br>
//如果有配置代理这里就设置代理<br>
// if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"<br>
// && WxPayConfig::CURL_PROXY_PORT != 0){<br>
// curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);<br>
// curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);<br>
// }<br>
curl_setopt($ch, CURLOPT_URL, $url);<br>
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);<br>
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //严格校验<br>
//设置header<br>
curl_setopt($ch, CURLOPT_HEADER, false);<br>
//要求结果为字符串且输出到屏幕上<br>
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);<br>
<br>
if ($useCert == true) {<br>
//设置证书<br>
//使用证书:cert 与 key 分别属于两个.pem文件<br>
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');<br>
curl_setopt($ch, CURLOPT_SSLCERT, WxPayConfig::SSLCERT_PATH);<br>
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');<br>
curl_setopt($ch, CURLOPT_SSLKEY, WxPayConfig::SSLKEY_PATH);<br>
}<br>
//post提交方式<br>
curl_setopt($ch, CURLOPT_POST, true);<br>
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);<br>
//运行curl<br>
$data = curl_exec($ch);<br>
//返回结果<br>
if ($data) {<br>
curl_close($ch);<br>
return $data;<br>
} else {<br>
$error = curl_errno($ch);<br>
curl_close($ch);<br>
E("curl出错,错误码:$error");<br>
}<br>
}<br>
<br>
/*<br>
* 产生随机字符串,不长于32位<br>
* @param int $length<br>
* @return 产生的随机字符串<br>
*/<br>
public static function getNonceStr($length = 32)<br>
{<br>
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";<br>
$str = "";<br>
for ($i = 0; $i
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);<br>
}<br>
return $str;<br>
}<br>
<br>
/**<br>
* 生成签名<br>
* @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值<br>
*/<br>
public function MakeSign($param)<br>
{<br>
//签名步骤一:按字典序排序参数<br>
ksort($param);<br>
$string = $this->ToUrlParams($param);<br>
//签名步骤二:在string后加入KEY<br>
$string = $string . "&key=" . $this->config['key'];<br>
//签名步骤三:MD5加密<br>
$string = md5($string);<br>
//签名步骤四:所有字符转为大写<br>
$result = strtoupper($string);<br>
return $result;<br>
}<br>
<br>
/**<br>
*<br>
* 检测签名<br>
*/<br>
public function CheckSign($param)<br>
{<br>
$sign = $this->MakeSign($param);<br>
if ($param['sign'] == $sign) {<br>
return true;<br>
} else {<br>
E("签名错误!");<br>
}<br>
}<br>
<br>
/**<br>
*<br>
* 拼接签名字符串<br>
* @param array $urlObj<br>
*<br>
* @return 返回已经拼接好的字符串<br>
*/<br>
public function ToUrlParams($param)<br>
{<br>
$buff = "";<br>
foreach ($param as $k => $v) {<br>
if ($k != "sign" && $v != "" && !is_array($v)) {<br>
$buff .= $k . "=" . $v . "&";<br>
}<br>
}<br>
<br>
$buff = trim($buff, "&");<br>
return $buff;<br>
}<br>
<br>
/**<br>
* 输出xml字符<br>
* @throws WxPayException<br>
**/<br>
public function ToXml($param)<br>
{<br>
if (!is_array($param)<br>
|| count($param)
E("数组数据异常!");<br>
}<br>
<br>
$xml = "<xml>";<br>
foreach ($param as $key => $val) {<br>
if (is_numeric($val)) {<br>
$xml .= "" . $val . "" . $key . ">";<br>
} else {<br>
$xml .= "" . $key . ">";<br>
}<br>
}<br>
$xml .= "</xml>";<br>
return $xml;<br>
}<br>
<br>
/**<br>
* 将xml转为array<br>
* @param string $xml<br>
* @throws WxPayException<br>
*/<br>
public function FromXml($xml)<br>
{<br>
if (!$xml) {<br>
E("xml数据异常!");<br>
}<br>
//将XML转为array<br>
//禁止引用外部xml实体<br>
libxml_disable_entity_loader(true);<br>
$this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);<br>
return $this->values;<br>
}<br>
<br>
/**<br>
*<br>
* 通过跳转获取用户的openid,跳转流程如下:<br>
* 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize<br>
* 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code<br>
*<br>
* @return 用户的openid<br>
*/<br>
public function GetOpenid()<br>
{<br>
//通过code获得openid<br>
if (!isset($_GET['code'])) {<br>
//触发微信返回code码<br>
$baseUrl = urlencode('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);<br>
$url = $this->__CreateOauthUrlForCode($baseUrl);<br>
Header("Location: $url");<br>
exit();<br>
} else {<br>
//获取code码,以获取openid<br>
$code = $_GET['code'];<br>
$openid = $this->getOpenidFromMp($code);<br>
return $openid;<br>
}<br>
}<br>
<br>
/**<br>
*<br>
* 通过code从工作平台获取openid机器access_token<br>
* @param string $code 微信跳转回来带上的code<br>
*<br>
* @return openid<br>
*/<br>
public function GetOpenidFromMp($code)<br>
{<br>
$url = $this->__CreateOauthUrlForOpenid($code);<br>
//初始化curl<br>
$ch = curl_init();<br>
//设置超时<br>
curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);<br>
curl_setopt($ch, CURLOPT_URL, $url);<br>
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);<br>
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);<br>
curl_setopt($ch, CURLOPT_HEADER, false);<br>
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);<br>
// if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"<br>
// && WxPayConfig::CURL_PROXY_PORT != 0){<br>
// curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);<br>
// curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);<br>
// }<br>
//运行curl,结果以jason形式返回<br>
$res = curl_exec($ch);<br>
curl_close($ch);<br>
//取出openid<br>
$data = json_decode($res, true);<br>
$this->data = $data;<br>
$openid = $data['openid'];<br>
return $openid;<br>
}<br>
<br>
/**<br>
*<br>
* 构造获取code的url连接<br>
* @param string $redirectUrl 微信服务器回跳的url,需要url编码<br>
*<br>
* @return 返回构造好的url<br>
*/<br>
private function __CreateOauthUrlForCode($redirectUrl)<br>
{<br>
$urlObj["appid"] = $this->config['appid'];<br>
$urlObj["redirect_uri"] = "$redirectUrl";<br>
$urlObj["response_type"] = "code";<br>
$urlObj["scope"] = "snsapi_base";<br>
$urlObj["state"] = "#wechat_redirect";<br>
$bizString = $this->ToUrlParams($urlObj);<br>
return "https://open.weixin.qq.com/connect/oauth2/authorize?" . $bizString;<br>
}<br>
<br>
/**<br>
*<br>
* 构造获取open和access_toke的url地址<br>
* @param string $code,微信跳转带回的code<br>
*<br>
* @return 请求的url<br>
*/<br>
private function __CreateOauthUrlForOpenid($code)<br>
{<br>
$urlObj["appid"] = $this->config['appid'];<br>
$urlObj["secret"] = $this->config['appsecret'];<br>
$urlObj["code"] = $code;<br>
$urlObj["grant_type"] = "authorization_code";<br>
$bizString = $this->ToUrlParams($urlObj);<br>
return "https://api.weixin.qq.com/sns/oauth2/access_token?" . $bizString;<br>
}<br>
}