MishiPay 的 PWA 将交易量提升 10 倍,并节省 2.5 年的排队时间

了解切换到 PWA 如何帮助 MishiPay 的业务。

MishiPay 使购物者能够使用智能手机扫描商品并付款,而无需在收银台浪费时间排队。借助 MishiPay 的 Scan & Go 技术,购物者可以使用自己的手机扫描商品上的条形码并付款,然后直接离开商店。研究表明,店内排队每年给全球零售业造成约 2000 亿美元的损失。

我们的技术依赖于设备硬件功能,例如 GPS 传感器和摄像头,使用户能够找到支持 MishiPay 的商店,在实体店内扫描商品条形码,然后使用他们选择的数字支付方式付款。我们 Scan & Go 技术的初始版本是特定于平台的 iOS 和 Android 应用程序,早期采用者非常喜欢这项技术。请继续阅读,了解切换到 PWA 如何使交易量增加 10 倍并节省 2.5 年的排队时间!

    10×

    交易量提升

    2.5 年

    节省排队时间

挑战

用户发现在排队或结账时我们的技术非常有帮助,因为它允许他们跳过排队并获得顺畅的店内体验。但是,下载 Android 或 iOS 应用程序的麻烦使得用户尽管认识到其价值,但仍然不选择我们的技术。这对 MishiPay 来说是一个日益严峻的挑战,我们需要以更低的准入门槛来提高用户采用率。

解决方案

我们构建和启动 PWA 的努力帮助我们消除了安装麻烦,并鼓励新用户在实体店内试用我们的技术、跳过排队并获得无缝的购物体验。自发布以来,与我们特定于平台的应用程序相比,我们的 PWA 的用户采用率大幅飙升。

直接启动 PWA(左,更快)与安装和启动 Android 应用(右,更慢)的并排比较。
Transactions by platform. ¡OS: 16397 (3.98%). Android: 13769 (3.34%). Web: 382184 (92.68%).
大部分交易都在 Web 上发生。

技术深入探讨

定位支持 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 码。这为更快的入职体验铺平了道路。用户只需扫描商店营销材料上打印的地理定位 QR 码,即可访问 Scan & Go Web 应用程序。这样,他们可以避免键入网址 mishipay.shop 来访问该服务。

使用 PWA 的店内扫描体验。

扫描商品

MishiPay 应用程序的核心功能是条形码扫描,因为这使我们的用户能够扫描自己的购买商品,并在他们到达收银台之前就看到正在计算的总额。

为了在 Web 上构建扫描体验,我们确定了三个核心层。

Diagram showing the three main thread layers: video stream, processing layer, and decoder layer.

视频流

借助 getUserMedia() 方法,我们可以使用下面列出的约束条件访问用户的后置摄像头。调用该方法会自动触发提示,让用户接受或拒绝访问他们的摄像头。一旦我们获得对视频流的访问权限,我们就可以将其传递到视频元素,如下所示

/**
 * Video Stream Layer
 * https://mdn.org.cn/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!!`);
  }
}

处理层

为了检测给定视频流中的条形码,我们需要定期捕获帧并将它们传输到解码器层。为了捕获帧,我们只需使用 drawImage() 方法,将来自 VideoElement 的流绘制到 Canvas APIHTMLCanvasElement 上。

/**
 * Processing Layer - Frame Capture
 * https://mdn.org.cn/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 密集型任务卸载到 Web Worker。在支持硬件图形加速的设备上,WebGL API 及其 WebGL2RenderingContext 可以优化 CPU 密集型预处理任务的性能。

解码器层

最后一层是解码器层,它负责从处理层捕获的帧中解码条形码。感谢 Shape Detection API(并非在所有浏览器上都可用),浏览器本身可以从 ImageBitmapSource 中解码条形码,ImageBitmapSource 可以是 img 元素、SVG image 元素、video 元素、canvas 元素、Blob 对象、ImageData 对象或 ImageBitmap 对象。

Diagram showing the three main thread layers: video stream, processing layer, and Shape Detection API.

/**
 * Barcode Decoder with Shape Detection API
 * https://webdev.ac.cn/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));
});

Flow diagram showing how, dependent on Barcode Detector support and the supported barcode formats, either the Shape Detection API or the fallback solution  is being used.

后备解决方案

有几个开源和企业级扫描库可供使用,可以轻松地与任何 Web 应用程序集成以实现扫描。以下是 MishiPay 推荐的一些库。

库名称 类型 Wasm 解决方案 条形码格式
QuaggaJs 开源 1D
ZxingJs 开源 1D 和 2D(有限)
CodeCorp 企业级 1D 和 2D
Scandit 企业级 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 审阅。