1. 程式人生 > >微信支付開發(7) 收貨地址共享介面V2

微信支付開發(7) 收貨地址共享介面V2

在這篇微信公眾平臺開發教程中,我們將介紹如何在網頁中實現獲取收貨地址的功能。

收貨地址共享介面 在2016年4月13日 進行過升級,2016年5月20日之後只能使用新介面,本教程為新版介面的教程!

本文分為以下二個部分:

  1. 生成JS-SDK許可權驗證簽名
  2. 實現獲取共享收貨地址


一、微信JS-SDK

1. 獲得Access Token

access token的獲得方法在前面有介紹,詳情見 微信公眾平臺開發(26) ACCESS TOKEN

2. 獲取jsapi_ticket

生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號用於呼叫微信JS介面的臨時票據。正常情況下,jsapi_ticket的有效期為7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api呼叫次數非常有限,頻繁重新整理jsapi_ticket會導致api呼叫受限,影響自身業務,開發者必須在自己的服務全域性快取jsapi_ticket 。

參考以下文件獲取access_token(有效期7200秒,開發者必須在自己的服務全域性快取access_token):
用第一步拿到的access_token 採用http GET方式請求獲得jsapi_ticket(有效期7200秒,開發者必須在自己的服務全域性快取jsapi_ticket),介面地址如下

https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

成功返回如下JSON:

{
    "errcode":0,
    "errmsg":"ok",
    
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA", "expires_in":7200 }

獲得jsapi_ticket之後,就可以生成JS-SDK許可權驗證的簽名了。

3. 簽名演算法實現

簽名生成規則如下:參與簽名的欄位包括noncestr(隨機字串), 有效的jsapi_ticket, timestamp(時間戳), url(當前網頁的URL,不包含#及其後面部分) 。對所有待簽名引數按照欄位名的ASCII 碼從小到大排序(字典序)後,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字串string1。這裡需要注意的是所有引數名均為小寫字元。對string1作sha1加密,欄位名和欄位值都採用原始值,不進行URL 轉義。

即signature=sha1(string1)。 示例:

noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value

步驟1. 對所有待簽名引數按照欄位名的ASCII 碼從小到大排序(字典序)後,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字串string1:

jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value

步驟2. 對string1進行sha1簽名,得到signature:

0f9de62fce790f9a083d5c99e95740ceb90c27ed

完整程式碼如下

<?php
class JSSDK {
  private $appId;
  private $appSecret;

  public function __construct($appId, $appSecret) {
    $this->appId = $appId;
    $this->appSecret = $appSecret;
  }

  public function getSignPackage() {
    $jsapiTicket = $this->getJsApiTicket();

    // 注意 URL 一定要動態獲取,不能 hardcode.
    $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
    $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";

    $timestamp = time();
    $nonceStr = $this->createNonceStr();

    // 這裡引數的順序要按照 key 值 ASCII 碼升序排序
    $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";

    $signature = sha1($string);

    $signPackage = array(
      "appId"     => $this->appId,
      "nonceStr"  => $nonceStr,
      "timestamp" => $timestamp,
      "url"       => $url,
      "signature" => $signature,
      "rawString" => $string
    );
    return $signPackage; 
  }

  private function createNonceStr($length = 16) {
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    $str = "";
    for ($i = 0; $i < $length; $i++) {
      $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
    }
    return $str;
  }

  private function getJsApiTicket() {
    // jsapi_ticket 應該全域性儲存與更新,以下程式碼以寫入到檔案中做示例
    $data = json_decode(file_get_contents("jsapi_ticket.json"));
    if ($data->expire_time < time()) {
      $accessToken = $this->getAccessToken();
      // 如果是企業號用以下 URL 獲取 ticket
      // $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=$accessToken";
      $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken";
      $res = json_decode($this->httpGet($url));
      $ticket = $res->ticket;
      if ($ticket) {
        $data->expire_time = time() + 7000;
        $data->jsapi_ticket = $ticket;
        $fp = fopen("jsapi_ticket.json", "w");
        fwrite($fp, json_encode($data));
        fclose($fp);
      }
    } else {
      $ticket = $data->jsapi_ticket;
    }

    return $ticket;
  }

  private function getAccessToken() {
    // access_token 應該全域性儲存與更新,以下程式碼以寫入到檔案中做示例
    $data = json_decode(file_get_contents("access_token.json"));
    if ($data->expire_time < time()) {
      // 如果是企業號用以下URL獲取access_token
      // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
      $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
      $res = json_decode($this->httpGet($url));
      $access_token = $res->access_token;
      if ($access_token) {
        $data->expire_time = time() + 7000;
        $data->access_token = $access_token;
        $fp = fopen("access_token.json", "w");
        fwrite($fp, json_encode($data));
        fclose($fp);
      }
    } else {
      $access_token = $data->access_token;
    }
    return $access_token;
  }

  private function httpGet($url) {
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_TIMEOUT, 500);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_URL, $url);

    $res = curl_exec($curl);
    curl_close($curl);

    return $res;
  }
}

 

