好久没有更新博客,最近做项目遇到了微信支付,整理一下需求,目的一个活动页面需要调微信支付!
应用框架THINKPHP5.1
注意:微信支付 需要注意 如果是h6页面调取支付的话,需要静默一个登录状态获取code,这样才可以保证支付调取的参数openid正常,这里的坑填了好久,微信内浏览器h6用户输入完手机号登录后,页面存cookie信息,有时候会不更新获取的openid 导致“下单账号和支付账号不一致,请核对”;
微信内浏览器刷新页面用jq执行: window.location.href = location.href+'?time='+((new Date()).getTime());
直接上代码:前端
function get_form(){
var price1 =$(".get_price1").val();
var price2 =$(".get_price2").val();
var price3 =$(".get_price3").val();
var price4 =$(".get_price4").val();
var pid =$(".pid").val();
var username =$(".username").val();
var mobile =$(".mobile").val();
var address =$(".address").val();
if(!username && !mobile && !address){
layer.alert('收货人信息不能为空');
return false;
}
$.post("home/get_from",//get_from 是后台请求支付的逻辑
{"price1":price1,"price2":price2,"price3":price3,"price4":price4,"username":username,"mobile":mobile,"address":address,"pid":pid},function(apiConfig){
console.log(apiConfig);
var ua = navigator.userAgent.toLowerCase();//获取判断用的对象是微信内还是外部
if (ua.match(/MicroMessenger/i) == "micromessenger") {
//内部
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":apiConfig.appId, //公众号名称,由商户传入
"timeStamp":apiConfig.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr":apiConfig.nonceStr, //随机串
"package":apiConfig.package,
"signType":apiConfig.signType, //微信签名方式:
"paySign":apiConfig.paySign //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ){
window.location.href ="方法路径"
}
});
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
}else{
//外部
var url="支付成功跳转";
window.location.href =apiConfig.mweb_url+"&redirect_url="+url;
}
});
}
后端代码:
静默登录 请求页面的时候通过tp5 redirect重置路由
再进入页面的方法里 查找有没有openid
$sa = Cookie::get('kj_phone');
$uid =$sa['id'];//用户id
$userAgent = $_SERVER['HTTP_USER_AGENT']; // 判断是 公众号支付还是外部网址支付
if (strpos($userAgent, 'MicroMessenger')) { // 公众号支付
$uid =$sa['id'];//用户id
$open_id= Cache::get($uid);
// print_r($open_id);
if(empty($open_id)){
$this->redirect("https://open.weixin.qq.com/connect/oauth3/authorize?appid=Appid&redirect_uri=页面进入后先通过get_open方法获得code获取openid&response_type=code&scope=snsapi_base&state=1#wechat_redirect");
}
}
get_open方法
public function get_open(){
$code1=empty($_GET['code']) ? '' : $_GET['code'];
$sa =Cookie::get('kj_phone');
// $appId ='appId ';
//$secret ='secret';
$get_token_url = 'https://api.weixin.qq.com/sns/oauth3/access_token?appid=Appid&secret=secre&code=' . $code1 . '&grant_type=authorization_code';
$json_obj = $this->http_request($get_token_url);
$json_obj = json_decode($json_obj,true);
$openid = empty($json_obj['openid']) ? "" : $json_obj['openid'];
$uid =$sa['id'];//用户id
Cache::set($uid,$openid,3600);
if($code1 == ''){
$this->redirect('重定向到渲染页面');
}
if(!empty($open_id)){
$this->redirect('重定向到渲染页面');
}else{
$this->redirect('重定向到渲染页面');
}
}
get_from:
public function get_from(){
if(request()->isPost()){
$data =input("post.");// 前端传过来的数据 自己写逻辑吧
$userAgent = $_SERVER['HTTP_USER_AGENT']; // 判断是 公众号支付还是外部网址支付
if (strpos($userAgent, 'MicroMessenger')) { // 公众号支付
$sa = Cookie::get('kj_phone');
$uid =$sa['id'];//用户id
$open_id= Cache::get($uid);
//print_r($open_id);exit;
$return = $this->weixinapp($open_id,$body,$order_id,$price,$attact);
return $return;
}else{
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$parameters = array(
'appid' => "小程序ID", //小程序ID
'mch_id' => "商户号", //商户号
'attach'=>$attact,
'nonce_str' => $this->createNoncestr(), //随机字符串
'body' =>$body, //商品描述
'out_trade_no' =>$order_id,//商户订单号
'total_fee' => $price * 100, //总金额 单位 分
'spbill_create_ip' => '111111', //终端IP
'notify_url' => '回调地址', //通知地址 确保外网能正常访问
'trade_type' => 'MWEB'//交易类型
);
//统一下单签名
$parameters['sign'] = $this->getSign($parameters);
$xmlData = $this->arrayToXml($parameters);
$return = $this->xmlToArray($this->postXmlCurl($xmlData, $url, 60));
return $return;
}
}
}
}
//统一下单接口
private function weixinapp($openid,$body,$orderId,$price,$attact) {
$unifiedorder = $this->unifiedorder($openid,$body,$orderId,$price,$attact);
$parameters = array(
'appId' => "appId", //小程序ID
'timeStamp' => '' . time() . '', //时间戳
'nonceStr' => $this->createNoncestr(), //随机串
'package' => 'prepay_id=' . $unifiedorder['prepay_id'], //数据包
'signType' => 'MD5'//签名方式
);
//签名
$parameters['paySign'] = $this->getSign($parameters);
return $parameters;
}
//作用:产生随机字符串,不长于32位
private function createNoncestr($length = 32) {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
private function getSign($Obj) {
//halt($Obj);
foreach ($Obj as $k => $v) {
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//签名步骤二:在string后加入KEY
$String = $String . "&key=d18e10ad06222c972499296f1cefa481" ;
//签名步骤三:MD5加密
$String = md5($String);
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
return $result_;
}
///作用:格式化参数,签名过程需要使用
private function formatBizQueryParaMap($paraMap, $urlencode) {
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if ($urlencode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
$reqPar="";
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
//数组转换成xml
private function arrayToXml($arr) {
$xml = "<root>";
foreach ($arr as $key => $val) {
if (is_array($val)) {
$xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">";
} else {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
}
}
$xml .= "</root>";
return $xml;
}
//xml转换成数组
private function xmlToArray($xml) {
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$val = json_decode(json_encode($xmlstring), true);
return $val;
}
function http_curl($url){//用curl传参
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);//关闭ssl验证
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch,CURLOPT_HEADER, 0);
$output = curl_exec($ch);
curl_close($ch);
return json_decode($output, true);
}
private static function postXmlCurl($xml, $url, $second = 30)
{
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
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_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
curl_setopt($ch, CURLOPT_TIMEOUT, 40);
set_time_limit(0);
//运行curl
$data = curl_exec($ch);
//返回结果
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
throw new WxPayException("curl出错,错误码:$error");
}
}
//回调
private function unifiedorder($openid,$body,$orderId,$price,$attact) {
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$parameters = array(
'appid' => "appid", //小程序ID
'mch_id' =>"mch_id", //商户号
'attach'=>$attact,
'nonce_str' => $this->createNoncestr(), //随机字符串
'body' => $body,
'out_trade_no'=> $orderId,
'total_fee' => $price * 100,
'spbill_create_ip' => '11111111', //终端IP
'notify_url' => 回调, //通知地址 确保外网能正常访问
'openid' => $openid, //用户id
'trade_type' => 'JSAPI'//交易类型
);
//统一下单签名
$parameters['sign'] = $this->getSign($parameters);
$xmlData = $this->arrayToXml($parameters);
$return = $this->xmlToArray($this->postXmlCurl($xmlData, $url, 60));
return $return;
}
//回调
public function callback(){
$xml = file_get_contents("php://input");
// 将XML 转换为数组
$xml = json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA));
$result = json_decode($xml, true);
//如果成功返回
if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
$where['order_id']=$result['out_trade_no'];
$data_e['states']=2;
$ud= db('hc_pr_order')->where($where)->update($data_e);
}
// 给微信的返回值
if ($ud) {
$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}else{
$str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
}
return $str;
return $result;
}