刚刚接触支付的时候会遇到很多坑.官方文档也不是很全面,在网上搜索出来很多版本也不知道哪个好用
下面是本人在tp5内使用的全套代码(其他框架写法稍作修改即可).附赠微信支付类
/****************************************调该方法发起支付*******************************************/
//jsapi微信支付
public function wxpay() {
Vendor("lib.Wechat");
$wechat = new \Wechat();
$openid = Cookie::get('openid'); //公众号支付和小程序支付必要参数
$body = '微信支付';
$attach = input('id');
$number=DB::name('order')->where('id',$attach)->value('order_number'); //唯一订单号
$price = 0.01; //价格
$notify_url = 'http://'.$_SERVER['HTTP_HOST'].'/index/Goods/xiadans'; //回调地址
// dump($notify_url);die;
$res = $wechat->createPayOrder($body, $attach, $number, $price, $notify_url, $openid); //发起支付
$data['info']='微信支付';
$data['status']='1';
$data['data']=$res;
return json($data);
}
/****************************************微信支付回调代码*******************************************/
public function xiadans(){
//获取接口数据,如果$_REQUEST拿不到数据,则使用file_get_contents函数获取
$post = $_REQUEST['/home/Goods/xiadans'];
if ($post == null) {
$post = file_get_contents("php://input");
}
if ($post == null) {
$post = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
}
if (empty($post) || $post == null || $post == '') {
//阻止微信接口反复回调接口 文档地址 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_7&index=7,下面这句非常重要!!!
$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
echo $str;
exit('Notify 非法回调');
}
/*****************微信回调返回数据样例*******************
$post = '<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx2421b1c4370ec43b]]></appid>
<mch_id><![CDATA[10000100]]></mch_id>
<nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
<sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
<trade_type><![CDATA[APP]]></trade_type>
</xml>';
*************************微信回调返回*****************/
libxml_disable_entity_loader(true); //禁止引用外部xml实体
$xml = simplexml_load_string($post, 'SimpleXMLElement', LIBXML_NOCDATA);//XML转数组
$post_data = (array)$xml;
/** 解析出来的数组
*Array
* (
* [appid] => wx1c870c0145984d30
* [bank_type] => CFT
* [cash_fee] => 100
* [fee_type] => CNY
* [is_subscribe] => N
* [mch_id] => 1297210301
* [nonce_str] => gkq1x5fxejqo5lz5eua50gg4c4la18vy
* [openid] => olSGW5BBvfep9UhlU40VFIQlcvZ0
* [out_trade_no] => fangchan_588796
* [result_code] => SUCCESS
* [return_code] => SUCCESS
* [sign] => F6890323B0A6A3765510D152D9420EAC
* [time_end] => 20180626170839
* [total_fee] => 100
* [trade_type] => JSAPI
* [transaction_id] => 4200000134201806265483331660
* )
**/
//订单号
$order_id = isset($post_data['attach']) && !empty($post_data['attach']) ? $post_data['attach'] : 0;
//查询订单信息
$order_info = DB::name('order')->where('id',$order_id)->find();
if(count($order_info) > 0){
/* //查询平台信息
$platform_info = DB::fetch_first("SELECT * FROM pingtaiInfo WHERE `open_pid`= {$order_info['part1']}");
//平台支付key
$wxpay_key = $platform_info['zhifu_key'];
//接收到的签名
$post_sign = $post_data['sign'];
unset($post_data['sign']);
//重新生成签名
$newSign = MakeSign($post_data,$wxpay_key);
//签名统一,则更新数据库
if($post_sign == $newSign){
$updateData = array();
$updateData['pay_time'] = TIMESTAMP; //支付时间
$updateData['order_status'] = 2; //订单状态
//更新order数据库
//Do what you want...*/
$update['status'] = 2;
Db::name('order')->where('id',$order_id)->update($update);
// }
}
//阻止微信接口反复回调接口 文档地址 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_7&index=7,下面这句非常重要!!!
$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
echo $str;
function MakeSign($params,$key){
//签名步骤一:按字典序排序数组参数
ksort($params);
$string = ToUrlParams($params); //参数进行拼接key=value&k=v
//签名步骤二:在string后加入KEY
$string = $string . "&key=".$key;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
function ToUrlParams( $params ){
$string = '';
if( !empty($params) ){
$array = array();
foreach( $params as $key => $value ){
$array[] = $key.'='.$value;
}
$string = implode("&",$array);
}
return $string;
}
}
/*************************************以下是微信支付三方类的全部代码****************************************/
<?php
/**
* 自定义微信工具类
*/
class Wechat {
/**
*以下内容均在商户后台获取
*/
public function __construct() {
$this->token = ''; //商户平台token(商户平台自己设置)
$this->appid = ''; //appid
$this->appsec = ''; //appsecrt
$this->mchid = ''; //商户id
$this->mchkey = ''; //商户秘钥
$this->accessToken = $this->getAccessToken();
}
/**
* 生成签名
*/
public function getSign($data){
ksort($data);
$data['key'] = $this->mchkey;
return md5(urldecode(http_build_query($data)));
}
/**
* 获取AccessToken
*/
public function getAccessToken(){
$cn = curl_init();
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appid&secret=$this->appsec";
//设置传输选项值
curl_setopt($cn,CURLOPT_URL,$url);
//设置传输选项值
curl_setopt($cn,CURLOPT_RETURNTRANSFER,1);
//执行 curl
$res = curl_exec($cn);
//关闭 curl
curl_close($cn);
// echo $res;
$data = json_decode($res,true);
$access_token = $data['access_token'];
return $access_token;
}
/**
* 获取微信js-sdk票据
*/
public function getJsApiTicket(){
$WechatTicket = M('WechatTicket') -> where(array('id'=>1)) -> find();
if(time()>$WechatTicket['expires_times']){
$url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$this->accessToken";
$res = $this->http_get($url);
$arr = json_decode($res,true);
$data = array('id'=>1,'ticket'=>$arr['ticket'],'expires_times'=>time() + $arr[expires_in] - 300);
M('wechat_ticket') -> save($data);
return M('wechat_ticket') -> where(array('id'=>1)) -> getField('ticket');
}else{
return $WechatTicket['ticket'];
}
}
/**
* 获取微信js-sdk签名
*/
public function getSignPackage($url) {
$timestamp = time();
$nonceStr = $this->createNonceStr();
$string = "jsapi_ticket=$this->JsApiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url";
$signature = sha1($string);
$signPackage = array(
"appId" => $this->appid,
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => $signature,
"rawString" => $string
);
return $signPackage;
}
/**
* 生成随机字符串
*/
private function createNonceStr($length = 16) {
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
/**
* 获取网页授权地址
*/
public function getAuthCode($callback,$state,$scope){
$url = urlencode($callback);
return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$this->appid&redirect_uri=$url&response_type=code&scope=$scope&state=$state#wechat_redirect";
}
/**
* 获取Auth协议AccessToken信息
* $res 已转换为数组类型
*/
public function getAuthAccessToken($code){
$url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$this->appid&secret=$this->appsec&code=$code&grant_type=authorization_code";
$res= json_decode($this->http_get($url),true);
return $res;
}
/**
* 试用Auth协议获取用户信息
* $res 已转换为数组类型
*/
public function getAuthUserinfo($accessToken,$openId){
$url = "https://api.weixin.qq.com/sns/userinfo?access_token=$accessToken&openid=$openId&lang=zh_CN";
$res= json_decode($this->http_get($url),true);
return $res;
}
/**
* 创建自定义菜单
* 创建成功微信服务器返回json:{"errcode":0,"errmsg":"ok"}
* 创建失败微信服务器返回json:{"errcode":40018,"errmsg":"invalid button name size"}
* $res 已转换为数组类型
*/
public function createWechatMenu($data){
$url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=$this->accessToken";
$res= json_decode($this->http_post($url, $data),true);
return $res;
}
/**
* 获取用户基本信息
*/
public function getSubscribe($openid){
$url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=$this->accessToken&openid=$openid&lang=zh_CN";
$res = json_decode($this->http_get($url),true);
return $res;
}
/**
* 企业付款
* $number
* $openid
* $truename
* $amount
* $desc
* $ip
*/
public function createWechatPay($number,$openid,$truename,$amount,$desc,$ip){
$url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
$data['mch_appid'] = $this->appid;
$data['mchid'] = $this->mchid;
$data['nonce_str'] = $this->createNonceStr();
$data['partner_trade_no'] = $number;//订单号
$data['openid'] = $openid;
$data['check_name'] = 'FORCE_CHECK';
$data['re_user_name'] = $truename;
$data['amount'] = $amount * 100;
$data['desc'] = $desc;
$data['spbill_create_ip'] = $ip;
$data['sign'] = $this->getSign($data);
$ch = curl_init ();
$MENU_URL="https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
curl_setopt ( $ch, CURLOPT_URL, $MENU_URL );
curl_setopt ( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, FALSE );
$zs1="D:\www\sotjb\cert\apiclient_cert.pem";
$zs2="D:\www\sotjb\cert\apiclient_key.pem";
curl_setopt($ch,CURLOPT_SSLCERT,$zs1);
curl_setopt($ch,CURLOPT_SSLKEY,$zs2);
curl_setopt ( $ch, CURLOPT_FOLLOWLOCATION, 1 );
curl_setopt ( $ch, CURLOPT_AUTOREFERER, 1 );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $this->arrayToXml($data) );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
$info = curl_exec ( $ch );
if (curl_errno ( $ch )) {
echo 'Errno' . curl_error ( $ch );
}
curl_close( $ch );
$res = $this->xmlToArray($info);
return $res['result_code'];
}
/**
* 微信支付-下单请求
* $body
* $number
* $price
* $notify_url
* $openid
*/
public function createPayOrder($body,$attach,$number,$price,$notify_url,$openid){
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$data['appid'] = $this->appid;
$data['mch_id'] = $this->mchid;
$data['nonce_str'] = $this->createNonceStr();
$data['body'] = $body;
$data['attach'] = $attach;
$data['out_trade_no'] = $number;
$data['total_fee'] = $price*100;
$data['spbill_create_ip'] = $this->get_client_ip();
$data['notify_url'] = $notify_url;
$data['trade_type'] = 'JSAPI';
$data['openid'] = $openid;
$data['sign'] = $this->getSign($data);
$res = $this->xmlToArray($this->http_post($url, $this->arrayToXml($data)));
// dump($res);die();
if($res['return_code']=='SUCCESS'){
if ($res['result_code'] == 'SUCCESS') {
unset($data);
$data['appId'] = $this->appid;
$data['timeStamp'] = (string)NOW_TIME;
$data['nonceStr'] = $this->createNonceStr();
$data['package'] = 'prepay_id='.$res['prepay_id'];
$data['signType'] = 'MD5';
$data['paySign'] = $this->getSign($data);
return json_encode($data);
}
if($res['result_code']=='FAIL'&& $res['err_code']=='OUT_TRADE_NO_USED'){
return "repeat";//订单号重复
}
if($res['result_code']=='FAIL'&& $res['err_code']=='ORDERPAID'){
return "payed";//已经支付
}
}else{
return $res;
}
}
/**
* 支付结果通知
*/
public function getNotify() {
return $res = $this->xmlToArray($GLOBALS["HTTP_RAW_POST_DATA"]);
}
/**
* 微信支付通知回应
*/
public function returnNotify() {
$data = array('return_code'=>'SUCCESS');
exit($this->arrayToXml($data));
}
/**
* 数组转xml
*/
public function arrayToXml($data){
$xml='<xml>';
foreach ($data as $k => $v){
$xml.="<".$k.">".$v."</".$k.">";
}
$xml.="</xml>";
return $xml;
}
/**
* xml转数组
*/
public function xmlToArray($xml){
return (array)simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
}
/**
* GET 请求
* @param string $url
*/
public function http_get($url){
$oCurl = curl_init();
if(stripos($url,"https://")!==FALSE){
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
}
curl_setopt($oCurl, CURLOPT_URL, $url);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
$sContent = curl_exec($oCurl);
$aStatus = curl_getinfo($oCurl);
curl_close($oCurl);
if(intval($aStatus["http_code"])==200){
return $sContent;
}else{
return false;
}
}
/**
* POST 请求
*/
public function http_post($url,$data,$post_file=false){
$oCurl = curl_init();
if(stripos($url,"https://")!==FALSE){
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
}
if (is_string($data) || $post_file) {
$strPOST = $data;
} else {
$aPOST = array();
foreach($data as $key=>$val){
$aPOST[] = $key."=".urlencode($val);
}
$strPOST = join("&", $aPOST);
}
curl_setopt($oCurl, CURLOPT_URL, $url);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($oCurl, CURLOPT_POST,true);
curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);
$sContent = curl_exec($oCurl);
$aStatus = curl_getinfo($oCurl);
curl_close($oCurl);
if(intval($aStatus["http_code"])==200){
return $sContent;
}else{
return false;
}
}
/**
* 带证书的支付请求
* @param unknown_type $url
* @param unknown_type $params
* @param unknown_type $method
* @param unknown_type $ssl
* @return boolean|mixed
*/
private function http($url, $params = array(), $method = 'GET', $ssl = false){
$opts = array(
CURLOPT_TIMEOUT => 30,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false
);
/* 根据请求类型设置特定参数 */
switch(strtoupper($method)){
case 'GET':
$opts[CURLOPT_URL] = $url .'?'. http_build_query($params);
break;
case 'POST':
$opts[CURLOPT_URL] = $url;
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = $params;
break;
}
if ($ssl) {
$pemPath = 'D:\www\sotjb\cert';
$pemCret = $pemPath.'\apiclient_cert.pem';
$pemKey = $pemPath.'\apiclient_key.pem';
if (!file_exists($pemCret)) {
echo '证书不存在1';
return false;
}
if (!file_exists($pemKey)) {
echo '密钥不存在';
return false;
}
$opts[CURLOPT_SSLCERTTYPE] = 'PEM';
$opts[CURLOPT_SSLCERT] = $pemCret;
$opts[CURLOPT_SSLKEYTYPE] = 'PEM';
$opts[CURLOPT_SSLKEY] = $pemKey;
}
/* 初始化并执行curl请求 */
$ch = curl_init();
curl_setopt_array($ch, $opts);
$data = curl_exec($ch);
$err = curl_errno($ch);
$errmsg = curl_error($ch);
curl_close($ch);
if ($err > 0) {
echo $errmsg;
return false;
}else {
return $data;
}
}
public function get_client_ip(){
if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'),'unknown')) {
$ip = getenv('HTTP_CLIENT_IP');
} elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'),'unknown')) {
$ip = getenv('HTTP_X_FORWARDED_FOR');
} elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'),'unknown')) {
$ip = getenv('REMOTE_ADDR');
} elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
$ip = $_SERVER['REMOTE_ADDR'];
}
return preg_match ( '/[\d\.]{7,15}/', $ip, $matches ) ? $matches [0] : '';
}
}
?>