thinkphp整合微信支付,绝对可行

时间:2022-09-24 18:13:56
http://www.inotcare.com/art/xinde/php/18.html
http://www.thinkphp.cn/code/1321.html
http://www.web-fish.com/program/php/856.html

thinkphp整合微信支付,绝对可行

  • _____卜在乎、卜解释
  • 2015-05-13
  • php
  • 围观4131次

上一篇文章是介绍了 ecshop 整合微信支付, 首先 ecshop 并不熟悉, 还有一些问题虽然解决了, 但是并不明白为什么, 这回用 thinkphp 整合 微信JSAPI 支付, 来特别说明一下(这个整合对微信JSAPI支付的原DEMO改动并不大)


这里 thinkphp使用的是3.2的版本, 框架系统目录改名为 _Core, 原先为 ThinkPHP


1, 首先, 去 https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course3_tmpl&lang=zh_CN 这上面下载  thinkphp整合微信支付,绝对可行

下载 JS API 接口文档


将 thinkphp整合微信支付,绝对可行  这些文件 拷贝至 _Core/Library/Vendor/Wxpay/jsapi  目录下(没有 Wxpay 和 jsapi 目录的话请自行创建), 将 WxPayPubHelper 目录下的文件拷贝至 _Core/Library/Vendor/Wxpay/jsapi 目录下 , 

thinkphp整合微信支付,绝对可行

WxPay.pub.config.php 改名为 WxPaypubconfig.php 


2, 应用入口文件名改为 Hz , 并生成一个新的模块, 名为 Pay, 在 Pay/Controller/ 目录下新建一个控制器 WxjsapiController.class.php 文件

thinkphp整合微信支付,绝对可行


WxjsapiController.class.php 文件的代码如下,


