開發與維運

支付寶小程序 API

本章內容出自《小程序開發不求人》電子書,點擊下載完整版

支付寶小程序 API

簡介

支付寶小程序開發框架提供了豐富的 JSAPI(原生 API) 和 OpenAPI(開放能力 API),開發者可方便快捷地調用這些 API,詳情請參見 API 概覽

  • OpenAPI 是支付寶開放平臺在小程序上開放的開放能力 API。通過 OpenAPI,小程序可以輕鬆實現用戶授權、獲取會員基礎信息、獲取用戶手機號、小程序喚起支付、跳轉支付寶卡包、會員開卡授權等多種多樣的功能。
  • JSAPI 按實現的功能分類,可分為界面、多媒體、緩存、文件、位置、網絡、設備、數據安全、分享、收藏、自定義通用菜單、小程序當前運行版本類型、自定義分析、更新管理等14 個大類。

本章我們將講述支付寶小程序比較常用的幾個 JSAPI,帶你走進 JSAPI 的奇妙世界。

如何祕密告白:小程序 HTTPS 網絡請求實現

木心的《從前慢》裡說:

記得早先少年時
大家誠誠懇懇
說一句 是一句
……
從前的日色變得慢
車,馬,郵件都慢
一生只夠愛一個人
從前的鎖也好看
鑰匙精美有樣子
你鎖了 人家就懂了

木心懷念著過去那個通過郵件傳遞信息的簡單時代。
當下的網絡時代雖然瞬息萬變,但傳遞信息也是一樣的“說一句,是一句”,“你鎖了,人家就懂了”
小程序經常需要往服務器傳遞數據或者從服務器拉取信息。
當用戶通過小程序加載服務器傳來的信息時,整個網絡過程如下:

  1. 用戶通過小程序向服務器發出 GET 請求,
  2. 服務器發送一個響應,響應信息包含一個數據文件。

image.png

該過程叫做 HTTP。
上面的流程也許過於簡化,其實用戶與服務器之間不可能面對面直接通話,因為它們相隔不是很近,甚至服務器是在瀏覽器的千里之外,而客戶端瀏覽器不可能直通服務器。

image.png

每一次的網絡請求,小程序傳遞給服務器的信息,中間經過多重的信息轉達。同理服務器迴應小程序的響應也是同樣的路徑。
通俗點說,就是傳紙條的原理。寫字條的同學需要把字條遞給旁邊第一個同學,然後第二個同學遞給第三個同學,以此類推,一直傳遞到最後的信息接收者。
讓我們看看,在傳遞字條的過程中,如果信息發出者想要給信息最末尾的接受者告白,會發生什麼呢?

image.png

在 HTTP 狀態下,傳遞者都可以打開字條,查看裡面的內容。而且發送信息者無法知道傳輸路徑,一旦發生信息竊取,甚至不知道是誰竊取的。

image.png

還是就是當信息落入心懷不軌的人手中,或者篡改信息內容,其後果不可設想。比如,把“你喜歡我嗎?”篡改成“你不喜歡我嗎?”。

image.png

為了避免這些情況發生,HTTP 安全版本應運而生,即 HTTPS。通過 HTTPS,傳送的每次信息都被加上一個鎖。

image.png

該鎖配套的公鑰和密鑰僅小程序和服務器知道,其他傳遞者無法獲取。因此,無論客戶端發送的信息經過多個路由器,他人都無法讀取信息內容。
客戶端發送初始信息到服務器時,在信息內容中包含服務器的名稱(在名為“服務器名稱指示”的字段中)。而服務器運行商可以在同一臺計算機上運行多個站點,因此運行商可以跟蹤到客戶端的訪問軌跡。雖然初始的信息已設置了加密,但是初始請求是仍未加密的。
這就是通過小程序 my.request 安全傳遞告白信息的故事。本節將介紹如何使用my.request API 實現網絡請求,並介紹一些使用注意事項。
版本要求:基礎庫 1.11.0 或更高版本;支付寶客戶端 10.1.32 或更高版本,若版本較低,建議做 兼容處理
發起網絡請求:

  • my.request 目前支持 GET/POST。
  • my.request 目前只支持 HTTPS 協議的請求。

