TP5微信公众号H5支付极简攻略

1.背景

每次写到微信支付这块的时候总觉得不够清晰,网上代码大部分太长,依赖太多,还是坚持我的原则,给人复制就尽量少一些依赖,方便配置和理解,高手看懂了自然回去拆分。

2.前置条件

已认证的服务号
公众号关联微信商户
微信商户中添加支付链接域名
下载证书文件到跟目录下新建‘cert’文件夹
以上部分都有清晰的教程,此处不做赘述,聚焦在代码和常见问题上

3.代码

3.1 前端

前端共三个function,比较好理解就不合并了

function pay(amount) {
	var that = this;
	axios.get('/index/test/wxpay?amount=' + amount + "&type=1&openid=" + openid)
		.then(res => {
			payInfo = res.data;
			console.log(res.data)
			apply();
		})
		.catch(err => {
			console.log(err)
		});
}

function jsApiCall() {
	WeixinJSBridge.invoke(
		'getBrandWCPayRequest', {
			"appId": payInfo['appId'], //公众号名称,由商户传入     
			"timeStamp": payInfo['timeStamp'], //时间戳,自1970年以来的秒数     
			"nonceStr": payInfo['nonceStr'], //随机串     
			"package": payInfo['package'],
			"signType": payInfo['signType'], //微信签名方式:     
			"paySign": payInfo['sign'] //微信签名 
		},
		function(res) {
			if (res.err_msg == "get_brand_wcpay_request:ok") {
				// 使用以上方式判断前端返回,微信团队郑重提示:
				//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
			}
		});
}

function apply() {
	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();
	}
}

3.2后端

后端相对有点长,但是不需要动脑子,直接在controller中粘贴进去就可以用了

