搜尋
首頁後端開發php教程PHP微信支付开发

1.开发环境
Thinkphp 3.2.3
微信:服务号,已认证
开发域名:http://test.paywechat.com (自定义的域名,外网不可访问)

2.需要相关文件和权限
微信支付需申请开通
微信公众平台开发者文档:http://mp.weixin.qq.com/wiki/home/index.html
微信支付开发者文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
微信支付SDK下载地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

3.开发
下载好微信支付PHP版本的SDK,文件目录为下图:


把微信支付SDK的Cert和Lib目录放入Thinkphp,目录为

现在介绍微信支付授权目录问题,首先是微信支付开发配置里面的支付授权目录填写,

然后填写JS接口安全域。

最后设置网页授权

这些设置完,基本完成一半,注意设置的目录和我thinkphp里面的目录。

4.微信支付配置

把相关配置填写正确。

/** * 配置账号信息 */class WxPayConfig{    //=======【基本信息设置】=====================================    //    /** * TODO: 修改这里配置为您自己申请的商户信息 * 微信公众号信息配置 * * APPID:绑定支付的APPID(必须配置,开户邮件中可查看) * * MCHID:商户号(必须配置,开户邮件中可查看) * * KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置) * 设置地址:https://pay.weixin.qq.com/index.php/account/api_cert * * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置), * 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN * @var string */    const APPID = '';    const MCHID = '';    const KEY = '';    const APPSECRET = '';    //=======【证书路径设置】=====================================    /** * TODO:设置商户证书路径 * 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载, * API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert,下载之前需要安装商户操作证书) * @var path */    const SSLCERT_PATH = '../cert/apiclient_cert.pem';    const SSLKEY_PATH = '../cert/apiclient_key.pem';    //=======【curl代理设置】===================================    /** * TODO:这里设置代理机器,只有需要代理的时候才设置,不需要代理,请设置为0.0.0.0和0 * 本例程通过curl使用HTTP POST方法,此处可修改代理服务器, * 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此时不开启代理(如有需要才设置) * @var unknown_type */    const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220";    const CURL_PROXY_PORT = 0;//8080;    //=======【上报信息配置】===================================    /** * TODO:接口调用上报等级,默认紧错误上报(注意:上报超时间为【1s】,上报无论成败【永不抛出异常】, * 不会影响接口调用流程),开启上报之后,方便微信监控请求调用的质量,建议至少 * 开启错误上报。 * 上报等级,0.关闭上报; 1.仅错误出错上报; 2.全量上报 * @var int */    const REPORT_LEVENL = 1;}

现在开始贴出代码:

namespace Wechat\Controller;use Think\Controller;/** * 父类控制器,需要继承 * @file ParentController.class.php * @author Gary <lizhiyong2204@sina.com> * @date 2015年8月4日 * @todu */class ParentController extends Controller {     protected $options = array (            'token' => '', // 填写你设定的key            'encodingaeskey' => '', // 填写加密用的EncodingAESKey            'appid' => '', // 填写高级调用功能的app id            'appsecret' => '', // 填写高级调用功能的密钥            'debug' => false,            'logcallback' => ''    );          public $errCode = 40001;       public $errMsg = "no access";      /** * 获取access_token * @return mixed|boolean|unknown */    public function getToken(){        $cache_token = S('exp_wechat_pay_token');        if(!empty($cache_token)){            return $cache_token;        }        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s';        $url = sprintf($url,$this->options['appid'],$this->options['appsecret']);              $result = $this->http_get($url);        $result = json_decode($result,true);          if(empty($result)){            return false;        }           S('exp_wechat_pay_token',$result['access_token'],array('type'=>'file','expire'=>3600));        return $result['access_token'];    }    /** * 发送客服消息 * @param array $data 消息结构{"touser":"OPENID","msgtype":"news","news":{...}} */    public function sendCustomMessage($data){        $token = $this->getToken();        if (empty($token)) return false;               $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s';        $url = sprintf($url,$token);        $result = $this->http_post($url,self::json_encode($data));        if ($result)        {            $json = json_decode($result,true);            if (!$json || !empty($json['errcode'])) {                $this->errCode = $json['errcode'];                $this->errMsg = $json['errmsg'];                return false;            }            return $json;        }        return false;    }    /** * 发送模板消息 * @param unknown $data * @return boolean|unknown */    public function sendTemplateMessage($data){        $token = $this->getToken();        if (empty($token)) return false;        $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";        $url = sprintf($url,$token);        $result = $this->http_post($url,self::json_encode($data));        if ($result)        {            $json = json_decode($result,true);            if (!$json || !empty($json['errcode'])) {                $this->errCode = $json['errcode'];                $this->errMsg = $json['errmsg'];                return false;            }            return $json;        }        return false;    }    public function getFileCache($name){        return S($name);    }    /** * 微信api不支持中文转义的json结构 * @param array $arr */    static function json_encode($arr) {        $parts = array ();        $is_list = false;        //Find out if the given array is a numerical array        $keys = array_keys ( $arr );        $max_length = count ( $arr ) - 1;        if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1            $is_list = true;            for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position                if ($i != $keys [$i]) { //A key fails at position check.                    $is_list = false; //It is an associative array.                    break;                }            }        }        foreach ( $arr as $key => $value ) {            if (is_array ( $value )) { //Custom handling for arrays                if ($is_list)                    $parts [] = self::json_encode ( $value ); /* :RECURSION: */                else                    $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */            } else {                $str = '';                if (! $is_list)                    $str = '"' . $key . '":';                //Custom handling for multiple data types                if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)                    $str .= $value; //Numbers                elseif ($value === false)                $str .= 'false'; //The booleans                elseif ($value === true)                $str .= 'true';                else                    $str .= '"' . addslashes ( $value ) . '"'; //All other things                // :TODO: Is there any more datatype we should be in the lookout for? (Object?)                $parts [] = $str;            }        }        $json = implode ( ',', $parts );        if ($is_list)            return '[' . $json . ']'; //Return numerical JSON        return '{' . $json . '}'; //Return associative JSON    }    /** +---------------------------------------------------------- * 生成随机字符串 +---------------------------------------------------------- * @param int $length 要生成的随机字符串长度 * @param string $type 随机码类型:0,数字+大小写字母;1,数字;2,小写字母;3,大写字母;4,特殊字符;-1,数字+大小写字母+特殊字符 +---------------------------------------------------------- * @return string +---------------------------------------------------------- */    static public function randCode($length = 5, $type = 2){        $arr = array(1 => "0123456789", 2 => "abcdefghijklmnopqrstuvwxyz", 3 => "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4 => "~@#$%^&*(){}[]|");        if ($type == 0) {            array_pop($arr);            $string = implode("", $arr);        } elseif ($type == "-1") {            $string = implode("", $arr);        } else {            $string = $arr[$type];        }        $count = strlen($string) - 1;        $code = '';        for ($i = 0; $i < $length; $i++) {            $code .= $string[rand(0, $count)];        }        return $code;    }       /** * GET 请求 * @param string $url */    private 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 请求 * @param string $url * @param array $param * @param boolean $post_file 是否文件上传 * @return string content */    private function http_post($url,$param,$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($param) || $post_file) {            $strPOST = $param;        } else {            $aPOST = array();            foreach($param 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;        }    }}
namespace Wechat\Controller;use Wechat\Controller\ParentController;/** * 微信支付测试控制器 * @file TestController.class.php * @author Gary <lizhiyong2204@sina.com> * @date 2015年8月4日 * @todu */class TestController extends ParentController {    private $_order_body       = 'xxx';    private $_order_goods_tag  = 'xxx';    public function __construct(){        parent::__construct();        require_once ROOT_PATH."Api/lib/WxPay.Api.php";        require_once ROOT_PATH."Api/lib/WxPay.JsApiPay.php";    }    public function index(){        //①、获取用户openid        $tools = new \JsApiPay();        $openId = $tools->GetOpenid();               //②、统一下单        $input = new \WxPayUnifiedOrder();             //商品描述        $input->SetBody($this->_order_body);        //附加数据,可以添加自己需要的数据,微信回异步回调时会附加这个数据        $input->SetAttach('xxx');        //商户订单号        $out_trade_no = \WxPayConfig::MCHID.date("YmdHis");        $input->SetOut_trade_no($out_trade_no);        //总金额,订单总金额,只能为整数,单位为分         $input->SetTotal_fee(1);        //交易起始时间        $input->SetTime_start(date("YmdHis"));        //交易结束时间        $input->SetTime_expire(date("YmdHis", time() + 600));        //商品标记        $input->SetGoods_tag($this->_order_goods_tag);        //通知地址,接收微信支付异步通知回调地址 SITE_URL=http://test.paywechat.com/Charge        $notify_url = SITE_URL.'/index.php/Test/notify.html';        $input->SetNotify_url($notify_url);        //交易类型        $input->SetTrade_type("JSAPI");        $input->SetOpenid($openId);        $order = \WxPayApi::unifiedOrder($input);        $jsApiParameters = $tools->GetJsApiParameters($order);        //获取共享收货地址js函数参数        $editAddress = $tools->GetEditAddressParameters();        $this->assign('openId',$openId);        $this->assign('jsApiParameters',$jsApiParameters);        $this->assign('editAddress',$editAddress);        $this->display();          }    /** * 异步通知回调方法 */    public function notify(){        require_once ROOT_PATH."Api/lib/notify.php";        $notify = new \PayNotifyCallBack();        $notify->Handle(false);        //这里的IsSuccess是我自定义的一个方法,后面我会贴出这个文件的代码,供参考。        $is_success = $notify->IsSuccess();          $bdata 		= $is_success['data'];           //支付成功        if($is_success['code'] == 1){                      $news = array(                    'touser' => $bdata['openid'],                    'msgtype' => 'news',                    'news'  => array (                            'articles'=> array (                                    array(                                            'title' => '订单支付成功',                                            'description' => "支付金额:{$bdata['total_fee']}\n".                                            "微信订单号:{$bdata['transaction_id']}\n"                                            'picurl' => '',                                            'url' => ''                                             )                            )                    )            );            //发送微信支付通知            $this->sendCustomMessage($news);                    }else{//支付失败        }    }    /** * 支付成功页面 * 不可靠的回调 */    public function ajax_PaySuccess(){        //订单号        $out_trade_no = I('post.out_trade_no');        //支付金额        $total_fee    = I('post.total_fee');        /*相关逻辑处理*/    }

贴上模板HTML

<html><head>    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>    <meta name="viewport" content="width=device-width, initial-scale=1"/>     <title>微信支付样例-支付</title>    <script type="text/javascript"> //调用微信JS api 支付 function jsApiCall() { WeixinJSBridge.invoke( 'getBrandWCPayRequest', {$jsApiParameters}, function(res){ WeixinJSBridge.log(res.err_msg); //取消支付 if(res.err_msg == 'get_brand_wcpay_request:cancel'){ //处理取消支付的事件逻辑 }else if(res.err_msg == "get_brand_wcpay_request:ok"){ /*使用以上方式判断前端返回,微信团队郑重提示: res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 这里可以使用Ajax提交到后台,处理一些日志,如Test控制器里面的ajax_PaySuccess方法。 */ } alert(res.err_code+res.err_desc+res.err_msg); } ); } function callpay() { if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', jsApiCall, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', jsApiCall); document.attachEvent('onWeixinJSBridgeReady', jsApiCall); } }else{ jsApiCall(); } } //获取共享地址 function editAddress() { WeixinJSBridge.invoke( 'editAddress', {$editAddress}, function(res){ var value1 = res.proviceFirstStageName; var value2 = res.addressCitySecondStageName; var value3 = res.addressCountiesThirdStageName; var value4 = res.addressDetailInfo; var tel = res.telNumber; alert(value1 + value2 + value3 + value4 + ":" + tel); } ); } window.onload = function(){ if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', editAddress, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', editAddress); document.attachEvent('onWeixinJSBridgeReady', editAddress); } }else{ editAddress(); } }; </script></head><body>    <br/>    <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">1分</span>钱</b></font><br/><br/>    <div align="center">        <button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" onclick="callpay()" >立即支付</button>    </div></body></html>

notify.php文件代码,这里有在官方文件里新添加的一个自定义方法。

require_once ROOT_PATH."Api/lib/WxPay.Api.php";require_once ROOT_PATH.'Api/lib/WxPay.Notify.php';require_once ROOT_PATH.'Api/lib/log.php';//初始化日志$logHandler= new \CLogFileHandler(ROOT_PATH."/logs/".date('Y-m-d').'.log');$log = \Log::Init($logHandler, 15);class PayNotifyCallBack extends WxPayNotify {    protected $para = array('code'=>0,'data'=>'');    //查询订单    public function Queryorder($transaction_id) {        $input = new \WxPayOrderQuery();        $input->SetTransaction_id($transaction_id);        $result = \WxPayApi::orderQuery($input);        \Log::DEBUG("query:" . json_encode($result));        if(array_key_exists("return_code", $result)            && array_key_exists("result_code", $result)            && $result["return_code"] == "SUCCESS"            && $result["result_code"] == "SUCCESS")        {            return true;        }        $this->para['code'] = 0;        $this->para['data'] = '';        return false;    }    //重写回调处理函数    public function NotifyProcess($data, &$msg) {        \Log::DEBUG("call back:" . json_encode($data));        $notfiyOutput = array();        if(!array_key_exists("transaction_id", $data)){            $msg = "输入参数不正确";            $this->para['code'] = 0;            $this->para['data'] = '';            return false;        }        //查询订单,判断订单真实性        if(!$this->Queryorder($data["transaction_id"])){            $msg = "订单查询失败";            $this->para['code'] = 0;            $this->para['data'] = '';            return false;        }        $this->para['code'] = 1;        $this->para['data'] = $data;        return true;    }    /** * 自定义方法 检测微信端是否回调成功方法 * @return multitype:number string */    public function IsSuccess(){        return $this->para;    }}

到这里基本上完成,可以在微信端打开http://test.paywechat.com/Charge/index.php/Test/index/
我的环境,HTTP服务器没有重写url,微信支付继续探索中,有些地方可能写的有问题或不足,望大家谅解,互相学习。

版权声明:本文为博主原创文章,未经博主允许不得转载。

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
哪些常見問題會導致PHP會話失敗?哪些常見問題會導致PHP會話失敗?Apr 25, 2025 am 12:16 AM

PHPSession失效的原因包括配置錯誤、Cookie問題和Session過期。 1.配置錯誤:檢查並設置正確的session.save_path。 2.Cookie問題:確保Cookie設置正確。 3.Session過期:調整session.gc_maxlifetime值以延長會話時間。

您如何在PHP中調試與會話相關的問題?您如何在PHP中調試與會話相關的問題?Apr 25, 2025 am 12:12 AM

在PHP中調試會話問題的方法包括:1.檢查會話是否正確啟動;2.驗證會話ID的傳遞;3.檢查會話數據的存儲和讀取;4.查看服務器配置。通過輸出會話ID和數據、查看會話文件內容等方法,可以有效診斷和解決會話相關的問題。

如果session_start()被多次調用會發生什麼?如果session_start()被多次調用會發生什麼?Apr 25, 2025 am 12:06 AM

多次調用session_start()會導致警告信息和可能的數據覆蓋。 1)PHP會發出警告,提示session已啟動。 2)可能導致session數據意外覆蓋。 3)使用session_status()檢查session狀態,避免重複調用。

您如何在PHP中配置會話壽命?您如何在PHP中配置會話壽命?Apr 25, 2025 am 12:05 AM

在PHP中配置會話生命週期可以通過設置session.gc_maxlifetime和session.cookie_lifetime來實現。 1)session.gc_maxlifetime控制服務器端會話數據的存活時間,2)session.cookie_lifetime控制客戶端cookie的生命週期,設置為0時cookie在瀏覽器關閉時過期。

