Узнайте, как переход на PWA помог бизнесу MishiPay.
MishiPay позволяет покупателям сканировать и оплачивать покупки с помощью своих смартфонов, вместо того, чтобы тратить время на очереди на кассе. Благодаря технологии MishiPay Scan & Go покупатели могут использовать свой телефон для сканирования штрих-кода на товарах и оплаты их, а затем просто покинуть магазин. Исследования показывают, что очереди в магазинах обходятся мировому сектору розничной торговли примерно в 200 миллиардов долларов в год.
Наша технология опирается на аппаратные возможности устройств, такие как датчики GPS и камеры, которые позволяют пользователям находить магазины с поддержкой MishiPay, сканировать штрих-коды товаров в физическом магазине, а затем оплачивать покупки, используя выбранный ими способ цифровой оплаты. Первоначальные версии нашей технологии Scan & Go представляли собой приложения для iOS и Android, ориентированные на платформы, и ранним пользователям эта технология понравилась. Читайте дальше, чтобы узнать, как переход на PWA увеличил число транзакций в 10 раз и сэкономил 2,5 года очередей!
10 ×
Увеличение транзакций
2,5 года
Очередь сохранена
Испытание
Пользователи находят нашу технологию чрезвычайно полезной при ожидании в очереди или на кассе, поскольку она позволяет им пропустить очередь и обеспечить удобство посещения магазина. Но трудности с загрузкой приложения для Android или iOS заставили пользователей не выбирать нашу технологию, несмотря на ее ценность. Это была растущая проблема для MishiPay, и нам нужно было повысить уровень принятия пользователями за счет более низкого барьера входа.
Решение
Наши усилия по созданию и запуску PWA помогли нам избежать проблем с установкой и побудили новых пользователей опробовать нашу технологию в физическом магазине, миновать очередь и получить беспрепятственный опыт покупок. С момента запуска мы наблюдали резкий рост популярности нашего PWA среди пользователей по сравнению с приложениями для конкретных платформ.
![Транзакции по платформам. ¡ОС: 16397 (3,98%). Android: 13769 (3,34%). Интернет: 382184 (92,68%).](https://web.dev/static/case-studies/mishipay/image/transactions-platform-3f29e33b0839e.png?authuser=5&hl=ru)
Техническое углубление
Поиск магазинов с поддержкой MishiPay
Чтобы включить эту функцию, мы полагаемся на API getCurrentPosition()
вместе с резервным решением на основе 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. Таким образом, они могут избежать ввода веб-адреса mishipay.shop
для доступа к услуге.
Сканирование продуктов
Основной функцией приложения 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');
}
}
В расширенных случаях использования этот слой также выполняет некоторые задачи предварительной обработки, такие как обрезка, вращение или преобразование в оттенки серого. Эти задачи могут быть ресурсоемкими и приводить к тому, что приложение перестанет отвечать на запросы, поскольку сканирование штрих-кода является длительной операцией. С помощью OffscreenCanvas API мы можем переложить задачу, нагружающую ЦП, на веб-работника. На устройствах, поддерживающих аппаратное ускорение графики, WebGL API и его WebGL2RenderingContext
могут оптимизировать выполнение задач предварительной обработки с интенсивным использованием ЦП.
Уровень декодера
Последний уровень — это уровень декодера, который отвечает за декодирование штрих-кодов из кадров, захваченных уровнем обработки. Благодаря API обнаружения формы (который пока доступен не во всех браузерах) браузер сам декодирует штрих-код из ImageBitmapSource
, который может быть элементом img
, элементом image
SVG, элементом 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;
}
}
Для устройств, которые еще не поддерживают API обнаружения формы, нам нужно запасное решение для декодирования штрих-кодов. API обнаружения формы предоставляет метод getSupportedFormats()
, который помогает переключаться между API обнаружения формы и резервным решением.
// Feature detection.
if (!('BarceodeDetector' in window)) {
return;
}
// Check supported barcode formats.
BarcodeDetector.getSupportedFormats()
.then((supportedFormats) => {
supportedFormats.forEach((format) => console.log(format));
});
Резервное решение
Доступно несколько библиотек сканирования с открытым исходным кодом и корпоративных библиотек, которые можно легко интегрировать с любым веб-приложением для реализации сканирования. Вот некоторые библиотеки, которые рекомендует MishiPay.
Все вышеперечисленные библиотеки представляют собой полноценные SDK, составляющие все рассмотренные выше уровни. Они также предоставляют интерфейсы для поддержки различных операций сканирования. В зависимости от форматов штрих-кодов и скорости обнаружения, необходимой для конкретного бизнес-кейса, решение может быть между решениями Wasm и другими решениями. Несмотря на накладные расходы, связанные с необходимостью использования дополнительного ресурса (Wasm) для декодирования штрих-кода, решения Wasm превосходят решения, отличные от Wasm, с точки зрения точности.
Scandit был нашим основным выбором. Он поддерживает все форматы штрих-кодов, необходимые для наших бизнес-приложений; по скорости сканирования он превосходит все доступные библиотеки с открытым исходным кодом.
Будущее сканирования
Как только API определения формы будет полностью поддерживаться всеми основными браузерами, у нас потенциально появится новый HTML-элемент <scanner>
, обладающий возможностями, необходимыми для сканера штрих-кода. Инженеры MishiPay считают, что существует хороший вариант использования функции сканирования штрих-кода в качестве нового элемента HTML из-за растущего числа открытых и лицензированных библиотек, которые обеспечивают такие возможности, как Scan & Go и многие другие.
Заключение
Усталость приложений — это проблема, с которой сталкиваются разработчики, когда их продукты выходят на рынок. Пользователи часто хотят понять ценность, которую дает им приложение, прежде чем загружать его. В магазине, где MishiPay экономит время покупателей и улучшает их качество обслуживания, нелогично ждать загрузки, прежде чем они смогут использовать приложение. Вот здесь и помогает наш PWA. Устранив барьер входа, мы увеличили объем транзакций в 10 раз и позволили нашим пользователям сэкономить 2,5 года ожидания в очереди.
Благодарности
Эта статья была рассмотрена Джо Медли .