使用說明:

  • 請預先在 支付寶小程序管理中心 > 小程序詳情 > 設置 > 開發設置 > 服務器域名白名單中配置域名白名單。小程序在以下 API 調用時只能與白名單中的域名進行通訊:HTTP 請求(my.request)、上傳文件(my.uploadFile)、下載文件(my.downloadFile)和WebSocket(my.connectSocket)。

    image.png

  • 添加服務器域名白名單後,需要重新打包上傳生成體驗版,服務器域名才會生效。
  • 在 IDE 上進行調試時,請使用真機預覽調試。
  • 支付寶客戶端已不再維護 my.httpRequest,建議使用 my.request。另外,釘釘客戶端尚不支持 my.request。若在釘釘客戶端開發小程序,則需要使用 my.httpRequest。

掃碼體驗

image.png

重要:

  • 小程序開發過程中,可在開發工具內 詳情 > 域名信息 > 忽略 httpRequest 域名合法性檢查 中選擇是否忽略域名合法性檢查,如果選擇忽略,則在模擬器、預覽以及真機調試場景不會校驗域名合法性,但小程序上線前必須確保通訊域名在白名單內,否則在正式版本無法調用。
  • my.request 的請求頭默認值為 {'content-type': 'application/json'},而不是{'contenttype': 'application/x-www-form-urlencoded'}。此外,請求頭對象裡面的 key 和value 必須是 String 類型。

示例代碼

// dataType 為 json 示例
my.request({
 url: 'https://httpbin.org/post',
 method: 'POST',
 data: {
 from: '支付寶',
 production: 'AlipayJSAPI',
 },
 dataType: 'json',
 success: function(res) {
 my.alert({content: 'success'});
 },
 fail: function(res) {
 my.alert({content: 'fail'});
 },
 complete: function(res) {
 my.hideLoading();
 my.alert({content: 'complete'});
 }
});
// dataType 為 base64 示例
my.request({
 url:
'https://gw.alipayobjects.com/mdn/miniapp_de/afts/img/A*G1kWSJbe2zEAAAAA
AAAAAABjARQnAQ',
 method: 'GET',
 dataType: 'base64',
 success: (resp) => {
 console.log('resp data length', resp.data.length);
 console.log('resp data', resp.data); // 返回格式類似於:
data:image/png;base64,iVBORw0KG...
 },
 fail: (err) => {
 console.log('error', err);
 },
});

入參

Object 類型,屬性如下:

image.png

data 參數說明

傳給服務器的數據最終會是 String 類型,如果 data 不是 String 類型,會被轉換成 String 。轉換規則如下:

  • 若方法為 GET,會將數據轉換成 query string:
    encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)...
  • 若方法為 POST 且 headers['content-type'] 為 application/json ,會對數據進行JSON 序列化
  • 若方法為 POST 且 headers['content-type'] 為 application/x-www-formurlencoded ,會將數據轉換成 query string:encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)...

success 回調函數

入參為 Object 類型,屬性如下:

屬性 類型 描述
data String 響應數據,格式取決於請求時的dataType 參數,如果 dataType 值為 base64時,返回的是符合 data URIscheme 規範的內容字符串。
status Number 響應碼。
headers Object 響應頭。

返回值 RequestTask

網絡請求任務對象。調用 my.request 後返回的請求對象。

RequestTask.abort()

中斷請求任務。
示例代碼

// 返回 RequestTask,可以調用 abort 方法取消請求
const task = my.request({url: 'https://httpbin.org/post'})
task.abort();

“抱歉,不是我的菜”:小程序掃碼點餐化解尷尬

月上柳梢頭,人約黃昏後。
週五到了,小心翼翼約她出來吃晚飯,她欣然應約。
餐廳位於徐彙區鬧中取靜的華山路,法式梧桐的點綴讓餐廳更顯典雅,也更富有異國情調。踏入餐廳,燈光是橘色的,餐具是藍的,桌椅也是藍的,讓人恍惚之間有到了愛琴海邊的錯覺,唯美的裝修風格、充滿歐洲風味的精緻美食,處處洋溢著地中海風情,真浪漫啊。
她翩翩而至,裙裾飛揚。
見到她我臉紅了。我緊張地問她要吃些什麼,又手忙腳亂地叫來服務員點完了菜,
臉上冒出了小汗珠。
窗外的小雨滴滴答答,窗內的我們顯得格外安靜。
我鼓起勇氣,打破沉默,小聲問道:“你……你對我印象如何?”
“抱歉,不是我的菜……”
此刻,我如同五雷轟頂,只覺天旋地轉,眼前華光溢彩的餐廳瞬間變得黯淡了。
“你是不是點錯菜了?還是上錯菜了呢?”她指著桌上的法式田螺和奶油蘑菇湯,瞪大了眼睛問我。旁邊站著滿臉疑惑的上菜員。

