前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微信公众号支付,JSAPI支付方法,ThinkPHP5+微信支付

微信公众号支付,JSAPI支付方法,ThinkPHP5+微信支付

作者头像
用户5745385
发布2020-05-09 15:25:42
2.4K0
发布2020-05-09 15:25:42
举报
文章被收录于专栏:XSYMambaXSYMamba

总结:开发微信公众号,接入微信支付功能,附上微信支付API接口的实现逻辑图以及相关代码。JSAPI支付:是指在微信内置浏览器内调用微信支付模块支付,比如可用于微信公众号内的微信商城之类的。

首先得在微信公众号的公众号设置里,把微信支付的授权目录填上

然后你还得在商户号里,开通微信JSAPI支付的功能

然后这里的授权目录也得填上

然后按照微信文档的时序图,大概分3步

项目结构:

配置文件:

Jsapi.php代码

<?php

namespace app\index\controller;

use app\common\controller\HomeBase;
use tools\WxPay;
use think\Config;
use phpqrcode\ApiQrcode;
use think\Request;
use think\Cache;
use tools\RetJosn;

/**
 * JSAPI支付DEMO
 * Class Index
 * @package app\index\controller
 */
class Jsapi extends HomeBase {

    /**
     * 首页
     */
    public function index() {
        $redirect_uri = urlencode('http://wxpay.ngrok2.xiaomiqiu.cn'.url('Jsapi/index2'));
        $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='.Config::get('wx_pay')['appid'].'&redirect_uri='.$redirect_uri.'&response_type=code&scope=snsapi_base&state=123#wechat_redirect';
        $this->redirect($url);
    }


    /**
     * 授权后跳转到此
     */
    public function index2(Request $request){
        $arr = $request->get();
        $code = $arr['code'];
        $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='.Config::get('wx_pay')['appid'].'&secret='.Config::get('wx_pay')['secret'].'&code='.$code.'&grant_type=authorization_code';
        $ret_json = file_get_contents("$url");
        $ret_arr = json_decode($ret_json,true);
        $openid = $ret_arr['openid'];
        $this->assign('openid',$openid);
        return $this->fetch();
    }


    /**
     * 下单获取支付参数
     * @param Request $request
     * @param WxPay $wxpay
     * @return \think\response\Json
     */
    public function getUrl(Request $request,WxPay $wxpay) {
        $pid = $request->get('id');
        $openid = $request->get('openid');
        //调用统一下单API
        $params = [
            'appid' => Config::get('wx_pay')['appid'],
            'mch_id' => Config::get('wx_pay')['mchid'],
            'nonce_str' => md5(time()),
            'body' => '订单号:'.$pid,
            'out_trade_no' => $pid,
            'total_fee' => 2,
            'spbill_create_ip' => $_SERVER['SERVER_ADDR'],
            'notify_url' => Config::get('wx_pay')['notify'],
            'trade_type' => 'JSAPI',
            'product_id' => $pid,
            'openid' => $openid
        ];
        $arr = $wxpay->unifiedorder($params);
        $wxpay->logs('logs.txt',$arr);

        if (isset($arr['prepay_id'])) {
            //重新签名
            $data = [
                'appId' => $arr['appid'],
                'timeStamp' => time(),
                'nonceStr' => md5(time()),
                'package' => 'prepay_id='.$arr['prepay_id'],
                'signType' => 'MD5'
            ];
            $data = $wxpay->setSign($data);
            $data['paySign'] = $data['sign'];
            unset($data['sign']);
            Cache::set('send' . $params['out_trade_no'], $params['total_fee'], 3600);
            return RetJosn::successJson('OK',$data);
        } else {
            return RetJosn::errorJson('err');
        }
    }

    /**
     * 接收腾讯推送支付通知
     * @param WxPay $wxpay
     */
    public function backOrder(WxPay $wxpay) {
        try {
            // 获取腾讯传回来的通知数据
            $xml = $wxpay->getPost();
            // 将XML格式的数据转换为数组
            $arr = $wxpay->XmlToArr($xml);
            $wxpay->logs('logs.txt', '1');
            // 验证签名
            if ($wxpay->checkSign($arr)) {
                Cache::set('back'.$arr['out_trade_no'],$arr['total_fee'],3600);
            }
            $wxpay->logs('logs.txt', '2');
            $params = [
                'return_code' => 'SUCCESS',
                'return_msg' => 'OK'
            ];
            echo $wxpay->ArrToXml($params);
        } catch (\Exception $e) {
            $wxpay->logs('logs.txt', $e->getMessage());
            exit();
        }
    }

    /**
     * 查询支付状态
     * @param Request $request
     * @return type
     */
    public function checkSuccess(Request $request)
{
        $pid = $request->get('id');
        if (Cache::get('send' . $pid) == Cache::get('back' . $pid)) {
            return RetJosn::successJson('支付成功');
        } else {
            return RetJosn::errorJson(Cache::get('send' . $pid) . '|' . Cache::get('back' . $pid));
        }
    }
}


微信支付类:
<?php

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

namespace tools;
use think\Config;
/**
 * Description of WxPay
 *
 * @author admin
 */
class WxPay {

    /**
     * 获取签名
     * @param type $arr
     * @return type
     */
    public function getSign($arr)
{
        //去除数组的空值
        array_filter($arr);
        if(isset($arr['sign'])){
            unset($arr['sign']);
        }
        //排序
        ksort($arr);
        //组装字符
        $str = $this->arrToUrl($arr) . '&key=' . Config::get('wx_pay')['key'];
        //使用md5 加密 转换成大写
       return strtoupper(md5($str));
    }

