先说一下我踩的坑,config 签名中的这个字段 nonceStr,请记住,必须是这样大小写,微信卡券签名中的这个字段 nonce_str,请记住,必须是下划线分割,然后下面介绍方法

一、Config签名,需要的是 jsapi_ticket

获取微信 jsapi_ticket 方法

/**
 * 获取微信 jsapi_ticket
 */
public function getJsTicket() {
    $redis_key = 'JsApiTicket:' . $this->appid;
    
    $ticket = Redis::get($redis_key);
    if ($ticket) {
        return $ticket;
    }
    
    $url = self::API_URL_PREFIX . '/ticket/getticket?access_token=' . $this->AccessToken . '&type=jsapi';
    
    $json = json_decode($this->httpGet($url), true);
    
    if ($json['errcode'] != 0) {
        return false;
    }
    
    Redis::setex($redis_key, $json['expires_in'] - 60, $json['ticket']);
    
    return $json['ticket'];
}

进行签名方法

/**
 * 获取微信JS-SDK使用权限签名
 *
 */
public function jsConfigSign($url) {
    $noncestr = str_random();
    $jsapi_ticket = $this->getJsTicket();
    $timestamp = time();
    
    $data = [
        'noncestr'     => $noncestr,
        'jsapi_ticket' => $jsapi_ticket,
        'timestamp'    => $timestamp,
        'url'          => $url,
    ];
    
    // 按照键名排序
    ksort($data);
    
    // 拼接成字符串,http_build_query 这个函数会自动进行 urlencode
    $string = '';
    foreach ($data as $k => $v) {
        $string .= $k . '=' . $v . '&';
    }
    
    // 去掉最末尾的 & 符号
    $string = substr($string, 0, -1);
    
    $signature = sha1($string);
    
    $data['sign'] = $signature;
    
    Log::info('jsConfigSign', ['data' => $data]);
    
    return $data;
}

二、微信卡券签名,需要的是卡券的 api_ticket

获取 api_ticket

/**
 * 获取微信卡券的 api_ticket,请记住这个和 jsapi_ticket 不同
 */
public function getCardTicket() {
    $redis_key = 'cardTicket:' . $this->appid;
    
    $cardTicket = Redis::get($redis_key);
    if ($cardTicket) {
        return $cardTicket;
    }
    
    $url = self::API_URL_PREFIX . '/ticket/getticket?access_token=' . $this->AccessToken . '&type=wx_card';
    
    $json = json_decode($this->httpGet($url), true);
    
    if ($json['errcode'] != 0) {
        return false;
    }
    
    Redis::setex($redis_key, $json['expires_in'] - 60, $json['ticket']);
    
    return $json['ticket'];
}

获取卡券签名

/**
 * 获取微信卡券签名
 *
 * @param $arrdata
 * @param string $method
 *
 * @return bool|array
 */
public function getTicketSignature($arrdata, $method = 'sha1') {
    if (!function_exists($method)) return false;
    $newArray = [];
    
    foreach ($arrdata as $key => $value) {
        array_push($newArray, (string)$value);
    }
    sort($newArray, SORT_STRING);
    return $method(implode($newArray));
}

批量下发卡券

// 批量添加卡券
public function addCardList(array $cards) {
    $api_ticket = $this->getCardTicket();
    
    $cardData = [];
    $time = time();
    foreach ($cards as $card_id) {
        $cardParams = [];
        $cardParams['api_ticket'] = $api_ticket;
        $cardParams['card_id'] = $card_id;
        $cardParams['timestamp'] = (string)$time;
        $cardParams['nonce_str'] = str_random(16);
        $cardParams['signature'] = $this->getTicketSignature($cardParams);
        $cardData[] = ['cardId' => $card_id, 'cardExt' => json_encode($cardParams)];
    }
    
    return $cardData;
}

三、前端代码

<!DOCTYPE html>
<html>
<head>
    <title>微信卡券</title>
    <meta charset="utf-8">
</head>
<body>

<h1 style="font-size: 10em">舒孝元</h1>

</body>

<script typet="text/javascript" src="https://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="https://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>

<script>
    $.ajax({
        type: 'POST',
        url: '/wx/card/sign',
        data: {
            'url': '{{ url()->current() }}',
            'cards': 'pA8P1w_Mjjiq_VRJlNnVfFjd7sjg,pA8P1w69e0_8BGxGsJg4dJoC1tAI,pA8P1wx9jFYi9y4XFRDBKCkSzmNA',
            '_token': "{{ csrf_token() }}",
        },
        dataType: "json",
        success: function (ret) {
            if (ret.code != 0) {
                alert(ret.msg);
                return false;
            }
            console.log(ret);
            data1 = ret.data;
            test(data1);
        },
        error: function (err) {
            alert('请求失败');
            return false;
        }
    });


    function test(data) {
        wx.config({
            debug: true,
            appId: 'wx4827e89d14f33911',
            timestamp: data.config.timestamp,
            nonceStr: data.config.noncestr,
            signature: data.config.sign,
            jsApiList: [
                // 所有要调用的 API 都要加到这个列表中
                'updateAppMessageShareData',
                'updateTimelineShareData',
                'onMenuShareTimeline',
                'onMenuShareAppMessage',
                'onMenuShareQQ',
                'onMenuShareWeibo',
                'onMenuShareQZone',
                'startRecord',
                'stopRecord',
                'onVoiceRecordEnd',
                'playVoice',
                'pauseVoice',
                'stopVoice',
                'onVoicePlayEnd',
                'uploadVoice',
                'downloadVoice',
                'chooseImage',
                'previewImage',
                'uploadImage',
                'downloadImage',
                'translateVoice',
                'getNetworkType',
                'openLocation',
                'getLocation',
                'hideOptionMenu',
                'showOptionMenu',
                'hideMenuItems',
                'showMenuItems',
                'hideAllNonBaseMenuItem',
                'showAllNonBaseMenuItem',
                'closeWindow',
                'scanQRCode',
                'chooseWXPay',
                'openProductSpecificView',
                'addCard',
                'chooseCard',
                'openCard'
            ]
        });

        wx.ready(function () {
            // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
            // console.log(1);
            // console.log(data.cardData);
            // console.log(2);
            // console.log(data.cardData);
            // cardData = JSON.stringify(data.cardData);
            // 拉取适用卡券列表并获取用户选择信息
            wx.addCard({
                // 需要添加的卡券列表
                cardList: data.cardData,
                success: function (res) {
                    // 添加的卡券列表信息
                    var cardList = res.cardList;
                    console.log(res.cardList);
                },
                error: function (res) {
                    alert('批量添加卡券失败');
                }
            });
        });

        wx.error(function (res) {
            // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
            alert('config 信息验证失败');
        });
    }

</script>
</html>