如何化解點錯菜的尷尬呢?這時就需要使用支付寶小程序掃碼點餐的功能了。

image.png

為了讓用戶減少輸入,我們可以把複雜的信息編碼成一個二維碼,利用 my.scanAPI 調起支付寶掃一掃,用戶掃碼之後,my.scan 的 success 回調會收到這個二維碼所對應的字符串信息。
例如餐廳點餐的小程序,我們給餐廳中每個餐桌編號 1-100 號,把這個數字編碼到二維碼中,掃碼獲得編號之後,就可以知道是哪一桌點的菜,大大提高點餐體驗和效率。

// 利用 my.scanCode 獲取二維碼的數據
//page.js
Page({
 // 點擊“掃碼訂餐”的按鈕,觸發 tapScan 回調
 tapScan: function() {
 // 調用 my.login 獲取微信登錄憑證
 my.scanCode({
 success: function(res) {
 var num = res.result // 獲取到的 num 就是餐桌的編號
 }
 })
 }
})

還有很多場景可以結合支付寶 App 掃碼能力做到很好的體驗,例如通過掃商品上的一維碼做一個商品展示的小程序;通過掃共享單車上的二維碼去開啟單車。我們可以多思考如何利用這個掃碼能力去替代一些繁瑣的輸入操作,讓我們的小程序變得更加便捷。

掃碼體驗

image.png

示例代碼

// API-DEMO page/API/scan-code/scan-code.json
{
 "defaultTitle": "Scan"
}
<!-- API-DEMO page/API/scan-code/scan-code.axml-->
<view class="page">
 <view class="page-section">
 <form onSubmit="scanCode">
 <view>
 <button type="primary" onTap="scan">掃碼</button>
 </view>
 </form>
 </view>
</view>
// API-DEMO page/API/scan-code/scan-code.js
Page({
 scan() {
 my.scan({
 type: 'qr',
 success: (res) => {
 my.alert({ title: res.code });
 },
 });
 }
})

入參

Object 類型,屬性如下:

image.png
image.png

success 回調函數

入參為 Object 類型,屬性如下:

image.png

定格甜蜜回憶:小程序上傳圖片功能實現

還記得你們第一次手牽手出門嗎?
還記得你們第一次共享浪漫的燭光晚餐嗎?
還記得你們第一次依偎在電影院中約會嗎?
還記得你們第一次去迪士尼約會圓她一個公主夢嗎?
甜蜜的回憶,多想定格在時光裡。不如開發一個小程序,用於上傳存儲這些甜甜的照片吧!

本節將介紹如何通過 my,uploadFile API 實現小程序上傳圖片的功能。
使用前提:
請預先在 支付寶小程序管理中心 > 小程序詳情 >設置 > 開發設置 > 服務器域名白名單 中配置域名白名單。小程序在以下 API 調用時只能與白名單中的域名進行通訊:HTTP 請求(my.request)、上傳文件(my.uploadFile)、下載文件(my.downloadFile)和 WebSocket(my.connectSocket)。
image.png

掃碼體驗

image.png

示例代碼

// API-DEMO page/upload-file/upload-file.json
{
 "defaultTitle": "Upload File"
}
<!-- API-DEMO page/upload-file/upload-file.axml -->
<view class="page">
 <button type="primary" onTap="uploadFile">上傳圖片</button>
</view>
// API-DEMO page/API/upload-file/upload-file.js
Page({
 uploadFile() {
 my.chooseImage({
 chooseImage: 1,
 success: res => {
 const path = res.apFilePaths[0];
 console.log(path);
 my.uploadFile({
 url: 'http://httpbin.org/post',
 fileType: 'image',
 fileName: 'file',
 filePath: path,
 success: res => {
 my.alert({ title: '上傳成功' });
 },
 fail: function(res) {
 my.alert({ title: '上傳失敗' });
 },
 });
 },
 });
 },
});