使用數據庫存儲會話的優點是什麼?使用數據庫存儲會話的優點是什麼?Apr 24, 2025 am 12:16 AM

使用數據庫存儲會話的主要優勢包括持久性、可擴展性和安全性。 1.持久性:即使服務器重啟,會話數據也能保持不變。 2.可擴展性:適用於分佈式系統,確保會話數據在多服務器間同步。 3.安全性:數據庫提供加密存儲,保護敏感信息。

您如何在PHP中實現自定義會話處理?您如何在PHP中實現自定義會話處理?Apr 24, 2025 am 12:16 AM

在PHP中實現自定義會話處理可以通過實現SessionHandlerInterface接口來完成。具體步驟包括:1)創建實現SessionHandlerInterface的類,如CustomSessionHandler;2)重寫接口中的方法(如open,close,read,write,destroy,gc)來定義會話數據的生命週期和存儲方式;3)在PHP腳本中註冊自定義會話處理器並啟動會話。這樣可以將數據存儲在MySQL、Redis等介質中,提升性能、安全性和可擴展性。

什麼是會話ID?什麼是會話ID?Apr 24, 2025 am 12:13 AM

SessionID是網絡應用程序中用來跟踪用戶會話狀態的機制。 1.它是一個隨機生成的字符串,用於在用戶與服務器之間的多次交互中保持用戶的身份信息。 2.服務器生成並通過cookie或URL參數發送給客戶端,幫助在用戶的多次請求中識別和關聯這些請求。 3.生成通常使用隨機算法保證唯一性和不可預測性。 4.在實際開發中,可以使用內存數據庫如Redis來存儲session數據,提升性能和安全性。

您如何在無狀態環境(例如API)中處理會議?您如何在無狀態環境(例如API)中處理會議?Apr 24, 2025 am 12:12 AM

在無狀態環境如API中管理會話可以通過使用JWT或cookies來實現。 1.JWT適合無狀態和可擴展性,但大數據時體積大。 2.Cookies更傳統且易實現,但需謹慎配置以確保安全性。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境