在之前的一篇Android_APP微信支付接口开发中说了大概的实现方式和一些注意细节,这是说在微信支付中应该由我们的服务器来生成支付订单,以及对prepay_id的再次签名都应该在我们的服务器中完成。
下面就贴出服务器端的代码,主要是生成支付订单以及对prepay_id进行签名,然后将生成的签名prepay_id传给客户端,在客户端进行二次签名并调起支付:
<?php
$json = stripslashes($_SGET['json']); //去斜杠,stripslashes()函数:删除反斜杠
$getarray = json_decode($json,true); //转码,json_decode()函数:对JSON格式的字符串进行编码//json_encode()函数:对变量进行JSON编码
//防止SQL注入
foreach($getarray as $k=>$v){
$str=show_sql_keyword($v);
if($str){
$msg = urlencode ("含有非法字符");//urlencode()函数:编码URL字符串;urldecode()函数:解码已经编码的URL字符串
$data = array(result => 2,msg => $msg);
echo urldecode ( json_encode ( $data ) );
exit;
}
}
if($getarray['submit']){
$OS = $getarray['OS'];
$paymentid = $getarray['paymentid'];
$username = $getarray['username'];
$money = $getarray['money'];
$orderNO = $getarray['orderNO'];
//判断金额是否为负数
if($money<0){
$msg = urlencode ("提款金额不能为负数");
$data = array(result => 5,msg => $msg);
echo urldecode ( json_encode ( $data ) );
exit;
}
$sql = "select `username`,`uid` from ".$_SC['tablepre']."user where `username`='{$username}'";
$query = $_SGLOBAL['db']->query($sql);
$consignee = $_SGLOBAL['db']->fetch_array($query);
//用户判断
if($consignee['username'] == ''){
$msg = urlencode ("nouser");
$data = array(result => 2,msg => $msg);
echo urldecode ( json_encode ( $data ) );
exit;
}
//充值金额判断
$money = $money;
$money_rules = '/^([123456789]+)\d*(\.?\d{0,2})$|^(0{1})(\.{1}\d{0,2})$/';
if(!preg_match($money_rules,$money)){ //preg_match()函数:执行一个正则表达式匹配
$msg = urlencode ("error");
$data = array(result => 4,msg => $msg);
echo urldecode ( json_encode ( $data ) );
exit;
}
//订单号判断
if($orderNO != ''){
$orderNO = $getarray['orderNO']; //如果订单号不为空 则取出数组里的订单号
}else{
$orderNO = substr(date("YmdHis"),2,8).mt_rand(100000,999999); //否则随机生成一个订单号
//记录充值
$payorder_data = array(
'payuserid'=>$consignee['uid'],
'orderNO'=>$orderNO,
'tradeNO'=>'',
'payname'=>'',
'consignee'=>$consignee['username'],
'consigneeuid'=>'',
'money'=>$money,
'paymentid'=>$paymentid,
'paymentlabel'=>"微信支付",
'payresult'=>0,
'deliverrs'=>'',
'paydate'=>'',
'dateline'=>time(),
);
inserttable($_SC['tablepre'],"payorder", $payorder_data, 1 );
}
//上面大部分代码是根据每个项目需要进行的逻辑处理,下面微信支付生成支付订单以及prepay_id的主要代码
$prepayxml='';
$data=array();
//判断是否是Android端传过来的调起支付
if($OS=="Android"){
$noncestr=md5(rand());//int rand(void)函数:产生一个随机整数,返回0到getrandMax()之间的伪随机整数。
//亦可带参数int rand ( int $min , int $max )
$prepayid_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$appkey='API密钥'; //API密钥,在商户平台设置
//注:appid、body、mch_id、notify_url填写上对应的值
$preamdata = array (
'appid' => 'appid',
'body' => '标题',
'input_charset'=>'UTF-8',
'mch_id'=>'商户号',
'notify_url'=>'http://www.XXXX.com/payapi/wxnotify.php',
'out_trade_no'=>$orderNO,
'spbill_create_ip'=>"192.168.1.16",
'total_fee'=>$money*100,
'trade_type'=>"APP",
"nonce_str"=>$noncestr,
);
$sign=MakeSign($preamdata,$appkey); //生成sign签名
//echo $str;
// 参数数组,拼接成XML
$preamstr='<?xml version="1.0" encoding="UTF-8"?>';
$preamstr.='<xml>';
$preamstr.='<appid>appid</appid>'; //appid
$preamstr.='<body>标题</body>'; //标题
$preamstr.='<input_charset>UTF-8</input_charset>'; //编码,不然body不能出现中文
$preamstr.='<mch_id>商户号</mch_id>'; //商户号
$preamstr.='<notify_url>http://www.XXXX.com/payapi/wxnotify.php</notify_url>'; //成功支付回调地址
$preamstr.='<out_trade_no>'.$orderNO.'</out_trade_no>'; //订单号
$preamstr.='<spbill_create_ip>192.168.1.16</spbill_create_ip>'; //ip
$preamstr.='<total_fee>'.($money*100).'</total_fee>'; //金额,微信支付默认分为单位,所以要乘以100
$preamstr.='<trade_type>APP</trade_type>'; //交易类型
$preamstr.='<nonce_str>'.$noncestr.'</nonce_str>';
$preamstr.='<sign>'.$sign.'</sign>'; //sign签名
$preamstr.='</xml>';
//echo $preamstr;
//创建一个新curl资源
$ch = curl_init ();
// print_r($ch);
//设置URL和相应的选项
curl_setopt ( $ch, CURLOPT_URL, $prepayid_url );
curl_setopt ( $ch, CURLOPT_POST, 1 );
curl_setopt ( $ch, CURLOPT_HEADER, 0 );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $preamstr );
//抓取URL并把它传递给浏览器
$return = curl_exec ( $ch );
//关闭curl资源,并且释放系统资源
curl_close ( $ch );
$prepayxml = $return;
}elseif($OS=="IOS"){ //判断是否是IOS端传过来的调起支付
$noncestr=md5(rand());
$prepayid_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$appkey='API密钥';
$preamdata = array (
'appid' => 'appid',
'body' => '标题',
'input_charset'=>'UTF-8',
'mch_id'=>'商户号',
'notify_url'=>'http://www.XXXX.com/payapi/wxnotify.php',
'out_trade_no'=>$orderNO,
'spbill_create_ip'=>"192.168.1.16",
'total_fee'=>$money*100,
'trade_type'=>"APP",
"nonce_str"=>$noncestr,
);
$sign=MakeSign($preamdata,$appkey); //生成sign签名
//echo $str;
// 参数数组,拼接成XML
$preamstr='<?xml version="1.0" encoding="UTF-8"?>';
$preamstr.='<xml>';
$preamstr.='<appid>appid</appid>';
$preamstr.='<body>标题</body>';
$preamstr.='<input_charset>UTF-8</input_charset>';
$preamstr.='<mch_id>商户号</mch_id>';
$preamstr.='<notify_url>http://www.XXXX.com/payapi/wxnotify.php</notify_url>';
$preamstr.='<out_trade_no>'.$orderNO.'</out_trade_no>';
$preamstr.='<spbill_create_ip>192.168.1.16</spbill_create_ip>';
$preamstr.='<total_fee>'.($money*100).'</total_fee>';
$preamstr.='<trade_type>APP</trade_type>';
$preamstr.='<nonce_str>'.$noncestr.'</nonce_str>';
$preamstr.='<sign>'.$sign.'</sign>';
$preamstr.='</xml>';
//echo $preamstr;
//创建一个新curl资源
$ch = curl_init ();
// print_r($ch);
//设置URL和相应的选项
curl_setopt ( $ch, CURLOPT_URL, $prepayid_url );
curl_setopt ( $ch, CURLOPT_POST, 1 );
curl_setopt ( $ch, CURLOPT_HEADER, 0 );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $preamstr );
//抓取URL并把它传递给浏览器
$return = curl_exec ( $ch );
//关闭curl资源,并且释放系统资源
curl_close ( $ch );
//print_r($return);
//echo $return;
$prepayxml = $return;
}else{
$msg = urlencode ("error");
$data = array(result => 5,msg => $msg);
echo urldecode ( json_encode ( $data ) );
exit;
}
$data['msg']='done';
$data['orderno'] = $orderNO;
$data['result'] = 1;
$str=urldecode ( json_encode ( $data ) );
echo substr($str,0,-1).',"prepayxml":"'.$prepayxml.'"}';
}
/**
* 生成签名
* @param $arr 用到的参数数组
* @param $appkey
* @return string
*/
function MakeSign($arr,$appkey){
//签名步骤一:按字典序排序参数,ksort()函数:对关联数组按照键名进行升序排序
ksort($arr);
$string = ToUrlParams($arr);
//签名步骤二:在string后加入KEY
$string = $string . "&key=".$appkey;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
/**
* 将参数进行拼接
* @param $arr 要拼接的数组
* @return string 拼接后得到的字符串
*/
function ToUrlParams($arr){
$buff = "";
foreach ($arr as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
//trim()函数:移除字符串两侧的空白字符或其他预定义字符
$buff = trim($buff, "&");
return $buff;
}
?>
下面是调起支付后回调页面的服务器处理代码:
<?php
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
//writelog($postStr);
//$pos = strpos($postStr, 'xml');
//simplexml_load_string()函数:把XML字符串载入对象中,如果失败,则返回false
$obj=simplexml_load_string($postStr,'SimpleXMLElement', LIBXML_NOCDATA);
$obj=json_decode(json_encode($obj), true);
$sign=$obj['sign'];
$orderno=$obj['out_trade_no'];
$tradeno=$obj['transaction_id'];
$return_code = $obj['return_code'];
$return_msg = $obj['return_msg'];
if($return_code == 'SUCCESS'){
$xmlstr="<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg><sign>
<![CDATA[".$sign."]]></sign></xml>";
//下面是我们根据项目需要而进行的处理
echo $xmlstr;
include_once('../common.php');
$data = array(
"tradeNO"=>$tradeno,
"payresult"=> 1,
"paydate"=> $_SGLOBAL['timestamp'],
);
updatetable($_SC['tablepre'],"payorder",$data,"orderNO='".$orderno."'",0);
$sql = "select * from ".$_SC['tablepre']."payorder where orderNO='$orderno'";
$query = $_SGLOBAL['db']->query($sql);
$payorder = $_SGLOBAL['db']->fetch_array($query);
$money=$payorder['money'];
$sql="update ".$_SC['tablepre']."user set money=money+$money where username='".$payorder['consignee']."'";
$query = $_SGLOBAL['db']->query($sql);
//记录充值记录
$data=array(
"uid"=> $payorder['payuserid'],
"wishid"=> 0,
"iotype"=> 1,
"buytype"=> 9,
"money"=> $money,
"description"=> "微信充值".$money."元",
"dateline"=> $_SGLOBAL['timestamp'],
);
inserttable($_SC['tablepre'],"userfinance", $data, 1 );
}else{
$xmlstr="<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[".$return_msg."]]></return_msg></xml>";
echo $xmlstr;
}
//日志记录
function logger($log_content)
{
$max_size = 100000;
$log_filename = "log.xml";
if(file_exists($log_filename) and (abs(filesize($log_filename)) > $max_size)){unlink($log_filename);}
file_put_contents($log_filename,$log_content."\r\n", FILE_APPEND);
}
exit;
?>
最后,贴出客户端接收服务端生成的签名prepay_id再进行二次签名并调起支付的主要代码:
Map<String,String> resultunifiedorder;
resultunifiedorder = decodeXml(prepayId);
//生成签名参数并发起支付请求
genPayReq();
public Map<String,String> decodeXml(String content) {
try {
Map<String, String> xml = new HashMap<String, String>();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new StringReader(content));
int event = parser.getEventType();
while (event != XmlPullParser.END_DOCUMENT) {
String nodeName=parser.getName();
switch (event) {
case XmlPullParser.START_DOCUMENT:
break;
case XmlPullParser.START_TAG:
if("xml".equals(nodeName)==false){
xml.put(nodeName,parser.nextText());
}
break;
case XmlPullParser.END_TAG:
break;
}
event = parser.next();
}
return xml;
} catch (Exception e) {
Log.e("orion",e.toString());
}
return null;
}
//获取二次签名sign并调起微信支付
private void genPayReq() {
req.appId = Constants.APP_ID;
req.partnerId = Constants.MCH_ID;
req.prepayId = resultunifiedorder.get("prepay_id");
req.packageValue = "Sign=WXPay";
req.nonceStr = genNonceStr();
req.timeStamp = String.valueOf(genTimeStamp());
List<NameValuePair> signParams = new LinkedList<NameValuePair>();
signParams.add(new BasicNameValuePair("appid", req.appId));
signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
signParams.add(new BasicNameValuePair("package", req.packageValue));
signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));
req.sign = genAppSign(signParams);
sb.append("sign\n"+req.sign+"\n\n");
Log.e("orion++++++++++ ", signParams.toString());
//发送请求调起微信支付
msgApi.registerApp(Constants.APP_ID);
msgApi.sendReq(req);
}
在接收到prepay _id后,须对prepay_ id进行解码,最后再调用genPayReq()获取二次签名sign并调起微信支付。