4、App接入微信支付,使用hbuilder中的支付
(1)支付配置参考http://ask.dcloud.net.cn/article/71 ,appid填写上面申请的移动应用的APPID
(2)在触发支付事件中添加代码
// 获取支付通道
plus.payment.getChannels(function(channels) {
for (var i in channels) {
var channel = channels[i];
if (channel.id == 'qhpay' || channel.id == 'qihoo' || channel.id == 'alipay') { // 过滤掉不支持的支付通道
continue;
}
pays[channel.id] = channel;//pays是全局变量,其定义var pays = {};
}
pay('wxpay');//自定义方法
}, function(e) {
alert('获取支付通道失败:' + e.message);
return false;
});
//调用统一下单接口及调起支付接口的前端代码
function pay(id){
// 从服务器请求支付订单
var payUrl = ...;//根据具体情况,自定义的支付接口地址
if(id != 'wxpay'){
plus.nativeUI.alert("不支持此支付通道!",null,"支付");
return;
}
plus.nativeUI.showWaiting();
var param = {};
param.bill_id = bill_id ;
... //创建支付单所需要的各参数
app.ajax(payUrl, param, function(data){
app.closeWaiting();
if(data.s == "ok") {
var info = data.data;
var param1 = {
appid: info.appid,
noncestr: info.nonce_str,
package: info.package,
partnerid: info.mch_id,
prepayid: info.prepay_id,
timestamp: info.timestamp,
sign: data.sign
};
plus.payment.request(pays[id], param1, function(result){
wxpayResultDeal(result);//微信支付成功后的结果处理自定义方法
},function(error){
plus.nativeUI.alert("支付失败:" + error.code);
});
} else {
app.closeWaiting();
plus.nativeUI.alert("支付失败1," + data.msg + "!");
}
}, true, function(e) {
app.closeWaiting();
plus.nativeUI.alert("支付失败2," + e.Message + "!");
});
}
/**************payUrl对应的地址的PHP主要代码****************/
//1、接收App传过来的参数
//2、参数验证(参数是否为空,参数对应订单是否存在等)
//3、拼凑统一下单所需的参数,示例如下
$order = array(
'id' => $pay_id,
'body' => '',
'attach' => '支付订单',
'orderId' => $code,
'total_fee' => $amount,
'start_time' => time(),
'end_time' => time() + 120
);
//4、调用统一下单接口方法wxpay获取预支付交易会话标识prepay_id等参数
//统一下单接口调用方法
function wxpay($order){
ini_set('date.timezone','Asia/Shanghai');
//引入相关的类文件
vendor('WxpayAPI.lib.WxPay#Api');
vendor('WxpayAPI.lib.WxAppPay#Config');//此文件跟WxPay.config一样,只是里面放的是app的配置
vendor('WxpayAPI.lib.WxPay#Data');
vendor('WxpayAPI.lib.WxPay#Exception');
vendor('WxpayAPI.lib.WxPay#Notify');
//调用统一支付接口
$input = new WxPayUnifiedOrder();
//商品描述----------------------------需要参数传递/统一信息
$input->SetBody($order['body']);
//附加数据
$input->SetAttach($order['attach']);
//商户订单号
$input->SetOut_trade_no($order['orderId']);
//总金额,要由元转换成分
$amount = $order['total_fee'] * 100;
$input->SetTotal_fee($amount);
//开始时间
$input->SetTime_start(date("YmdHis", time()));
//结束时间
$input->SetTime_expire(date("YmdHis", time() + 120));//设置开始时间和结束时间防止订单重复错误
//商品标记
$input->SetGoods_tag("***");
//异步通知地址,不能携带任何参数
$input->SetNotify_url("***");
//交易类型
$input->SetTrade_type("APP");
//商品id
$input->SetProduct_id($order['code']);
//获取统一支付接口结果
$result = WxPayApi::unifiedOrder($input);
return $result;
}
统一下单官方文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
App微信支付业务流程:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3 ,此文档中的重点内容如下图
上图红色标记处,着重3点
(1)需要对统一下单接口返回的参数再按统一下单接口中的签名方式生成新的签名传给App,进而调起微信App进行支付
(2)用于生成新签名的参数由appid、partnerid、prepayid、noncestr、timestamp、package,此处需要注意的是从统一下单接口获取的参数名称可能与需要的参数名称不一致,且存在不需要的参数,这里就需要自行处理下
(3)package参数的值是固定的“package”:"Sign:WXPay"
根据上述3点,对统一接口返回的参数二次签名
(1)统一接口返回的参数如下(我的)
//统一下单接口调用成功返回的参数如下
"appid":"wx8888888888888888",
"mch_id":"1111111111",
"nonce_str":"EZGnkkPkWsHyR99o",
"prepay_id":"wx10103600414559714e590f044155183369",
"result_code":"SUCCESS",
"return_code":"SUCCESS",
"return_msg":"OK",
"sign":"2E5F4007A38D109BCC9FAA0DAD8E401A",
"trade_type":"APP"
(2)文档中已说明参与二次签名的参数有appid,partnerid,prepayid,noncestr,timestamp,package,统一接口返回的参数与二次签名需要的参数名称不同,进行二次签名时要自己定义数组。
$keys = array('appid','mch_id','prepay_id','nonce_str','timestamp','package');
//$values是统一接口返回的参数,加上自己放进去的timestamp和package
foreach($values as $kk => $vv) {
if (in_array($kk, $keys)) {
$vals[$kk] = $vv;
} else {
continue;
}
}
unset($values);
foreach($vals as $kk => $vv){
switch($kk){
case "appid":
case "timestamp":
case "package":
$values[$kk] = $vv;
break;
case "mch_id":
$values['partnerid'] = $vv;
break;
case "prepay_id":
$values['prepayid'] = $vv;
break;
case "nonce_str":
$values['noncestr'] = $vv;
break;
}
}
//执行完上述代码后得到的$values就是二次签名所需要的参数
注:上面的timestamp既要自己二次签名使用、也要传给App让微信那边签名使用(微信那边签名要和自己的签名作比较,所以这个值要使用一个),所以获取到统一下单的参数(如果返回的参数中没有)后,自己加上这个参数。例子如下
//数组$result是统一接口返回的参数
$result['timestamp'] = time();
(3)二次签名需要的参数上面已经得到,下面就看下统一接口中签名使用的方法,使用统一接口中的签名方法对上面的参数进行签名(这步是在后台进行的,安全)。示例如下
//上面得到的签名参数。这里一定要使用统一接口中的签名方法,下面的是我的
function getSign($values){
//签名步骤一:按字典序排序参数
ksort($values);
$string = $this->ToUrlParams($values);
//签名步骤二:在string后加入KEY
$string = $string . "&key=".$config['APPKEY'];
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
/**
* 格式化参数格式化成url参数
*/
protected function ToUrlParams($values){
$buff = "";
foreach ($values as $k => $v){
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
(4)把二次生成的签名、统一下单接口返回的参数和生成二次签名用到的而统一下单接口没返回的参数 一并传给hbuilder的调起支付方法。代码如下,
var param = {
appid: "统一下单接口返回的appid",
noncestr: "统一下单接口返回的nonce_str",
package: "Sign=WXPay",
partnerid: "统一下单接口返回的mch_id",
prepayid: "统一下单接口返回的prepay_id",
timestamp: "自己二次签名的时间戳,验证签名时微信也用这个值",
sign: "用统一下单接口返回的参数获取的签名"
};
plus.payment.request(pays[id], param, function(result){
wxpayResultDeal(result);//根据返回的参数result及业务逻辑自行写这个支付成功的结果处理
},function(error){
plus.nativeUI.alert("支付失败:" + error.code);
});