MishiPay';PWA 提升了交易 10 倍,並省下 2.5 年的佇列

瞭解改用 PWA 後,MishiPay 的業務有何變化。

MishiPay 讓購物者可以用智慧型手機掃描並付款,不必浪費時間排隊結帳。有了 MishiPay 的掃描即走技術,購物者就能使用自己的手機掃描商品條碼並付款,然後直接離開商店。研究顯示,全球零售業每年因店內排隊而損失約 2,000 億美元。

我們的技術仰賴裝置硬體功能,例如 GPS 感應器和相機,讓使用者可以找到支援 MishiPay 的商店、掃描實體商店中的商品條碼,然後使用所選數位付款方式付款。掃描與前往技術的初始版本是針對 iOS 和 Android 平台的應用程式,早期採用者非常喜歡這項技術。請繼續閱讀,瞭解改用 PWA 後,交易量增加了 10 倍,並節省了 2.5 年的排隊時間!

    10×

    交易量增加

    2.5 年

    已儲存排隊功能

挑戰

使用者在排隊或結帳時,會發現我們的技術非常實用,因為這項技術可讓他們略過排隊,享有順暢的店內體驗。但由於下載 Android 或 iOS 應用程式相當麻煩,因此使用者即使認為我們的技術很有價值,也不會選擇採用。這對 MishiPay 來說是一項日益嚴峻的挑戰,因此我們需要降低使用門檻,提高使用者採用率。

解決方案

我們努力建構及推出 PWA,不僅可免除安裝麻煩,還能鼓勵新使用者在實體商店中試用我們的技術,省去排隊等候的時間,享受無縫的購物體驗。自推出以來,與特定平台應用程式相比,使用者採用 PWA 的比例大幅增加。

直接啟動 PWA (左側,速度較快) 與安裝及啟動 Android 應用程式 (右側,速度較慢) 的並排比較。
按平台劃分的交易。¡OS:16397 (3.98%)。Android:13769 (3.34%)。網頁:382184 (92.68%)。
大部分交易都會在網路上發生。

深入探討技術

尋找支援 MishiPay 的商店

為啟用這項功能,我們會使用 getCurrentPosition() API 搭配 IP 版備用解決方案。

const geoOptions = {
  timeout: 10 * 1000,
  enableHighAccuracy: true,
  maximumAge: 0,
};

window.navigator.geolocation.getCurrentPosition(
  (position) => {
    const cords = position.coords;
    console.log(`Latitude :  ${cords.latitude}`);
    console.log(`Longitude :  ${cords.longitude}`);
  },
  (error) => {
    console.debug(`Error: ${error.code}:${error.message}`);
    /**
     * Invoke the IP based location services
     * to fetch the latitude and longitude of the user.
     */
  },
  geoOptions,
);

這種做法在早期版本的應用程式中運作良好,但後來證實對 MishiPay 使用者而言是個大問題,原因如下:

  • 以 IP 為基礎的備用方案位置不準確。
  • 隨著支援 MishiPay 的商店數量增加,使用者必須捲動清單才能找出正確的商店。
  • 使用者偶爾會不小心選擇錯誤的商店,導致購買交易記錄錯誤。

為解決這些問題,我們在各門市店內的螢幕上嵌入獨特的地理位置 QR code。這有助於加快新手上路體驗。使用者只要掃描商店內行銷資料上印製的地理位置 QR code,即可存取 Scan & Go 網頁應用程式。這樣一來,使用者就不必輸入網址 mishipay.shop 就能存取服務。

使用 PWA 的店內掃描體驗。

掃描產品

MishiPay 應用程式的核心功能是條碼掃描,讓使用者在結帳前掃描自己的購物項目,並查看累積總金額。

為了在網路上提供掃描體驗,我們已找出三個核心層級。

圖表顯示三個主要執行緒層:影片串流、處理層和解碼器層。

影片串流

透過 getUserMedia() 方法的協助,我們可以根據下列限制存取使用者的後視鏡頭。叫用這個方法會自動觸發提示,要求使用者接受或拒絕攝影機存取權。取得影片串流後,我們可以將影片串流轉送至影片元素,如下所示:

/**
 * Video Stream Layer
 * https://developer.mozilla.org/docs/Web/API/MediaDevices/getUserMedia
 */
const canvasEle = document.getElementById('canvas');
const videoEle = document.getElementById('videoElement');
const canvasCtx = canvasEle.getContext('2d');
fetchVideoStream();
function fetchVideoStream() {
  let constraints = { video: { facingMode: 'environment' } };
  if (navigator.mediaDevices !== undefined) {
    navigator.mediaDevices
      .getUserMedia(constraints)
      .then((stream) => {
        videoEle.srcObject = stream;
        videoStream = stream;
        videoEle.play();
        // Initiate frame capture - Processing Layer.
      })
      .catch((error) => {
        console.debug(error);
        console.warn(`Failed to access the stream:${error.name}`);
      });
  } else {
    console.warn(`getUserMedia API not supported!!`);
  }
}

處理層

