thinkphp整合微信支付,绝对可行
来源:互联网 发布:在线网络测速 编辑:程序博客网 时间:2024/06/05 17:15
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 文件的代码如下,
<?php
namespace
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') 的配置
'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 的代码如下
<?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/ 即可
- thinkphp整合微信支付,绝对可行
- thinkphp 整合微信支付
- Thinkphp整合微信支付功能
- ThinkPHP整合微信支付之JSAPI模式
- ThinkPHP整合微信支付之刷卡模式
- thinkphp整合系列之微信公众号支付
- thinkphp整合系列之微信公众号支付
- thinkphp整合系列之微信公众号支付
- ThinkPHP整合微信支付之刷卡模式
- ThinkPHP整合微信支付之JSAPI模式
- thinkphp整合系列之微信公众号支付
- thinkphp微信支付
- 整合微信支付
- ThinkPHP整合微信支付之Native 扫码支付 模式二
- ThinkPHP整合微信支付之Native 扫码支付 模式一
- ThinkPHP整合微信支付之Native 扫码支付 模式一
- ThinkPHP整合微信支付之Native 扫码支付 模式二
- ThinkPHP-微信支付案例
- hdu 2732 Leapin' Lizards (最大流)★
- Eclipse中使用GIT(一):配置和安装GIT
- C++ 三天打鱼两天晒网(YTU OJ2811)
- JAVA MAIL发送邮件实例
- C语言常用库函数
- thinkphp整合微信支付,绝对可行
- git 使用
- seajs 2.3.0 添加jquery
- HDU 1020 Encoding 字符串 水题
- 析构函数
- 安卓布局之线性布局(Linearlayout)
- [网络推广]直通车学习
- 利用Fiddler对手机进行抓包
- 对android中ActionBar中setDisplayHomeAsUpEnabled和setHomeButtonEnabled和setDisplayShowHomeEnabled方法的理解