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 这上面下载
下载 JS API 接口文档
将 这些文件 拷贝至 _Core/Library/Vendor/Wxpay/jsapi 目录下(没有 Wxpay 和 jsapi 目录的话请自行创建), 将 WxPayPubHelper 目录下的文件拷贝至 _Core/Library/Vendor/Wxpay/jsapi 目录下 ,
WxPay.pub.config.php 改名为 WxPaypubconfig.php
2, 应用入口文件名改为 Hz , 并生成一个新的模块, 名为 Pay, 在 Pay/Controller/ 目录下新建一个控制器 WxjsapiController.class.php 文件
WxjsapiController.class.php 文件的代码如下,
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 | <?php namespace
use
class
extends private
; private
; public _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
'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
$this ->wxpayConfig); } /** * 获取当前页面完整URL地址 */ private get_url() { $sys_protocal
$_SERVER [ 'SERVER_PORT' ]) && $_SERVER [ 'SERVER_PORT' ] == '443'
'https://'
'http://' ; $php_self
$_SERVER [ 'PHP_SELF' ] ? $_SERVER [ 'PHP_SELF' ] : $_SERVER [ 'SCRIPT_NAME' ]; $path_info
$_SERVER [ 'PATH_INFO' ]) ? $_SERVER [ 'PATH_INFO' ] : '' ; $relate_url
$_SERVER [ 'REQUEST_URI' ]) ? $_SERVER [ 'REQUEST_URI' ] : $php_self .(isset( $_SERVER [ 'QUERY_STRING' ]) ? '?'
$_SERVER [ 'QUERY_STRING' ] : $path_info ); return . (isset( $_SERVER [ 'HTTP_HOST' ]) ? $_SERVER [ 'HTTP_HOST' ] : '' ) . $relate_url ; } public index() { } /** * 获取openid */ private get_openid() { $openid
$_COOKIE [ 'apiopenid' ]; if ( empty ( $openid )) { // 使用jsapi接口 $jsApi
new // 通过code获得openid if
$_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
; } /** * 支付 */ public 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 // 设置统一支付接口参数 // 设置必填参数 // 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
// 总金额 $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 ->setPrepayId( $prepay_id ); $jsApiParameters
$jsApi ->getParameters(); // echo $jsApiParameters; $returnurl
$RETURN_URL ; $path
__FILE__ ); $button
<html> <head> <meta http-equiv= "content-type"
"text/html;charset=utf-8"
<title>微信安全支付</title> </head> <body> <script type= "text/javascript" > // 调用微信JS api 支付 function
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
{ if
"undefined" ){ if ( document.addEventListener ){ document.addEventListener( 'WeixinJSBridgeReady' , jsApiCall, false); } else (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;"
"button"
"callpay()"
</body> </html> EOT; echo
; } /** * 服务器异步通知页面路径 */ public Paynotify() { /** * 通用通知接口demo * ==================================================== * 支付完成后,微信会把相关支付和用户信息发送到商户设定的通知URL, * 商户接收回调信息后,根据需要设定相应的处理流程。 * * 这里举例使用log文件形式记录回调信息。 */ //include_once("./log_.php"); //include_once("../WxPayPubHelper/WxPayPubHelper.php"); // 使用通用通知接口 $notify
new // 存储微信的回调 $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
; //==商户根据实际情况设置相应的处理流程,此处仅作举例======= // 以log文件形式记录回调信息 $log_
new
$log_name
"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
'Wap/Shop' ); $shop ->EndPay( $orderid , 'wxjsapi' ); $url
'/Wap/Shop/orderList/type/2' ); header( 'Location:'
$url ); } //商户自行增加处理流程, //例如:更新订单状态 //例如:数据库操作 //例如:推送支付完成信息 } } } |
Hz/Common/conf/config.php 配置如下, 只列出 C('WXJSAPI_CONFIG') 的配置
1234567 | 'MODULE_ALLOW_LIST'
array ( 'Pay' ), // 'WXJSAPI_CONFIG'
array ( 'SSLCERT_PATH'
'Library/Vendor/Wxpay/jsapi/cacert/apiclient_cert.pem' , // 证书路径,注意应该填写绝对路径 'SSLKEY_PATH'
'Library/Vendor/Wxpay/jsapi/cacert/apiclient_key.pem' , // 证书路径,注意应该填写绝对路径 'CURL_TIMEOUT'
), |
WxPaypubconfig.php 的代码如下
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758 | <?php /** * */ class
{ static $APPID ; static $APPSECRET ; static $MCHID ; static $KEY ; static $JS_API_CALL_URL ; static $CURL_TIMEOUT ; static $SSLCERT_PATH ; static $SSLKEY_PATH ; static $NOTIFY_URL ; static $RETURN_URL ; public __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/ 即可