private $config=[
    'appId'=>'<公众号APPID>', 
    'mch_id'=>'<微信商户号ID>',
    'key'=>'<微信商户KEY>',
    'secret'=>'<微信商户SECRET>'
  ];
  public function wxpay(){
    $desc="描述";
    $openid=input('openid');
    header('Content-Type: text/html; charset=utf-8');
      $total_amount =input('param.amount');// (100) * $amount;//充值的金额
      $data=array(
      'appid'=> $this->config['appId'],//商户账号appid
      'mch_id'=> $this->config['mch_id'],//商户号
      'nonce_str'=> $this->createNoncestr(),//随机字符串
      'out_trade_no'=> date('YmdHis').rand(1000, 9999),//商户订单号
      'openid'=> $openid,//用户openid
      'total_fee'=>$total_amount,//金额
      'body'=> $desc,//充值描述信息
      'spbill_create_ip'=> '47.97.231.53',//Ip地址
      'trade_type'=>'JSAPI',
      'notify_url'=>'<你的回调地址>'//你的回调地址
    );
      $secrect_key = $this->config['key'];//API密码
      $data = array_filter($data);//过滤函数
      ksort($data);
      $str ='';
      foreach($data as $k=>$v) {
        $str.=$k.'='.$v.'&';
      }
      $str.='key='.$secrect_key;//把秘钥和字符串拼接起来
      $data['sign'] = strtoupper(md5($str));//用md5得到sign 转换成大写
      $xml = $this->arraytoxml($data);//拼接成xml的格式
      $url='https://api.mch.weixin.qq.com/pay/unifiedorder'; //调用接口
      $res = $this->wx_curl($xml,$url);
      $return = $this->xmltoarray($res);
      $responseObj = simplexml_load_string($res, 'SimpleXMLElement', LIBXML_NOCDATA);
      $jsonStr = json_encode($responseObj);//转换为字符串
      $jsonArray = json_decode($jsonStr,true);//转换为数组
      if($jsonArray['return_code']='SUCCESS' && $jsonArray['result_code']='SUCCESS'){
        $time = time();//时间戳
        $info = array(
          'appId'=>$this->config['appId'],
          'nonceStr'=>$jsonArray['nonce_str'],
          'package'=>'prepay_id='.$jsonArray['prepay_id'],
          'signType'=>'MD5',
          'timeStamp'=>"".$time."",
        );
      $info_s = array_filter($info);//过滤函数
      ksort($info_s);
      $str_s ='';
      foreach($info as $k=>$v) {
        $str_s.=$k.'='.$v.'&';
      }
      $str_s.='key='.$this->config['key'];//把秘钥和字符串拼接起来
      $info['sign']=strtoupper(md5($str_s));
      $infos = array(
        'status'=>1,
        'content'=>'success',
        'result'=>$info
      );
      return json($info);     
    }else{
      return json([
        'status'=>0,
        'content'=>$jsonArray['return_msg'],
        'result'=>$jsonArray['err_code_des']
      ]);
    }
  }
  public function sendMoney($amount,$re_openid,$desc='你的简介',$check_name=''){
    header('Content-Type: text/html; charset=utf-8');
      $total_amount =$amount;// (100) * $amount;//充值的金额
      $data=array(
      'appid'=> $this->config['appId'],//商户账号appid
      'mch_id'=> $this->config['mch_id'],//商户号
      'nonce_str'=> $this->createNoncestr(),//随机字符串
      'out_trade_no'=> date('YmdHis').rand(1000, 9999),//商户订单号
      'openid'=> $re_openid,//用户openid
      'total_fee'=>$total_amount,//金额
      'body'=> $desc,//充值描述信息
      'spbill_create_ip'=> '47.97.231.53',//Ip地址
      'trade_type'=>'JSAPI',
      'notify_url'=>'<你的回调地址>'//你的回调地址
    );
      $secrect_key = $this->config['key'];//API密码
      $data = array_filter($data);//过滤函数
      ksort($data);
      $str ='';
      foreach($data as $k=>$v) {
        $str.=$k.'='.$v.'&';
      }
      $str.='key='.$secrect_key;//把秘钥和字符串拼接起来
      $data['sign'] = strtoupper(md5($str));//用md5得到sign 转换成大写
      $xml = $this->arraytoxml($data);//拼接成xml的格式
      $url='https://api.mch.weixin.qq.com/pay/unifiedorder'; //调用接口
      $res = $this->wx_curl($xml,$url);
      $return = $this->xmltoarray($res);
      $responseObj = simplexml_load_string($res, 'SimpleXMLElement', LIBXML_NOCDATA);
      $jsonStr = json_encode($responseObj);//转换为字符串
      $jsonArray = json_decode($jsonStr,true);//转换为数组
      if($jsonArray['return_code']='SUCCESS' && $jsonArray['result_code']='SUCCESS'){
        $time = time();//时间戳
        $info = array(
          'appId'=>$this->config['appId'],
          'nonceStr'=>$jsonArray['nonce_str'],
          'package'=>'prepay_id='.$jsonArray['prepay_id'],
          'signType'=>'MD5',
          'timeStamp'=>"".$time."",
        );
      $info_s = array_filter($info);//过滤函数
      ksort($info_s);
      $str_s ='';
      foreach($info as $k=>$v) {
        $str_s.=$k.'='.$v.'&';
      }
      $str_s.='key='.$this->config['key'];//把秘钥和字符串拼接起来
      $info['sign']=strtoupper(md5($str_s));
      $infos = array(
        'status'=>1,
        'content'=>'success',
        'result'=>$info
      );
      return json($info);
    }else{
      return json([
        'status'=>0,
        'content'=>$jsonArray['return_msg'],
        'result'=>$jsonArray['err_code_des']
      ]);
    }
  }

  public 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;
  }
  public function arraytoxml($data){
    $str='<xml>';
    foreach($data as $k=>$v) {
        $str.='<'.$k.'>'.$v.'</'.$k.'>';
    }
    $str.='</xml>';
    return $str;
  }
  public function xmltoarray($xml) {
    libxml_disable_entity_loader(true);//禁止引用外部xml实体
    $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
    $val = json_decode(json_encode($xml),true);
    return $val;
  }
  public function wx_curl($vars,$url,$second = 30, $aHeader = array()) {
    $isdir = ROOT_PATH."cert/";//证书位置
    $ch = curl_init();//初始化curl
    curl_setopt($ch, CURLOPT_TIMEOUT, $second);//设置执行最长秒数
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
    curl_setopt($ch, CURLOPT_URL, $url);//抓取指定网页
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);// 终止从服务端进行验证
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//
    curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');//证书类型
    curl_setopt($ch, CURLOPT_SSLCERT, $isdir . 'apiclient_cert.pem');//证书位置
    curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中规定的私钥的加密类型
    curl_setopt($ch, CURLOPT_SSLKEY, $isdir . 'apiclient_key.pem');//证书位置
    if (count($aHeader) >= 1) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);//设置头部
    }
    curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
    curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);//全部数据使用HTTP协议中的"POST"操作来发送
 
    $data = curl_exec($ch);//执行回话
    if ($data) {
        curl_close($ch);
        return $data;
    } else {
        $error = curl_errno($ch);
        echo "call faild, errorCode:$error\n";
        curl_close($ch);
        return false;
    }
  }

4常见问题

4.1 “chooseWXPay:fail, the permission value is offline verifying”

微信开发者工具打开时控制台报这个错,不用担心,真机打开就可以了。

4.2 “公众号支付 当前页面url未注册”

在微信商户中添加支付链接域名就可以了,具体路径:
登录微信支付商户平台–>【产品中心】–>【开发配置】

4.3 “调用支付jsapi缺少参数 appid”

大概率是因为没有返回正确的支付数据,如果按我上面提供的代码一般不会有其他问题,只要查看config中的各项设置就可以了,常见问题是appid不一致,微信商户未绑定等问题,可以在wxpay()加断点进行排查

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值