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

瞭解 MishiPay 如何透過改用 PWA 提升業務成效。

MishiPay 讓購物者可用智慧型手機掃描並付款,不必浪費時間排隊結帳。有了 MishiPay 的 Scan & Go 技術,購物者就能使用自己的手機掃描物品上的條碼並支付款項,然後直接離開商店即可。研究顯示,店內排入佇列每年的全球零售業成本約為 $2,000 億美元。

我們的技術仰賴裝置硬體功能,例如 GPS 感應器和相機,讓使用者可以找到支援 MishiPay 的商店、掃描實體商店中的商品條碼,然後使用所選數位付款方式付款。最初的 Scan &Go 技術有個平台專屬的 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 的裝置,我們需要備用解碼條碼的解決方案。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
Scandit Enterprise 1D 和 2D
比較開放原始碼與商業條碼掃描程式庫

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

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

掃描的未來趨勢

一旦所有主要瀏覽器都完全支援 Shape Detection API,我們可能就會推出新的 HTML 元素 <scanner>,提供條碼掃描器所需的功能。MishiPay 的工程認為,由於開放原始碼和授權程式庫的數量持續增加,進而提供 Scan &Go 等體驗服務,因此條碼掃描功能確實會長為新的 HTML 元素。

結論

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

特別銘謝

本文經過 Joe Medley