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 应用使用户不再选择我们的技术,尽管我们的技术价值极高。它正在逐渐发展壮大 因此,我们需要提高用户采用率,同时降低进入门槛。

解决方案

我们在构建和发布 PWA 的过程中所做的努力使我们免去了安装麻烦, 鼓励新用户在实体店内试用我们的技术,免去排队的麻烦, 打造顺畅的购物体验。自推出以来,我们看到用户采用率大幅提升, 相较于平台专用应用。

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
</ph> 直接启动 PWA(左侧,更快)与安装并启动 Android 应用(右侧、更慢)的并排比较。
<ph type="x-smartling-placeholder">
</ph> 交易(按平台)。¡OS:16397 (3.98%)。Android:13769 (3.34%)。Web:382184 (92.68%)。 <ph type="x-smartling-placeholder">
</ph> 大多数交易都是在网络上进行的。

技术深入探究

查找支持 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 的商店越来越多,这需要用户滚动列表并 找到正确的商店
  • 用户偶尔会误选错误的商店,导致购买交易被记录 错误。

为了解决这些问题,我们在店内显示屏上嵌入了经过地理定位的唯一二维码, 。这为实现更快的新手入门体验铺平了道路。用户只需扫描经过地理定位的二维码 (在实体店内的营销材料上),Go Web 应用。 这样,用户无需输入网址 mishipay.shop 即可访问该服务。

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
</ph> 使用 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!!`);
  }
}

处理层

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

/**
 * 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 密集型任务交给 Web Worker。在支持硬件图形加速的设备上, WebGL API 及其 WebGL2RenderingContext可以 优化 CPU 密集型预处理任务的收益。

解码器层

最后一层是解码器,该层负责解码帧中的条形码 捕获的数据多亏了 Shape Detection API(即 不是所有浏览器都支持的)浏览器本身会通过 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;
  }
}

对于尚不支持形状检测 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 或后备解决方案的流程图,具体取决于条形码检测器支持和支持的条形码格式。

后备解决方案

有几个开源和企业扫描库可供选择,可以轻松集成 与任何 Web 应用搭配使用,以实现扫描。以下是 MishiPay 提供的一些库 。

。 <ph type="x-smartling-placeholder">
图书馆名称 类型 Wasm 解决方案 条形码格式
QuaggaJs 开源 1 日
ZxingJs 开源 1D 和2D(受限)
CodeCorp 企业 1D 和二维
Scandit 企业 1D 和二维
</ph> 开源和商用条形码扫描库的比较

上述所有库都是成熟的 SDK,构成了上述所有层。他们还 公开接口以支持各种扫描操作。根据条形码格式和 业务案例所需的检测速度,可以在 Wasm 和非 Wasm 解决方案之间做出决定。 尽管需要额外的资源 (Wasm) 来解码条形码,但会产生开销,Wasm 解决方案在准确率方面优于非 Wasm 解决方案。

Scandit 是我们的首选。支持所有条形码 业务用例所需的广告格式它优于 扫描速度。

未来扫描方式

在所有主流浏览器全面支持 Shape Detection API 后,我们可能需要 新 HTML 元素 <scanner>,它具有条形码扫描器所需的功能。工程学 我们认为,将条形码扫描功能作为一种新型 越来越多的开放源代码和经过许可的库支持此类 HTML 元素, 例如扫描和Go 以及许多其他功能。

总结

应用疲劳是开发者在产品上市时面临的一个问题。用户通常希望 在他们下载应用之前,先了解应用能为其带来的价值。在商店里 MishiPay 为买家节省了并改善他们的体验,等待 然后才能使用应用这正是 PWA 大显身手的地方。消除障碍 我们的交易量增加了 10 倍,使我们的用户节省了 2 年半的时间。 在队列中等待。

致谢

本文由 Joe Medley 审核。