上傳文件的後端代碼,相關 openAPI 請參考
alipay.offline.material.image.upload(上傳門店照片和視頻接口)

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {

 String path = req.getParameter("filePath");
 //得到要下載的文件名
 String fileName = URLEncoder.encode(req.getParameter("fileName"),"utf-8");
 String fileType = path.substring(path.lastIndexOf('.')+1,path.length());
 FileInputStream fis = new FileInputStream(path);

 System.out.println("debugFileName: "+ fileName);
 //下載文件存放路徑
 String localPath = "";

 FileOutputStream fs = new FileOutputStream(localPath + fileName
+"."+fileType);

 resp.setHeader("content-disposition", "attachment;filename="+fileName);
 resp.setHeader("content-type", fileType );

 //執行 fileOutputStream 的輸出操作
 int len = 1;
 byte[] b = new byte[1024];
 while((len=fis.read(b))!=-1){
 fs.write(b, 0, len);
 }
 fs.close();
 fis.close();
}

入參

Object 類型,屬性如下:

image.png
image.png

success 回調函數

入參為 Object 類型,屬性如下:

image.png

愛情不掉線:小程序獲取設備的網絡狀態

地球不爆炸,愛情不掉線。宇宙不重啟,我們不分離。
不想要和手機裡的女朋友掉線的你,如何獲取到她的手機網絡狀態呢?

image.png

大家都知道,手機連接到互聯網有幾種方式:WiFi、2G、3G、4G,包括很快到來的 5G。每種方式的上傳速度和下載速度差異很大,它們的計費方式的差異也導致用戶在使用互聯網服務的時候有不同的使用習慣。
WiFi 相對於其他幾種網絡連接方式,其速度會更快。WiFi 一般情況下,都是免費供用戶使用,而移動數據網絡是需要根據使用流量進行計費的。
考慮這樣一個場景,小程序需要下載一些文檔,然後通過小程序的能力去預覽這個文檔,這些文檔可能文件體積比較大,對於某些用戶來說,他們並不想耗費太多的數據流量去預覽文檔。這種情況下,可以通過小程序提供的獲取網絡狀態 APImy.getNetworkType,做一些更友好的體驗提示。

掃碼體驗

image.png

入參

Object 類型,屬性如下:

image.png

success 回調函數

入參為 Object 類型,屬性如下:

image.png

示例代碼

// API-DEMO page/API/get-network-type/get-network-type.json
{
 "defaultTitle": "獲取手機網絡狀態"
}
<!-- API-DEMO page/API/get-network-type/get-network-type.axml-->
<view class="page">
 <view class="page-section">
 <view class="page-section-demo">
 <view class="page-body-title">網絡狀態</view>
 <block a:if="{{hasNetworkType === false}}">
 <text class="page-body-text">未獲取</text>
 <text class="page-body-text">點擊按鈕可獲取網絡狀態</text>
 </block>
 <block a:if="{{hasNetworkType === true}}">
 <text class="page-body-text-network-type">{{networkType}}</text>
 </block>
 </view>
 <view class="page-section-btns">
 <view onTap="getNetworkType">獲取手機網絡狀態</view>
 <view onTap="clear">清空</view>
 </view>
 </view>
</view>
// API-DEMO page/API/get-network-type/get-network-type.js
Page({
 data: {
 hasNetworkType: false
 },
 onLoad() {
 this.onChange = this.onChange.bind(this);
 // my.onNetworkChange(this.onChange);
 },
 onChange(res){
 console.log('onNetworkChange', res);
 this.setData({
 hasNetworkType: true,
 networkType: res.networkType
 });
 },
 onUnload() {
 // my.offNetworkChange(this.onChange);
 },
 getNetworkType() {
 my.getNetworkType({
 success: (res) => {
 this.setData({
 hasNetworkType: true,
 networkType: res.networkType
 })
 }
 })
 },
 clear() {
 this.setData({
 hasNetworkType: false,
 networkType: ''
 })
 },
});
/* API-DEMO page/API/get-network-type/get-network-type.acss */
.page-body-info {
 height: 200rpx;
}
.page-body-text-network-type {
 font-size: 80rpx;
 font-family: Helvetica;
}

Leave a Reply

Your email address will not be published. Required fields are marked *