如要在特定影片串流中偵測條碼,我們需要定期擷取影格,並將影格傳送至解碼器層。如要擷取影格,只要使用 Canvas APIdrawImage() 方法,將 VideoElement 的串流繪製到 HTMLCanvasElement 即可。

/**
 * Processing Layer - Frame Capture
 * https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Manipulating_video_using_canvas
 */
async function captureFrames() {
  if (videoEle.readyState === videoEle.HAVE_ENOUGH_DATA) {
    const canvasHeight = (canvasEle.height = videoEle.videoHeight);
    const canvasWidth = (canvasEle.width = videoEle.videoWidth);
    canvasCtx.drawImage(videoEle, 0, 0, canvasWidth, canvasHeight);
    // Transfer the `canvasEle` to the decoder for barcode detection.
    const result = await decodeBarcode(canvasEle);
  } else {
    console.log('Video feed not available yet');
  }
}

針對進階用途,這個圖層也會執行一些預先處理工作,例如裁剪、旋轉或轉換為灰階。這些工作可能會耗用大量 CPU,且由於條碼掃描是長時間執行的作業,因此可能會導致應用程式無法回應。有了 OffscreenCanvas API 的協助,我們就能將耗用大量 CPU 資源的工作卸載至網頁工作者。在支援硬體圖形加速的裝置上,WebGL API 及其 WebGL2RenderingContext 可在 CPU 密集的預處理工作中,提升效益。

解碼器層

最後一個層級是解碼器層,負責從處理層擷取的影格中解碼條碼。多虧形狀偵測 API (目前尚未在所有瀏覽器上提供),瀏覽器本身會從 ImageBitmapSource 解碼條碼,ImageBitmapSource 可以是 img 元素、SVG image 元素、video 元素、canvas 元素、Blob 物件、ImageData 物件或 ImageBitmap 物件。

圖表顯示三個主要執行緒層:影像串流、處理層和形狀偵測 API。

/**
 * Barcode Decoder with Shape Detection API
 * https://web.dev/shape-detection/
 */
async function decodeBarcode(canvas) {
  const formats = [
    'aztec',
    'code_128',
    'code_39',
    'code_93',
    'codabar',
    'data_matrix',
    'ean_13',
    'ean_8',
    'itf',
    'pdf417',
    'qr_code',
    'upc_a',
    'upc_e',
  ];
  const barcodeDetector = new window.BarcodeDetector({
    formats,
  });
  try {
    const barcodes = await barcodeDetector.detect(canvas);
    console.log(barcodes);
    return barcodes.length > 0 ? barcodes[0]['rawValue'] : undefined;
  } catch (e) {
    throw e;
  }
}

對於尚不支援形狀偵測 API 的裝置,我們需要備用解碼條碼的解決方案。形狀偵測 API 會公開 getSupportedFormats() 方法,協助您在形狀偵測 API 和備用解決方案之間切換。

// Feature detection.
if (!('BarceodeDetector' in window)) {
  return;
}
// Check supported barcode formats.
BarcodeDetector.getSupportedFormats()
.then((supportedFormats) => {
  supportedFormats.forEach((format) => console.log(format));
});

流程圖顯示,視 Barcode Detector 支援的功能和支援的條碼格式而定,系統會使用 Shape Detection API 或備用解決方案。

備用解決方案

您可以使用多個開放原始碼和企業掃描程式庫,輕鬆將其與任何網路應用程式整合,以便實作掃描作業。以下是 MishiPay 推薦的部分程式庫。

程式庫名稱 類型 Wasm 解決方案 條碼格式
QuaggaJs 開放原始碼 1 天
ZxingJs 開放原始碼 1D 和 2D (受限)
CodeCorp Enterprise 1D 和 2D
Scandit Enterprise 1D 和 2D
開放原始碼和商業條碼掃描程式庫的比較

上述所有程式庫都是完整的 SDK,可組成上述所有層級。並公開介面,支援各種掃描作業。視業務需求所需的條碼格式和偵測速度而定,您可以選擇使用 Wasm 或非 Wasm 解決方案。雖然需要額外資源 (Wasm) 來解碼條碼,但 Wasm 解決方案在準確度方面優於非 Wasm 解決方案。

我們主要選擇 Scandit。這項工具支援所有業務用途所需的條碼格式,在掃描速度方面勝過所有可用的開放原始碼程式庫。

掃描的未來

一旦所有主要瀏覽器都完全支援 Shape Detection API,我們可能就會擁有新的 HTML 元素 <scanner>,其中包含條碼掃描器所需的功能。MishiPay 的工程團隊認為,由於開放原始碼和授權程式庫的數量不斷增加,可提供「掃描並前往」等體驗,因此條碼掃描功能確實適合作為新的 HTML 元素。

結論

開發人員在產品進入市場時,可能會遇到應用程式疲勞的問題。使用者通常會在下載應用程式前,先瞭解應用程式提供的價值。在商店中,MishiPay 可為購物者省下時間並改善體驗,因此如果購物者必須等待下載作業完成才能使用應用程式,這就違反直覺。這正是 PWA 發揮作用的地方。我們消除了進入門檻,交易量因此增加了 10 倍,使用者也能省下 2.5 年的等待時間。

特別銘謝

本文由 Joe Medley 審查。