注意点:
1、签名是俩次签名,第一次,是获取prepay_id时候,第二次签名是最后返回结果的时候,所谓签名就是对所传递参数的一个加密操作;
2、加签方式默认md5就可以,如果要改成HMAC-SHA256,需要在第一次签名时候,传递参数:$newPara["sign_type"] = "HMAC-SHA256"; 然后在签名加密的时候,使用:$String = hash_hmac("sha256",参数拼接字符串,密钥);
3、使用的密钥,不是AppSecret,而是在微信商户平台上自己设定的api密钥 32位;
4、重点:body参数格式:应用市场上的APP名字-实际商品名称,比如:天天爱消除-游戏充值。
5、安卓端可以配合下面框架使用
代码:/*
//生成支付信息
$wx = new TegWXPay();
$res = $wx->weChatPay(订单号,金额,商品名称);
echo $res;
//服务器端异步回调
$wx = new TegWXPay();
$re_arr = $wx->verifyNotify();
if($re_arr !== false){
//订单信息放在$re_arr里面,下面开始处理业务逻辑...
//下面代码告诉微信处理成功
exit('');
}else{
//下面代码告诉微信处理失败了
exit('');
}
*/
//weixinpay class
class TegWXPay{
private $config = array(
'appid' => "", /*微信开放平台上的应用id*/
'mch_id' => "", /*微信申请成功之后邮件中的商户id*/
'api_key' => "", /*在微信商户平台上自己设定的api密钥 32位*/
'notify_url' => '', /*自定义的回调程序地址*/
'appname' => '' //app名称
);
//入口函数
public function weChatPay($order_num,$price,$subject)
{
$json = array();
//生成预支付交易单的必选参数:
$newPara = array();
//应用ID
$newPara["appid"] = $this->config['appid']; //商户appid;
//商品描述
$newPara["body"] = $this->config['appname']."-".$subject;
//设备号
$newPara["device_info"] = "WEB";
//商户号
$newPara["mch_id"] = $this->config['mch_id']; //商户id
//随机字符串,这里推荐使用函数生成
$newPara["nonce_str"] = $this->createNoncestr();
//通知地址,注意,这里的url里面不要加参数
$newPara["notify_url"] = $this->config['notify_url'];//支付成功后的回调地址;
//商户订单号,这里是商户自己的内部的订单号
$newPara["out_trade_no"] = $order_num;
//签名类型 HMAC-SHA256 MD5
$newPara["sign_type"] = "MD5";
//终端IP
$newPara["spbill_create_ip"] = $this->get_client_ip();
//总金额
$newPara["total_fee"] = $price;
//交易类型
$newPara["trade_type"] = "APP";
$key = $this->config['api_key']; //密钥:在商户后台个人安全中心设置
//第一次签名
$newPara["sign"] = $this->appgetSign($newPara,$key);
//echo json_encode($newPara);
//exit;
//把数组转化成xml格式
$xmlData = $this->arrayToXml($newPara);
$get_data = $this->sendPrePayCurl($xmlData);
//返回的结果进行判断。
if($get_data['return_code'] == "SUCCESS" && $get_data['result_code'] == "SUCCESS")
{
//根据微信支付返回的结果进行二次签名
//二次签名所需的随机字符串
$newPara["nonce_str"] = $this->createNoncestr();
//二次签名所需的时间戳
$newPara['timeStamp'] = time()."";
//二次签名剩余参数的补充
$secondSignArray = array(
"appid"=>$newPara['appid'],
"noncestr"=>$newPara['nonce_str'],
"package"=>"Sign=WXPay",
"prepayid"=>$get_data['prepay_id'],
"partnerid"=>$newPara['mch_id'],
"timestamp"=>$newPara['timeStamp'],
);
$json['success'] = 1;
$json['ordersn'] = $newPara["out_trade_no"]; //订单号
$json['order_arr'] = $secondSignArray; //返给前台APP的预支付订单信息
$json['order_arr']['sign'] = $this->appgetSign($secondSignArray,$key); //预支付订单签名
$json['data'] = "预支付完成";
//预支付完成,在下方进行自己内部的业务逻辑
/*****************************/
//return json_encode($json);
$json['order_arr']['nonce_str'] = $secondSignArray['noncestr'];
$json['order_arr']['prepay_id'] = $secondSignArray['prepayid'];
$json['order_arr']['return_code'] = 'SUCCESS';
$json['order_arr']['return_msg'] = 'OK';
$test['pay_code'] = $json['order_arr'];
return json_encode($test);
}else{
$json['success'] = 0;
$json['error'] = $get_data['return_msg'];
return json_encode($json);
}
}
//将数组转换为xml格式
public function arrayToXml($arr)
{
$xml = "";
foreach ($arr as $key=>$val)
{
//if (is_numeric($val))
if(false)
{
$xml.="".$val."".$key.">";
}
else
$xml.="".$key.">";
}
$xml.="";
return $xml;
}
//发送请求
public function sendPrePayCurl($xml,$second=30)
{
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
$data_xml_arr =$this->XMLDataParse($data);
if($data_xml_arr)
{
return $data_xml_arr;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."
";
//echo "错误原因查询";
curl_close($ch);
return false;
}
}
//xml格式数据解析函数
public function XMLDataParse($data)
{
$xml = simplexml_load_string($data,NULL,LIBXML_NOCDATA);
$array = json_decode(json_encode($xml),true);
return $array;
}
//随机字符串
public function createNoncestr( $length = 32 )
{
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str ="";
for ( $i = 0; $i
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/*
* 格式化参数格式化成url参数 生成签名sign
*/
public function appgetSign($Obj,$appwxpay_key)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'';
//签名步骤二:在string后加入KEY
if($appwxpay_key){
$String = $String."&key=".$appwxpay_key;
}
//echo "【string2】".$String."";
//签名步骤三:MD5加密
$String = md5($String);
//HMAC-SHA256签名方式
//$String = hash_hmac("sha256",$String,$appwxpay_key);
//echo "【string3】 ".$String."";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."";
return $result_;
}
//按字典序排序参数
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
//获取当前服务器的IP
public function get_client_ip()
{
if ($_SERVER['REMOTE_ADDR']) {
$cip = $_SERVER['REMOTE_ADDR'];
} elseif (getenv("REMOTE_ADDR")) {
$cip = getenv("REMOTE_ADDR");
} elseif (getenv("HTTP_CLIENT_IP")) {
$cip = getenv("HTTP_CLIENT_IP");
} else {
$cip = "unknown";
}
return $cip;
}
//签名验证
function getVerifySign($data, $key)
{
$String = $this->formatBizQueryParaMap($data, false);
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $key;
//签名步骤三:MD5加密
$String = md5($String);
//签名步骤四:所有字符转为大写
$result = strtoupper($String);
return $result;
}
/**
* 异步通知信息验证
* @return boolean|mixed
*/
public function verifyNotify()
{
$xml = file_get_contents('php://input');
if(!$xml){
return false;
}
$wx_back = $this->XMLDataParse($xml);
if(empty($wx_back)){
return false;
}
$checkSign = $this->getVerifySign($wx_back, $this->config['api_key']);
if($checkSign=$wx_back['sign']){
return $wx_back;
}else{
return false;
}
}
}