微信公众号支付

下面以微信公众号支付为例,公众号支付必须在微信浏览器中调起。

同时微信公众号要开启相应的权限,主要有:

1、获取用户权限(在公众号管理平台-》设置-》公众号设置-》功能设置当中有“业务域名”、“js 接口安全域名”、“网页授权域名”,均需设置如www.baidu.com ,子目录的可设置为test.baidu.com/wxpaycs ,每月最多可修改 3 次)

2、商户平台的开发配置(在商户平台产品中心-》开发配置-》公众号支付里设置支付授权目录,最多可添加 5 个,可设置如http://www.baidu.com/pay/

新建支付服务类文件\Service\WxpayService.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?php
namespace Home\Service;

class WxpayService {
/**
* 微信配置信息
* 原包需要修改\lib\WxPay.Config.php里相应的配置,以下配置只供本类部分调用
* 原包需要修改\example\WxPay.JsApiPay.php里相应的引入require_once dirname ( __FILE__ ).DIRECTORY_SEPARATOR."../lib/WxPay.Api.php";
* 原包需要修改\lib\WxPay.Api.php里537行左右curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验
*/
function __construct() {
$payConfig = array(
'APPID' => D('Config')->get('wxpay_appid'), //'wx*******', //绑定支付的APPID(必须配置,开户邮件中可查看)
'MCHID' => D('Config')->get('wxpay_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
'notify_url' => C('PROTOCOL') . $_SERVER['SERVER_NAME'] . U('Pay/wxpay_notify_url'), //异步通知地址
'pay_log_txt' => __ROOT__ . '/Public/wxpay_log_bef74b0ac451f8e6e5301453f94799856.txt', //支付日志(需要先建立此文件)
);
$this->payConfig = $payConfig;
}

/**
* 获取支付宝配置参数
* @return array
*/
public function getConfig() {
return $this->payConfig;
}

/**
* 设置支付配置参数
* @param $data
*/
public function setConfig($data) {
$payConfig = $this->payConfig;
foreach ($data as $k => $v) {
$payConfig[$k] = $v;
}
$this->payConfig = $payConfig;
}

/**
* 支付处理
* @param $data
* @return mixed
*/
public function pay($data) {
Vendor('Wxpay.lib.WxPay#Api');
Vendor('Wxpay.example.WxPay#JsApiPay');
$config = $this->payConfig;
//①、获取用户openid
$tools = new \JsApiPay();
$WxData = S('WxData-'.session('uid'));//这里要带上会员uid标识,防止出现“下单账号与支付账号不一致”
//②、统一下单
$input = new \WxPayUnifiedOrder();
$input->SetBody($data['body']);
$input->SetAttach($data['attach']);
$input->SetOut_trade_no($data['out_trade_no']);
$input->SetTotal_fee($data['total_fee']);
$input->SetTime_start(date("YmdHis"));
$input->SetTime_expire(date("YmdHis", time() + 600));
$input->SetGoods_tag($data['goods_tag']);
$input->SetNotify_url($config['notify_url']);
$input->SetTrade_type("JSAPI");
$input->SetOpenid($WxData['openid']);
$order = \WxPayApi::unifiedOrder($input);
//dump($order);dump($data);dump(S('userInfo'));die;
$jsApiParameters = $tools->GetJsApiParameters($order);
$editAddress = $tools->GetEditAddressParameters(); //获取共享收货地址js函数参数
$reData['jsApiParameters'] = $jsApiParameters;
$reData['editAddress'] = $editAddress;
return $reData;
}

/**
* 记录支付异步记录
* @param $data
*/
public function setPaylog($data) {
$config = $this->payConfig;
$str = serialize($data) . "\r\n==================================================================================\r\n";
import("Org.Util.File");
$file = new \File($config['pay_log_txt']);
$realfiel = $file->getRealFile();
file_put_contents($realfiel, $str, FILE_APPEND);
}

/**
* 将xml转为数组
* @param $xml
* @return mixed
*/
public function xmlToArray($xml) {
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
}

支付处理控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<?php
namespace Home\Controller;

use Think\Controller;
use Common\Controller\HomeController;

/**
* 商城订单支付控制器
* Class PayController
* @package Home\Controller
*/
class PayController extends HomeController {

/**
* 初始化
*/
function __construct() {
parent::__construct();
$pay_scene = 1; //支付场景为订单支付
$ModelObj = D('Order'); //订单存储模型
$bodytags = '产品购买';
$is_test = true; //是否为测试,使用不同的支付金额
$this->pay_scene = $pay_scene;
$this->ModelObj = $ModelObj;
$this->bodytags = $bodytags;
$this->is_test = $is_test;
}

/**
* 微信支付
*/
public function wxpay() {
$ModelObj = $this->ModelObj;
//接收记录初始订单号
$order_number = I('order_number') ? I('order_number') : session('order_number');
session('order_number', $order_number);
if (I('order_number')) {
//如果传过来的订单号则跳转先获取用户信息
$this->redirect('wx_getuser');
}

$order = $ModelObj->where(array('order_number' => $order_number))->find();
//支付数据
$payData = array(
'out_trade_no' => $order['order_number'],
'attach' => $order_number, //商家数据包原封不动地返回
'total_fee' => $this->is_test ? 1 : $order['real_all_money'] * 100, ////微信支付是以分为单位的。
'body' => $this->bodytags,
'goods_tag' => $this->bodytags,
);
$reData = D('Wxpay', 'Service')->pay($payData);
$this->reData = $reData;
$this->order = $order;
$this->display('wxpay');
}

/**
* 获取微信用户信息
*/
public function wx_getuser() {
$payConfig = D('Wxpay', 'Service')->getConfig();
$appid = $payConfig['APPID'];
$appsecret = $payConfig['APPSECRET'];
//如果是换取了code或access_token处于有效状态则获取用户信息。如果换取了code动作后则原access_token就会失效。
if (I('code') || S('WxData-'.session('uid'))) {//这里要带上会员uid标识,防止出现“下单账号与支付账号不一致”
$code = I('code');
if (!S('WxData-'.session('uid'))) {//这里要带上会员uid标识,防止出现“下单账号与支付账号不一致”
$open_id_url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appid . '&secret=' . $appsecret . '&code=' . $code . '&grant_type=authorization_code';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $open_id_url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$result = curl_exec($ch);
curl_close($ch);
$access_token_info = json_decode($result, true);
//保存access_token信息
S('WxData-'.session('uid'), $access_token_info, $access_token_info['expires_in'] - 1000);//这里要带上会员uid标识,防止出现“下单账号与支付账号不一致”
}
$data = S('WxData-'.session('uid'));//这里要带上会员uid标识,防止出现“下单账号与支付账号不一致”
//换取用户信息
$user_info_url = 'https://api.weixin.qq.com/sns/userinfo?access_token=' . $data['access_token'] . '&openid=' . $data['openid'] . '&lang=zh_CN';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $user_info_url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$userinfo = curl_exec($ch);
curl_close($ch);
$userinfoArr = json_decode($userinfo, true);
S('userInfo', $userinfoArr); //保存用户信息可供存储这里不要用session,用数据缓存
$this->redirect('wxpay'); //回跳到支付处理页
} else {
//换取code
$code_url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $appid . '&redirect_uri=' . urlencode(C('PROTOCOL') . $_SERVER['SERVER_NAME'] . U(CONTROLLER_NAME . '/' . ACTION_NAME)) . '&response_type=code&scope=snsapi_userinfo&state=state_str#wechat_redirect';
header("Location:" . $code_url);
exit();
}
}

/**
* 微信支付回调地址
*/
public function wxpay_notify_url() {
$ModelObj = $this->ModelObj;
$payServiceObj = D('Wxpay', 'Service');
$payConfig = $payServiceObj->getConfig();
$xml = file_get_contents("php://input");
$array = $payServiceObj->xmlToArray($xml);
//记录日志
$payServiceObj->setPaylog($array);
//交易判断
if ($array['result_code'] == 'SUCCESS' && $array['return_code'] == 'SUCCESS' && $array['mch_id'] == $payConfig['MCHID']) {
$orderid = $array['out_trade_no'];
//交易支付成功
$re = $ModelObj->setPay($orderid, 2);
$array['pay_scene'] = $this->pay_scene; //支付场景指定
$re2 = D('PayWx')->addRecord($array); //增加支付详情参数
}

}

/**
* 微信成功跳转地址
*/
public function wx_resultinfo() {
$ModelObj = $this->ModelObj;
$order_number = I('out_trade_no');
$order = $ModelObj->where(array('order_number' => $order_number))->find();
$this->order = $order;
$this->display('pay_ok');
}

}

其中微信支付 thml 页面模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<!DOCTYPE html>
<html>
<head>
<title>微信在线付款</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">
//调用微信JS api 支付
function jsApiCall() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{$reData['jsApiParameters']},
function (res) {
WeixinJSBridge.log(res.err_msg);
//alert(res.err_code + res.err_desc + res.err_msg);
//alert(res.err_msg);
if (res.err_msg == 'get_brand_wcpay_request:ok') {
//alert('支付成功');
window.location.href = "{:U('wx_resultinfo',array('out_trade_no'=>$order['order_number']))}";
}
}
);
}

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>
<script type="text/javascript">
//获取共享地址
function editAddress() {
WeixinJSBridge.invoke(
'editAddress',
{$reData['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);
}
);
}
callpay();//调起支付
</script>
</head>
<body></body>
</html>