    /**
     * 校验签名
     * @param type $arr
     * @return boolean
     */
    public function checkSign($arr){
        //生成新签名
        $sign = $this->getSign($arr);
        //和数组中原始签名比较
        if($sign == $arr['sign']){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 获取带签名的数组
     * @param array $arr
     * @return type
     */
    public function setSign($arr)
{
        $arr['sign'] = $this->getSign($arr);
        return $arr;
    }

    /**
     * 数组转URL字符串 不带key
     * @param type $arr
     * @return type
     */
    public function arrToUrl($arr)
{
        return urldecode(http_build_query($arr));
    }

    /**
     * 记录到文件
     * @param type $file
     * @param type $data
     */
    public function logs($file,$data)
{
        $data = is_array($data) ? print_r($data,true) : $data;
        file_put_contents('./public/paylogs/' .$file, $data);
    }
    /**
     * 接收POST推送
     * @return type
     */
    public function getPost()
{
        return file_get_contents('php://input');
    }

    /**
     * Xml 文件转数组
     * @param type $xml
     * @return string
     */
    public function XmlToArr($xml)
{
        if($xml == '') return '';
        libxml_disable_entity_loader(true);
        $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $arr;
    }

    /**
     * 数组转XML
     * @param type $arr
     * @return string
     */
    public function ArrToXml($arr)
{
        if(!is_array($arr) || count($arr) == 0) return '';

        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
                if (is_numeric($val)){
                        $xml.="<".$key.">".$val."</".$key.">";
                }else{
                        $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
                }
        }
        $xml.="</xml>";
        return $xml;
    }

    /**
     * 发送POST请求
     * @param type $url
     * @param type $postfields
     * @return type
     */
    public function postStr($url,$postfields)
{
        $ch = curl_init();
        $params[CURLOPT_URL] = $url;    //请求url地址
        $params[CURLOPT_HEADER] = false; //是否返回响应头信息
        $params[CURLOPT_RETURNTRANSFER] = true; //是否将结果返回
        $params[CURLOPT_FOLLOWLOCATION] = true; //是否重定向
        $params[CURLOPT_POST] = true;
        $params[CURLOPT_SSL_VERIFYPEER] = false;//禁用证书校验
        $params[CURLOPT_SSL_VERIFYHOST] = false;
        $params[CURLOPT_POSTFIELDS] = $postfields;
        curl_setopt_array($ch, $params); //传入curl参数
        $content = curl_exec($ch); //执行
        curl_close($ch); //关闭连接
        return $content;
    }

    /**
     * 统一下单
     * @param type $params
     * @return boolean
     */
    public function unifiedorder($params)
{
        //获取到带签名的数组
        $params = $this->setSign($params);
        //数组转xml
        $xml = $this->ArrToXml($params);
        //发送数据到统一下单API地址
        $data = $this->postStr(Config::get('wx_pay')['uourl'], $xml);
        $arr = $this->XmlToArr($data);
        if($arr['result_code'] == 'SUCCESS' && $arr['return_code'] == 'SUCCESS'){
            return $arr;
        }else{
            $this->logs('error.txt', $data);
            return false;
        }
    }
}


前端页面:
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1" />
    </head>
    <body>
        <h1 id="openid"></h1>
        <div class="btn-box">
            <button onclick="getUrl()">下单</button>
        </div>
        <div id="info">

        </div>
    </body>
    <script src="__STATIC__/default/js/jquery-1.9.1.min.js"></script>
    <script>
        var openid = '';
        $(function () {
            localStorage.getItem('openid');
            if (localStorage.getItem('openid')) {
                openid = localStorage.getItem('openid');
            } else {
                openid = '{$openid}';
                localStorage.setItem('opendi',openid);
            }
            $('#openid').html(openid);
        });

        /*验证码生成*/
        function getUrl() {
            var id = getId(10);
            $.ajax({
                url:"{:url('Jsapi/getUrl')}?id=" + id + '&openid=' + openid,
                dataType:'json',
                success:function(res) {
                    if (res.code == 200) {
                        alert('1'+JSON.stringify(res));
                        var data = res.data;
                        wakeup(res.data);
                        checkSuccess(id);
                    }
                }
            });
        }

        function wakeup(data){
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest', data,
                function(res){
                    if(res.err_msg == "get_brand_wcpay_request:ok" ){
                        // 使用以上方式判断前端返回,微信团队郑重提示:
                        //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                        alert('支付成功');
                    }
                });
        }


        /*生成唯一Id*/
        function getId(length){
            var tmp = Date.parse( new Date() ).toString();
            tmp = tmp.substr(0,length);
            return tmp;
        }

        /*轮询支付状态*/
        function checkSuccess(id){
            var interval = window.setInterval(function(){
                $.ajax({
                   url:"{:url('Jsapi/checkSuccess')}?id=" + id,
                   dataType:'json',
                   success:function(res) {
                       if (res.code == 200) {
                           $('#info').html('订单号:'+id+','+res.msg);
                           clearInterval(interval);
                       }
                   }
                })
            },2000)
        }
    </script>
</html>

效果演示:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 XSYMamba 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 总结:开发微信公众号,接入微信支付功能,附上微信支付API接口的实现逻辑图以及相关代码。JSAPI支付:是指在微信内置浏览器内调用微信支付模块支付,比如可用于微信公众号内的微信商城之类的。
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档