Текущая версия протокола: 0.4.0
- Status of this document
- MMP
- Usage
- MMP-file
- Aggregation of MMP-feeds
- How to become a merchant
- Checkout page query parameters
- Callback to user
Данный документ является черновиком протокола и находится в активной разработке, любые части документа могут быть изменены в будущем. Документ предназначен для ознакомления будущими мерчантами и сбора мнений от сообщества. Дискуссию по документу предпочтительно вести в GitHub-issues или в чате @MinterDevChat
Протокол MMP позволяет любому мерчанту подключить по единому стандарту свой фид цифровых товаров к кошельку wallet.minter.org или любым другим приложениям, поддерживающим этот протокол. Подключенные мерчанты имеют возможность предоставлять информацию о своих товарах, их параметрах и наличии, а также совершать продажи и обновлять статусы.
Minter Merchants Protocol подразумевает создание и последующее обновление MMP-файла, в котором содержится вся необходимая информация о мерчанте и его товарах, представленная в унифицированном стандарте.
Агрегатор собирает предложения различных мерчантов через их MMP-фиды. Агрегатор раз в 10 минут запрашивает каждый фид, актуализируя информацию о ценах и наличии товаров.
- попадает на страницу со списком офферов
- выбирает товар,
- нажимает "перейти к оплате",
- генерируется чек для мерчанта, он действителен до блока +60 от текущего (≈5 минут).
- юзер вместе с чеком перенаправляется к мерчанту на страницу оплаты, где видит информацию о магазине и товаре.
- юзер нажимает "оплатить" (кнопка оплаты доступны после проверки баланса адреса и наличия товара).
- после оплаты юзер попадает на уникальную страницу с информацией о получении товара
После попадания пользователя в магазин на страницу оплаты, мерчант получает данные о заказе из query параметров: id оффера, чек и пароль для обналичивания, публичный ключ для шифрования пейлоада.
Мерчант проверяет что у него есть достаточное количество товара, проверяет что в чеке находится достаточное количество средств, кнопка "оплатить" становится активной, юзер на неё нажимает, мерчант обналичивает чек.
После обналичивания чека мерчант генерирует для пользователя уникальную ссылку на страницу на своём сайте и сразу перенаправляет его на неё. Там пользователь сможет просмотреть информацию о получении своего заказа: статус заказ (готов, в обработке), сам товар, если он быть отгружен в электронном виде, например, redeem code. На этой странице пользователь может произвести какие-то дополнительные действия, если требуются, чтобы получить свой товар: согласовать доставку, связаться с техподдержкой и т. д.
После выполнения заказа мерчант отправляет уведомление в виде пустой транзакции на адрес пользователя с пейлоадом, содержащим информацию о заказе, чтобы история о заказе была всегда доступна юзеру, и чтобы он понимал, в какой момент заказ был исполнен. Пейлоад должен быть зашифрован публичным ключом юзера. Транзакцию нужно отправлять с того же адреса, с которого был обналичен чек, уведомления с других адресов не будут отображаться у юзера, чтобы избежать спама.
Предоставляется мерчантом в формате .json
Должен обновляться по мере изменения информации о товарах, либо раз в 5-10 минут, при отсутствии изменений. Фиды возрастом больше 10 минут не будут обрабатывать агрегатором и соответственно не будут отображены пользователю.
Версия должна соответствовать последней версии протокола, фиды с устаревшими версиями не будут обрабатываться агрегатором.
Допустимые кодировки MMP-файла: UTF-8
В качестве разделителя целой и дробной частей любых чисел используется только точка.
{
"protocol": "mmp",
"version": "1.0.0",
"timestamp": "2020-11-10T15:03:07.0196Z",
"provider": {
"name": "Minter Push",
"slug": "minter-push",
"checkout_url": "https://minterpush.com/mmp-checkout",
"logo": "https://minterpush.com/logo.png",
"support": "https://minterpush.com/support",
"legal": [
{
"title": "Privacy policy",
"url": "https://minterpush.com/privacy"
},
{
"title": "Terms of service",
"url": "https://minterpush.com/terms"
}
],
"offer_list": [{
"id": "0c765fbf-554f-4af0-9463-a348a797c5ea",
"name": "Product",
"vendor": "Apple",
"category": "Games",
"url": "https://...",
"picture": "https://...",
"short_description": "about",
"description": "about",
"price_bip": "100",
"quantity": 1000,
"field_list": [
{
"role": "quantity",
"name": "Amount",
"description": "Amount to top up the phone"
},
{
"role": "phone",
"name": "Mobile phone number",
"description": "Use international phone format"
}
]
}]
}
}
version
: версия MMP протоколаtimestamp
: должен соответствовать дате и времени генерации MMP-файла на стороне провайдера. Дата должна иметь формат ISO 8601:YYYY-MM-DDThh:mm:ss[.SSS]Z
provider
: информация о провайдере, который предлагает различные товары.
provider
name
: название провайдераlogo
: ссылка на логотип в формате .SVG, .PNG, .JPGslug
: идентификатор провайдера, будет использоваться при формировании URL, допустимые символы regex:/[a-z0-9-]/
url
: URL главной страницыsupport
: URL ссылка на службу поддержкиlegal
: массив объектов, каждый из которых представляет собой юридические документ, с полямиtitle
иurl
checkout_url
: страница оплаты, куда будет перенаправляться юзер для, данные заказа будут переданы в query параметрах,offer_list
: информация о предложениях/товарах магазина (пример: Apple iTunes Gift Card). Каждый товар является элементом массива
provider.offer_list[]
id
: идентификатор товара внутри магазинаname
: название товараvendor
: название производителяcategory
: категория товара, доступны для выбора "Games", "Phone", "Gift cards", остальные значения будут попадать в "Other"url
: ссылка на страницу товара в магазинеpicture
: URL ссылка на картинку товараshort_description
: короткое описание товара, без HTML тэговdescription
: полное описание товара, без HTML тэгов, возможны переносы строкиprice_bip
: стоимость товара в эквиваленте BIPquantity
: количество товара;1234
- число, сколько максимум пользователь может купить;0
— out of stock;-1
— не ограничено, например, при пополнении телефонаfield_list
: Список полей, для заполнения юзером, например, номер телефона, на котором нужно пополнять баланс
provider.offer_list[].field_list[]
role
: роль, это значение будет использоваться для передачи в query параметрах на страницу оплаты, доступные значенияquantity
- целое количество товара,phone
- номер телефона в международном форматеname
: Заголовок поля отображаемый юзеруdescription
: Описание, подсказка отображаемая юзеру
field_list[].role == 'quantity'
Целое количество товара.
Будет отображаться только для бесконечных товаров, у которых задано количество "quantity": -1
. Если количество товара конечное, то для его выбора будут использоваться стрелочки вверх/вниз и настройка полей "field_list": [{"role": "quantity"}]
будет игнорироваться.
field_list[].role == 'phone'
Номер телефона в международном формате.
Используется только для указания номера для пополнения баланса.
Нельзя использовать для получения контактного номера для связи с пользователем.
field_list[].role == 'username'
Аккаунт пользователя. Используется для начисления средств по имени пользователя.
Агрегатор собирает фиды у подтверждённых мерчантов из списка //@TODO ссылка на гитхаб
При агрегации фидов нужно брать только те, где:
- таймстамп фида не старше 10 минут
- версия протокола не является устаревшей
Агрегатор предоставляет данные в следующем формате:
{
"version": "1.0.0",
"timestamp": "2020-11-10T15:03:07.0196Z",
"provider_list": [{}, {}]
}
version
версия MMP протоколаtimestamp
должен соответствовать дате и времени сбора MMP-файлов. Дата должна иметь формат ISO 8601:YYYY-MM-DDThh:mm:ss[.SSS]Z
provider_list
: массив провайдеров, каждый из которых получен из поляprovider
в MMP-файле мерчанта
Для начала разработки и тестирования своего мерчанта мы подготовили тестового аггрегатора и тестовый кошелёк. https://minter-wallet.kubernetes.icu (пока что логин работает только через Google) https://mma-api.kubernetes.icu/api/v1/offers
Чтобы добавить своего мерчанта в каталог, нужно сделать HTTP запрос с указанием MMP-файла
curl -X POST --location "https://mma-api.kubernetes.icu/api/v1/merchant" -H "Content-Type: application/json" -d "{\"feed_url\": \"https://example.com/mmp_feed\" }"
В репозитории https://github.com/MinterTeam/minter-merchants-protocol будет собиратся список проверенных мерчантов, каждый мерчант должен будет сделать пулл-реквест, чтобы добавить свой фид
merchant-list.json
{
"version": "1.0.0",
"feed_list": [
{
"name": "Minter Push",
"feed": "https://…",
"homepage": "https://",
"email": "contact@minterpush.com",
"description": "",
"company_register_number": "",
"company_extract": ""
}
]
}
homepage
-email
- адрес для связиdescription
- описание, какие услуги предоставляются
Данные предоставляются, если деятельность ведётся от юридического лица
company_register_number
- регистрационный номер компанииcompany_extract
- публичные данные компании,
Страница на сайте мерчанта, на которой он сможет обналичить чек от пользователя. Страница должны быть выполнена в дизайне Minter. Готовый HTML+CSS шаблон
Пользователь попадает на эту страницу редиректом из кошелька, данные о заказе передаются в query параметрах:
check
: "Mc..." - чек для оплаты товараpassword
: "checkpassword" - пароль для обналичивания чекаpublic_key
: "0x…" - публичный ключ пользователя для шифрования пейлоадаoffer_id
: "0c765fbf-554f-4af0-9463-a348a797c5ea" - идентификатор оффераquantity
: "1" - количество товараphone
: "79991231234" - опциональное поле, передаётся если в оффере был указан"field_list": [ {"role": "phone"} ]
Пример перехода на страницу оплаты
https://minterpush.com/mmp-checkout?offer_id=123&quantity=1&check=Mcf89f8869366a795a58372d01830b995a80881bc16d674ec8000080b8416a842d9f3a2e115683191f170f2a044e6a1ab1962afa8ec119a9ced8b9637c0b139316ccf3fb8130b98f50a4b8f1ddc2a1c08f7921a1a79b89e20685d89818a3011ca0ef93008572a207a0d11c6fbae3364ec2f44683af239014073348c3c97b4e4eb9a031c6c5297656c43916e7e3730bcb5fa70459dd626a3ddb9174e9e58955204c57&password=fcViycYgwea4m-iz&public_key=0x66c808e6b5be6d6620934bc6ffa2b8b47f9786c002bfb06d53a0c27535641a5d1
Мерчант посылает уведомление после завершения обработки заказа.
Уведомления посылаются в виде пустой транзакции на адрес пользователя, сообщение будет находиться в транзакции в поле payload
и будет зашифровано публичным ключом пользователя.
Транзакцию нужно отправлять с того же адреса, с которого был обналичен чек, уведомления с других адресов не будут отображаться у юзера, чтобы избежать спама.
формат:
mmp{{version}} {{merchant_public_key}}{{encrypted_data}}
пример:
mmp1.0.0 ASdasdASDASdasd...
mmp{{version}}
- заголовок с версией протокола, отделяется пробелом от бинарных данныеmerchant_public_key
- публичный ключ мерчанта для расшифровки сообщенияencrypted_data
- зашифрованный JSON5 (чтобы не нужно было название полей оборачивать кавычками, тем самым экономим байты в пейлоаде) с полямиu
,n
,m
,s
{
u: "https://example.com/unique-order-url",
n: "Offer name",
m: "Your WorldOfWarcraft account NagibatorPWNZ is fulfilled with 1000 Gold",
s: "YOUR_REDEEM_CODE"
}
u
- URL, обязательное поле, уникальная ссылка на сайт мерчанта, где юзер может может посмотреть информацию о заказе и получить свой товар
n
- name, название товара
m
- message, обязательное поле, сообщение пользователю
s
- shipment, опциональное поле, в нём содержится сам товар, используется, если товар является, например, подарочным купоном в виде строки символов
Сообщение в payload
'e транзакции шифруется публичным ключом пользователя.
Будет использоваться гибридная схема шифрования на основе публичного ключа ECIES на кривой secp256k1. Кривая выбрана для унификации кода с Minter и Ethereum. Без HMAC [1].
- мерчант получает от юзера compressed публичный ключ длиной 33 байта, если нужно, восстанавливает из него uncompressed (65 байт)
- для каждого сообщения генерирует свою новую пару приватный-публичный ключ. Внимание: нельзя переиспользовать приватный ключ для шифрования, т.к. это может позволить злоумышленнику дешифровать исходное сообщение
- использует ECDH для получения общего секрета (shared secret, 65 байт) из публичного ключа пользователя и своего сгенерированного приватного ключа
- из общего секрета длиной 65 байт вычленяет Х координату (Px, 32 байта) получает секретный ключ для шифрования с помощью HKDF хэширования [2]. В качестве функция хэширования используется SHA512. Из результата SHA512 берётся первые 32 байта.
- зашифровывает сообщение секретным ключом с использованием симметричного шифрования AES256 CBC, в качестве вектора инициализации IV будет использована версия mmp протокола, например "mmp1.0.0"
- отправляет пользователю свой сжатый (compressed) публичный ключ и зашифрованное сообщение, итоговый пейлоад будет состоять из следующих частей:
- префикса с версией протокола "mmpX.X.X" (который так же является вектором инициализации), 8+ байт [3]
- пробела, отделяющего префикс от бинарных данных, 1 байт
- сжатого публичного ключа отправителя, 33 байт
- зашифрованного текста
Заметки
- [1] В данной схеме HMAC не используется, т.к. наличие транзакций в блокчейне с нужного адреса уже является авторизацией
- [2] Общий секрет нельзя использовать напрямую, т.к. он недостаточно равномерно распределён. И поэтому нужно брать хэш от него.
- [3] Версия может занимать больше 8 байт, например mmp12.34.567 - 12 байт
Реализация MMP ECIES на JavaScript github.com/MinterTeam/mmp-ecies-js