了解改用 PWA 对 MishiPay 的业务有何帮助。
MishiPay 让买家可以使用智能手机扫描商品并付款,而无需浪费时间排队结账。借助 MishiPay 的 Scan & Go 技术,买家可以使用自己的手机扫描商品上的条形码并付款,然后只需离开商店即可。研究表明,每年因在实体店内排队而造成的损失约为 2,000 亿美元。
我们的技术依赖于设备硬件功能(例如 GPS 传感器和摄像头),可让用户查找支持 MishiPay 的商店、扫描实体店内的商品条形码,然后使用他们选择的数字付款方式付款。扫码即走技术的最初版本是平台专用 iOS 和 Android 应用,早期采用者对这项技术赞不绝口。请继续阅读,了解如何通过改用 PWA 将交易量提高 10 倍,并节省了 2.5 年的时间!
10×
交易量增加
2.5 年
已保存队列
挑战
用户在排队或结账时会发现我们的技术非常有用,因为它可以让他们跳过队列,获得顺畅的实体店内体验。但是,下载 Android 或 iOS 应用很麻烦,这就使得用户不会选择我们的技术,尽管我们的技术价值所在。这对 MishiPay 来说是一个日益严峻的挑战,我们需要降低使用门槛,从而提高用户采用率。
解决方案
我们在构建和发布 PWA 方面的努力帮助我们消除了安装麻烦,并鼓励新用户在实体店内试用我们的技术,跳过排队,获得顺畅的购物体验。自发布以来,与平台专用应用相比,我们发现用户对 PWA 的采用率大幅增加。
技术层面的深入探讨
查找支持 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 的商店越来越多,用户必须滚动列表才能找到正确的商店。
- 用户偶尔会误选错误的商店,导致购买交易记录不正确。
为了解决这些问题,我们在每家商店的店内陈设上嵌入了经过地理定位的唯一二维码。这为用户提供了更快捷的新手入门体验。用户只需扫描商店内营销材料上印刷的地理定位二维码,即可访问“扫码即走”网站应用。这样,用户无需输入网址 mishipay.shop
即可访问该服务。
扫描商品
MishiPay 应用的核心功能是条形码扫描,因为此功能使用户能够扫描自己的购买交易,甚至在他们到达收银机之前就能查看累计总额。
为了在 Web 上构建扫描体验,我们确定了三个核心层。
视频流
借助 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 API 的 drawImage()
方法将 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 密集型任务分流到 Web 工作器。在支持硬件图形加速的设备上,WebGL API 及其 WebGL2RenderingContext
可以优化对 CPU 密集型预处理任务的提升效果。
解码器层
最后一层是解码器层,负责对处理层捕获的帧中的条形码进行解码。借助 Shape Detection API(尚未在所有浏览器上提供),浏览器本身可以从 ImageBitmapSource
解码条形码,ImageBitmapSource
可以是 img
元素、SVG image
元素、video
元素、canvas
元素、Blob
对象、ImageData
对象或 ImageBitmap
对象。
/**
* 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));
});
后备解决方案
有多个开源和企业扫描库可供使用,这些库可轻松与任何 Web 应用集成以实现扫描。以下是 MishiPay 推荐的一些库。
上述所有库都是成熟的 SDK,构成了上述所有层。它们还会公开接口来支持各种扫描操作。根据业务案例所需的条形码格式和检测速度,您可以选择 Wasm 解决方案或非 Wasm 解决方案。尽管需要其他资源 (Wasm) 来解码条形码的开销,但 Wasm 解决方案在准确性方面优于非 Wasm 解决方案。
Scandit 是我们的主要选择。它支持我们的业务用例所需的所有条形码格式;在扫描速度方面,它胜过所有可用的开源库。
未来扫描方式
一旦所有主要浏览器都完全支持 Shape Detection API,我们可能会推出一个具有条形码扫描器所需功能的新 HTML 元素 <scanner>
。MishiPay 的工程团队认为,由于支持 Scan &Go 等体验的开源库和许可库数量不断增加,将条形码扫描功能转变为新的 HTML 元素是一个很好的用例。
总结
应用疲劳是开发者在其产品进入市场时面临的一个问题。用户通常希望在下载应用之前了解该应用为他们带来的价值。在商店中,MishiPay 可以为买家节省时间并改善他们的体验,如果买家需要等待下载才能使用应用,这违背了常理。这正是 PWA 的用武之地。通过消除进入门槛,我们的事务数量增加了 10 倍,用户减少了 2.5 年在队列中的等待时间。
致谢
本文由 Joe Medley 审核。