123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 <?phpnamespace
Pay\Controller;
use
Think\Controller;
class
WxjsapiController
extends
Controller {
     private
$wxpayConfig
;
    private
$wxpay
;
     public
function
_initialize(){
        header("Content-type: text/html; charset=utf-8");        vendor('Wxpay.jsapi.WxPaypubconfig');        vendor('Wxpay.jsapi.WxPayPubHelper');        vendor('Wxpay.jsapi.demo.log_');        vendor('Wxpay.jsapi.SDKRuntimeException');         $this->wxpayConfig = C('WXJSAPI_CONFIG');         $paycnfT
= M(
'pay_cnf');
        $this->wxpay = $paycnfT->where("P_C_Paytype = 'wxjsapi'")->find();         $this->wxpayConfig['appid'] = $this->wxpay['P_C_Appid'];     // 微信公众号身份的唯一标识        $this->wxpayConfig['appsecret'] = $this->wxpay['P_C_Secret'];// JSAPI接口中获取openid        $this->wxpayConfig['mchid'] = $this->wxpay['P_C_Pid'];           // 受理商ID        $this->wxpayConfig['key'] = $this->wxpay['P_C_Key'];         // 商户支付密钥Key        $this->wxpayConfig['js_api_call_url'] = $this->get_url();        $this->wxpayConfig['notifyurl'] = $this->wxpay['P_C_NotifyUrl'];        $this->wxpayConfig['returnurl'] = $this->wxpay['P_C_ReturnUrl'];         // 初始化WxPayConf_pub        $wxpaypubconfig
=
new
\WxPayConf_pub(
$this->wxpayConfig);
    }     /**     * 获取当前页面完整URL地址     */    private
function
get_url() {
        $sys_protocal
= isset(
$_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443'
?
'https://'
:
'http://';
        $php_self
=
$_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
        $path_info
= isset(
$_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
        $relate_url
= isset(
$_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $php_self.(isset($_SERVER['QUERY_STRING']) ? '?'
.
$_SERVER['QUERY_STRING'] : $path_info);
        return
$sys_protocal
. (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') . $relate_url;
    }     public
function
index() {
    }     /**     *  获取openid     */    private
function
get_openid() {
         $openid
=
$_COOKIE['apiopenid'];
         if(empty($openid)) {            // 使用jsapi接口            $jsApi
=
new
\JsApi_pub();
             // 通过code获得openid            if
(!isset(
$_GET['code'])) {
                // 触发微信返回code码                $url
=
$jsApi->createOauthUrlForCode(\WxPayConf_pub::$JS_API_CALL_URL);
                Header("Location: " . $url);            }else
{
                // 获取code码,以获取openid                $code
=
$_GET['code'];
                $jsApi->setCode($code);                $openid
=
$jsApi->getOpenId();
                setcookie('apiopenid',$openid, time() + 86400);            }        }        return
$openid
;
    }     /**     *  支付     */    public
function
pay() {
        if(isset($_SESSION['orderinfo']) && !empty($_SESSION['orderinfo']['orderid']) && !empty($_SESSION['orderinfo']['payprice'])) {            $orderid
=
$_SESSION['orderinfo']['orderid'];
            $payprice
=
$_SESSION['orderinfo']['payprice'];
        }         if(empty($orderid) || empty($payprice)) {            die('订单参数不完整!');        }         // 1,获取openid        $openid
=
$this->get_openid();
         // 2,使用统一支付接口        $unifiedOrder
=
new
\UnifiedOrder_pub();
         // 设置统一支付接口参数        // 设置必填参数        // appid已填,商户无需重复填写        // mch_id已填,商户无需重复填写        // noncestr已填,商户无需重复填写        // spbill_create_ip已填,商户无需重复填写        // sign已填,商户无需重复填写        $unifiedOrder->setParameter("openid",$openid);        $unifiedOrder->setParameter("body",$orderid
);                         
// 商品描述
        // 自定义订单号,此处仅作举例        //$timeStamp = time();        //$out_trade_no = \WxPayConf_pub::$APPID . $timeStamp;        $out_trade_no
=
$orderid;
        //$out_trade_no = time();        $unifiedOrder->setParameter("out_trade_no",$out_trade_no);             // 商户订单号        $unifiedOrder->setParameter("total_fee",$payprice
* 100);              
// 总金额
        $unifiedOrder->setParameter("notify_url", \WxPayConf_pub::$NOTIFY_URL); // 通知地址        $unifiedOrder->setParameter("trade_type","JSAPI");                     // 交易类型        // 非必填参数,商户可根据实际情况选填        //$unifiedOrder->setParameter("sub_mch_id", "XXXX");                 // 子商户号        //$unifiedOrder->setParameter("device_info", "XXXX");                    // 设备号        //$unifiedOrder->setParameter("attach", "XXXX");                     // 附加数据        //$unifiedOrder->setParameter("time_start", "XXXX");                 // 交易起始时间        //$unifiedOrder->setParameter("time_expire", "XXXX");                    // 交易结束时间        //$unifiedOrder->setParameter("goods_tag", "XXXX");                      // 商品标记        //$unifiedOrder->setParameter("openid", "XXXX");                     // 用户标识        //$unifiedOrder->setParameter("product_id", "XXXX");                 // 商品ID         $prepay_id
=
$unifiedOrder->getPrepayId();
         // 3,使用jsapi调起支付        $jsApi
=
new
\JsApi_pub();
        $jsApi->setPrepayId($prepay_id);        $jsApiParameters
=
$jsApi->getParameters();
        // echo $jsApiParameters;        $returnurl
= \WxPayConf_pub::
$RETURN_URL;
        $path
=  dirname(
__FILE__);
         $button
= <<<EOT
        <html>        <head>        <meta http-equiv="content-type"
content=
"text/html;charset=utf-8"
/>
        <title>微信安全支付</title>        </head>        <body>        <script type="text/javascript">        // 调用微信JS api 支付        function
jsApiCall(){
            WeixinJSBridge.invoke('getBrandWCPayRequest',{$jsApiParameters},function(res){                    //WeixinJSBridge.log(res.err_msg);                    alert(JSON.stringify({$jsApiParameters}));                    alert(res.err_code+'调试信息:'+res.err_desc+res.err_msg);                    if(res.err_msg.indexOf('ok')>0){                        window.location.href='{$returnurl}';                    }                });        }         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();            }        }    </script>    <button style="width:100%; height:40px; background:#FE6714; border:0px #FE6714 solid; cursor: pointer;  color:white;  font-size:16px;"
type=
"button"
onclick=
"callpay()"
>立即使用微信支付</button>
    </body>    </html>EOT;        echo
$button
;
    }     /**     *  服务器异步通知页面路径     */    public
function
Paynotify() {
        /**         * 通用通知接口demo         * ====================================================         * 支付完成后,微信会把相关支付和用户信息发送到商户设定的通知URL,         * 商户接收回调信息后,根据需要设定相应的处理流程。         *         * 这里举例使用log文件形式记录回调信息。         */        //include_once("./log_.php");        //include_once("../WxPayPubHelper/WxPayPubHelper.php");         // 使用通用通知接口        $notify
=
new
\Notify_pub();
         // 存储微信的回调        $xml
=
$GLOBALS['HTTP_RAW_POST_DATA'];
        $notify->saveData($xml);         // 验证签名,并回应微信。        // 对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,        // 微信会通过一定的策略(如30分钟共8次)定期重新发起通知        // 尽可能提高通知的成功率,但微信不保证通知最终能成功。        if($notify->checkSign() == FALSE){            $notify->setReturnParameter("return_code","FAIL");     // 返回状态码            $notify->setReturnParameter("return_msg","签名失败");  // 返回信息        }else
{
            $notify->setReturnParameter("return_code","SUCCESS");  // 设置返回码        }        $returnXml
=
$notify->returnXml();
        echo
$returnXml
;
         //==商户根据实际情况设置相应的处理流程,此处仅作举例=======         // 以log文件形式记录回调信息        $log_
=
new
\Log_();
        $log_name
= THINK_PATH .
"Library/Vendor/Wxpay/jsapi/demo/notify_url.log"// log文件路径
        $log_->log_result($log_name,"【接收到的notify通知】:\n"
.
$xml
.
"\n");
         if($notify->checkSign() == TRUE) {            if
(
$notify->data["return_code"] == "FAIL") {
                // 此处应该更新一下订单状态,商户自行增删操作                $log_->log_result($log_name,"【通信出错】:\n"
.
$xml
.
"\n");
            }elseif
(
$notify->data["result_code"] == "FAIL"){
                // 此处应该更新一下订单状态,商户自行增删操作                $log_->log_result($log_name,"【业务出错】:\n"
.
$xml
.
"\n");
            }else
{
                // 此处应该更新一下订单状态,商户自行增删操作                $order
=
$notify->getData();
                $orderid
=
$order["out_trade_no"];
                $log_->log_result($log_name,"【支付成功】:\n"
.
$orderid
.
"\n");
                 $shop
= A(
'Wap/Shop');
                $shop->EndPay($orderid,'wxjsapi');                $url
= U(
'/Wap/Shop/orderList/type/2');
                header('Location:'
.
$url);
            }             //商户自行增加处理流程,            //例如:更新订单状态            //例如:数据库操作            //例如:推送支付完成信息        }    }}
隐藏代码(双击代码内容快速复制)


Hz/Common/conf/config.php 配置如下, 只列出 C('WXJSAPI_CONFIG') 的配置


1234567 'MODULE_ALLOW_LIST'
=>
array('Pay'),
//
Wxjsapi
        'WXJSAPI_CONFIG'
=>
array(
                'SSLCERT_PATH'
=> __ROOT__ . THINK_PATH .
'Library/Vendor/Wxpay/jsapi/cacert/apiclient_cert.pem',       // 证书路径,注意应该填写绝对路径
                'SSLKEY_PATH'
=> __ROOT__ . THINK_PATH .
'Library/Vendor/Wxpay/jsapi/cacert/apiclient_key.pem',         // 证书路径,注意应该填写绝对路径
                'CURL_TIMEOUT'
=> 30
        ),
隐藏代码(双击代码内容快速复制)



WxPaypubconfig.php 的代码如下


12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758 <?php/***  
配置账号信息
*/ class
WxPayConf_pub
{    static
public
$APPID;
    static
public
$APPSECRET;
    static
public
$MCHID;
    static
public
$KEY;
    static
public
$JS_API_CALL_URL;
    static
public
$CURL_TIMEOUT;
    static
public
$SSLCERT_PATH;
    static
public
$SSLKEY_PATH;
    static
public
$NOTIFY_URL;
    static
public
$RETURN_URL;
     public
function
__construct($wxpayconfig
=
array()) {
        self::$APPID
=
$wxpayconfig['appid'];
        self::$APPSECRET
=
$wxpayconfig['appsecret'];
        self::$MCHID
=
$wxpayconfig['mchid'];
        self::$KEY
=
$wxpayconfig['key'];
        self::$JS_API_CALL_URL
=
$wxpayconfig['js_api_call_url'];
        self::$CURL_TIMEOUT
=
$wxpayconfig['CURL_TIMEOUT'];
        self::$SSLCERT_PATH
=
$wxpayconfig['SSLCERT_PATH'];
        self::$SSLKEY_PATH
=
$wxpayconfig['SSLKEY_PATH'];
        self::$NOTIFY_URL
=
$wxpayconfig['notifyurl'];
        self::$RETURN_URL
=
$wxpayconfig['returnurl'];
    }    /*    //=======【基本信息设置】=====================================    //微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看    const APPID = 'wx8888888888888888';    //受理商ID,身份标识    const MCHID = '18888887';    //商户支付密钥Key。审核通过后,在微信发送的邮件中查看    const KEY = '48888888888888888888888888888886';    //JSAPI接口中获取openid,审核后在公众平台开启开发模式后可查看    const APPSECRET = '48888888888888888888888888888887';     //=======【JSAPI路径设置】===================================    //获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面    const JS_API_CALL_URL = 'http://www.xxxxxx.com/demo/js_api_call.php';     //=======【证书路径设置】=====================================    //证书路径,注意应该填写绝对路径    const SSLCERT_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_cert.pem';    const SSLKEY_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_key.pem';     //=======【异步通知url设置】===================================    //异步通知url,商户根据实际开发过程设定    const NOTIFY_URL = 'http://www.xxxxxx.com/demo/notify_url.php';     //=======【curl超时设置】===================================    //本例程通过curl使用HTTP POST方法,此处可修改其超时时间,默认为30秒    const CURL_TIMEOUT = 30; */}
隐藏代码(双击代码内容快速复制)



修改 WxPayPubHelper.php 文件,

(1) 155 行 curl_setopt($ch, CURLOP_TIMEOUT, $second);   CURLOP_TIMEOUT 修改为 CURLOPT_TIMEOUT

(2) 821 行 curl_setopt($ch, CURLOP_TIMEOUT, $this->curl_timeout);  CURLOP_TIMEOUT 修改为 CURLOPT_TIMEOUT

(3) 175 行 if($data) { curl_close($ch); return $data; }  注释 curl_close($ch); 这行

(4) 将所有的 WxPayConf_pub:: 替换为 WxPayConf_pub::$

到此 WxPayPubHelper.php 就修改完了


WxjsapiController.class.php  文件方法说明 :

_initialize() 方法是整合微信支付的数据库配置与config.php的微信支付配置, 并初始化 WxPaypubconfig.php 

pay() 方法, 发起支付的方法,  $openid = $this->get_openid();  可以先打印出 openid, 看是否正常获取

Paynotify() 回调方法, 支付成功后的回调


现在说一下 微信平台的配置

1, 修改 开发者中心->网页账号->修改为 当前域名

2, 修改 微信支付->开发配置->添加测试授权目录->http://当前域名/Pay/Wxjsapi/ , 并添加测试用的微信号


注意, thinkphp 配置 'URL_MODEL' => 2,  能直接以 http://域名/控制器名/方法名  这种形式来访问, 不需要 index.php


total_fee 参数 最小为 1, 微信支付是以分 作为单位的

现在说明一下 微信支付中的错误:

1, appid and openid not match  这个其实不算是微信支付的错误, 主要是因为修改了微信支付的账号, 而原先的 openid 是采用 cookie 保存的, 先重新获取 openid, 并写入 cookie,  再重新开启 $openid = $_COOKIE['apiopenid']  , 这个问题一般不会碰到吧

2, 获取 openid 时的 redirect_uri 错误, 网页账号->修改域名, 带www与不带www 试下, 这个问题也没去详细了解, 现在没碰到这问题了

3, 点击  立即使用微信支付  没反应, 那么请查看参数是否完整, alert(JSON.stringify({$jsApiParameters}));   , 如果是 prepay_id 如果是空的话, 打开WxPayPubHelper.php  文件, 找到 getPrepayId() 方法, 打印 print_r($this->result);exit; 看看是什么错误

4, fail_no permission to execute 授权目录错误, 修改 微信支付->开发配置->支付测试里的授权目录

5, 最艹蛋的问题, fail_invalid_appid 这个弄了好久, 始终不行, 解决方法如下:

比如用户下订单后, 选择支付方式, 跳转到 Pay/Wxjsapi/pay 方法下, 莪这里是这么写的,

 $this->redirect(U('Pay/Wxjsapi/pay',array('payprice' => $order['S_O_Payprice'], 'orderid' => $order['S_O_OrderId']))); 比如 payprice 为 1, orderid 为 123456

传递了 订单号与支付金额, 生成的URL地址为  http://域名/Pay/Wxjsapi/pay/price/1/orderid/123456 , 但是这种跳转至 Pay/Wxjsapi/pay 方法下, 在发起支付的时候, 就会报这个错, 如果是这个地址, 那么微信支付的授权目录就得修改为 http://域名/Pay/Wxjsapi/pay/price, 到这里写不完整, 因为不知道对应的参数是什么, 所以, 在支付的时候传递 订单号 之类的参数, 莪使用的是存入 $_SESSION, 

$_SESSION['orderinfo']['orderid'] = $order['S_O_OrderId'];

$_SESSION['orderinfo']['payprice'] = $order['S_O_Payprice'];

$this->redirect(U('Pay/Wxjsapi/pay'), array());

然后在 pay() 方法 里获取 session, 这样的话, 用户支付时跳转的页面地址就变成 http://域名/Pay/Wxjsapi/pay  , 同样的, 授权目录只要修改为 http://域名/Pay/Wxjsapi/ 即可 , 而不是 http://域名/Pay/Wxjsapi/pay/ 否则会报 fail_no permission to execute 错误, 到此, 就解决了 fail_invalid_appid 这个错误

6, 回调地址的填写 http://域名/Pay/Wxjsapi/Paynotify 即可

7, 微信支付成功后, 点击完成按钮的跳转地址, 填写为你要跳转的地址即可,

    if(res.err_msg.indexOf('ok')>0){
        window.location.href='{$returnurl}';
    }

这里调用了

以上就是莪使用微信JS API支付时所碰到的问题, 现已解决, 可以成功使用微信支付


如果用户下订单后跳转至支付页面时, 需要带上一个参数的话, 比如 orderid ,  http://域名/Pay/Wxjsapi/pay/orderid/123456 那么, 授权目录改为 http://域名/Pay/Wxjsapi/pay/orderid/ 即可