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

瞭解改用 PWA 如何協助 MishiPay 提升業績。

MishiPay 可讓購物者使用智慧型手機掃描商品並支付購物款項,不必浪費時間排入結帳頁面。有了 MishiPay 的掃描及 Go 技術,購物者可以使用自己的手機掃描商品條碼並支付商品費用,接著只要離開商店即可。研究指出,每年的實體商店排入佇列費用,相當於全球零售業大約 $2,000 億美元。

我們的技術仰賴裝置硬體功能,例如 GPS 感應器和攝影機,可讓使用者找出支援 MishiPay 的商店、掃描實體商店內的商品條碼,然後使用自己選擇的數位付款方式付款。我們最初的 Scan & Go 技術為特定平台的 iOS 和 Android 應用程式,而早期採用者都很喜歡這項技術。歡迎閱讀下文,瞭解如何改用 PWA 將交易量提高 10 倍,並省下 2.5 年的佇列時間!

    10×

    交易量增加

    2.5 年

    已儲存待播清單

挑戰

使用者認為我們的技術在排隊或退房時非常有幫助,因為這樣可以略過佇列,享有流暢的店內體驗。但是,下載 Android 或 iOS 應用程式非常麻煩,使用者卻無法選擇我們的技術。對 MishiPay 來說,這是日益艱鉅的挑戰,我們希望盡可能降低使用者採用率,並降低進入門檻。

解決方法

我們建構及推出 PWA 時,我們投注了心力減少安裝方面的麻煩,同時也鼓勵新使用者在實體商店中試用我們的技術、跳過等候並享受流暢的購物體驗。自從推出 PWA 以來,我們發現與平台專屬應用程式相比,使用者採用 PWA 的使用者人數激增。

並排比較直接啟動 PWA (左側、更快) 與安裝及啟動 Android 應用程式 (速度較慢) 的比較結果。
交易 (依平台區分)。你好: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 的預先處理工作上盡可能提高成效。

解碼器層

最後一層是解碼器層,負責從處理層擷取的影格中解碼條碼。藉助 Shape Detection API (部分瀏覽器尚未支援這個 API),瀏覽器本身會從 ImageBitmapSource 解碼 ImageBitmapSource 的條碼,例如 img 元素、SVG image 元素、video 元素、canvas 元素、Blob 物件、ImageData 物件或 ImageBitmap 物件。

這張圖表顯示三個主要執行緒層:影片串流、處理層和 Shape Detection 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;
  }
}

如果裝置尚未支援 Shape Detection API,我們需要備用解決方案來解碼條碼。Shape Detection API 提供 getSupportedFormats() 方法,有助於在 Shape Detection API 和備用解決方案之間切換。

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

流程圖,說明如何根據條碼偵測器支援情形和支援的條碼格式,使用 Shape Detection API 或備用解決方案。

備用解決方案

可使用一些開放原始碼和企業掃描程式庫,輕鬆與任何網頁應用程式整合,以進行掃描。以下是 MishiPay 建議的幾個程式庫。

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

上述所有程式庫都是發展成熟的 SDK,可組合上述所有層。他們也會公開介面來支援各種掃描作業。視業務案例所需的條碼格式和偵測速度而定,您可以在 Wasm 和非 Wasm 解決方案之間做出決定。雖然需要額外資源 (Wasm) 才能解碼條碼,但 Wasm 解決方案在準確率方面比非 Wasm 解決方案的成效更好。

我們主要使用 Scandit。它支援我們業務用途所需的所有條碼格式,掃描速度比所有可用的開放原始碼程式庫還快。

未來的掃描作業

所有主要瀏覽器全面支援 Shape Detection API 後,我們可能會提供新的 HTML 元素 <scanner>,其中包含條碼掃描器所需的功能。MishiPay 工程團隊認為,條碼掃描功能已是可靠的新 HTML 元素,因為開放原始碼和授權程式庫的數量持續增加,無法支援掃描和 Go 等多種功能。

結論

應用程式疲勞屬於開發人員的產品進入市場時,會感到困擾。使用者通常會想要在下載應用程式前,瞭解應用程式提供的價值。在 MishiPay 的商店中,購物者可以節省寶貴時間並改善使用者體驗,因此在操作應用程式前,應先等待下載完成,再使用應用程式。這就是我們的 PWA 可以幫上忙的地方。藉由消除進入障礙,交易量增加了 10 倍,使用者也因此能省下等待 2.5 年的時間。

特別銘謝

本文是由 Joe Medley 審查過的。