二、收貨地址共享介面

1. 簡介

微信收貨地址共享,是指使用者在微信瀏覽器內開啟網頁,填寫過地址後,後續可以免填寫支援快速選擇,也可增加和編輯。此地址為使用者屬性,可在各商戶的網頁中共享使用。支援原生控制元件填寫地址,地址資料會傳遞到商戶。

地址共享是基於微信JavaScript API 實現,只能在微信內建瀏覽器中使用,其他瀏覽器呼叫無效。同時,需要微信5.0 版本才能支援,建議通過user agent 來確定使用者當前的版本號後再呼叫地址介面。以iPhone 版本為例,可以通過useragent可獲取如下微信版本示例資訊:"Mozilla/5.0(iphone;CPU iphone OS 5_1_1 like Mac OS X)AppleWebKit/534.46(KHTML,like Geocko) Mobile/9B206MicroMessenger/5.0"其中5.0 為使用者安裝的微信版本號,商戶可以判定版本號是否高於或者等於5.0。

地址格式
微信地址共享使用的資料欄位包括:

  • 收貨人姓名
  • 地區,省市區三級
  • 詳細地址
  • 郵編
  • 聯絡電話

其中,地區對應是國標三級地區碼,如“廣東省-廣州市-天河區”,對應的郵編是是510630。詳情參考連結:http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201401/t20140116_501070.html

2. 繫結域名

先登入微信公眾平臺進入“公眾號設定”的“功能設定”裡填寫“JS介面安全域名”。

3. 獲取簽名包

<?php
require_once "jssdk.php";
$jssdk = new JSSDK("yourAppID", "yourAppSecret");
$signPackage = $jssdk->GetSignPackage();
?>

4. 引入JS檔案

在需要呼叫JS介面的頁面引入如下JS檔案:

特別注意:JS-SDK版本需使用http://res.wx.qq.com/open/js/jweixin-1.1.0.js

<script src="http://res.wx.qq.com/open/js/jweixin-1.1.0.js"></script>

5.通過config介面注入許可權驗證配置

所有需要使用JS-SDK的頁面必須先注入配置資訊,否則將無法呼叫。

        <script>
          wx.config({
            debug: false,
            appId: '<?php echo $signPackage["appId"];?>',
            timestamp: <?php echo $signPackage["timestamp"];?>,
            nonceStr: '<?php echo $signPackage["nonceStr"];?>',
            signature: '<?php echo $signPackage["signature"];?>',
            jsApiList: [
              // 所有要呼叫的 API 都要加到這個列表中
                'checkJsApi',
                'openAddress',
              ]
          });
        </script>

6. 通過ready介面處理成功驗證

需要在頁面載入時就呼叫,需要把相關介面放在ready函式中呼叫來確保正確執行

wx.ready(function () {
});

7. 通過checkJsApi判斷當前客戶端版本是否支援分享引數自定義

 wx.checkJsApi({
                jsApiList: [
                    'openAddress',
                ],
                success: function (res) {
                    alert(JSON.stringify(res));
                }
            });  

8. 實現收貨地址共享

            wx.openAddress({
              trigger: function (res) {
                alert('使用者開始拉出地址');
              },
              success: function (res) {
                alert('使用者成功拉出地址');
                alert(JSON.stringify(res));
                document.form1.address1.value         = res.provinceName;
                document.form1.address2.value         = res.cityName;
                document.form1.address3.value         = res.countryName;
                document.form1.detail.value           = res.detailInfo;
                document.form1.national.value         = res.nationalCode;
                document.form1.user.value            = res.userName;
                document.form1.phone.value            = res.telNumber;
                document.form1.postcode.value         = res.postalCode;
                document.form1.errmsg.value         = res.errMsg;
                document.form1.qq.value             = 1354386063;
              },
              cancel: function (res) {
                alert('使用者取消拉出地址');
              },
              fail: function (res) {
                alert(JSON.stringify(res));
              }
            });

返回說明

返回值

說明

errMsg

獲取編輯收貨地址成功返回“openAddress:ok”。

userName

收貨人姓名。

postalCode

郵編。

provinceName

國標收貨地址第一級地址(省)。

cityName

國標收貨地址第二級地址(市)。

countryName

國標收貨地址第三級地址(國家)。

detailInfo

詳細收貨地址資訊。

nationalCode

收貨地址國家碼。

 

三、實現效果