3.1.2. Самоучитель по торговым API#

3.1.2.1. Введение#

3.1.2.1.1. О GNU Taler#

GNU Taler - это открытый протокол для электронной платежной системы с эталонной реализацией в свободном программном обеспечении. GNU Taler предлагает безопасную, быструю и простую обработку платежей с использованием хорошо понятных криптографических методов. GNU Taler позволяет клиентам сохранять анонимность, в то же время гарантируя, что торговцы могут быть привлечены к ответственности правительствами. Таким образом, GNU Taler совместим с нормами по борьбе с отмыванием денег (AML) и знанием своего клиента (KYC), а также с нормами по защите данных (например, GDPR).

3.1.2.1.2. Об этом учебнике#

В этом руководстве рассматривается обработка платежей с помощью GNU Taler merchant Backend. Аудитория этого руководства - разработчики торговых предприятий (например, интернет-магазинов), которые работают над интеграцией GNU Taler с фронтендом, ориентированным на клиентов, и бэк-офисом, ориентированным на персонал.

В этой главе объясняются некоторые базовые понятия. Во второй главе вы узнаете, как выполнять основные платежи.

Эта версия учебника содержит примеры для Python3. В ней используется библиотека requests для HTTP-запросов. Версии для других языков/среды также доступны.

Если вы хотите посмотреть на несколько простых, работающих примеров, посмотрите эти:

3.1.2.1.3. Обзор архитектуры#

Программный стек Taler для торговца состоит из следующих основных компонентов:

  • Фронтенд, взаимодействующий с браузером покупателя. Фронтенд позволяет покупателю создать корзину и оформить заказ. После оплаты он запускает соответствующую бизнес-логику для выполнения заказа. Этот компонент не входит в комплект поставки Taler, а предполагается, что он существует у продавца. В этом руководстве описано, как разработать фронтенд Taler.

  • Платежный бэкенд, специфичный для Taler, который облегчает фронтенду обработку финансовых транзакций с помощью Taler. В этом уроке вы будете использовать публичный бэкенд из песочницы. Для производственного использования вы должны либо настроить свой собственный бэкенд, либо попросить другого человека сделать это за вас.

Следующее изображение иллюстрирует различные взаимодействия этих ключевых компонентов:

../../_images/arch-api.png

Бэкэнд обеспечивает поддержку криптографических протоколов, хранит специфическую для Taler финансовую информацию и взаимодействует с биржей GNU Taler через Интернет. Фронтенд получает доступ к бэкенду через RESTful API. В результате фронтенду никогда не приходится напрямую общаться с биржей, а также не приходится иметь дело с конфиденциальными данными. В частности, ключи подписи продавца и информация о банковском счете инкапсулируются в бэкенде Taler.

Некоторые функции бэкэнда («публичный интерфейс») открыты для браузера клиента напрямую. В HTTP API все приватные конечные точки (для бэк-офиса) имеют префикс /private/. В данном руководстве рассматриваются конечные точки /private/. Публичный интерфейс используется непосредственно кошельком и не имеет значения для продавца (кроме того, что API должен быть открыт).

3.1.2.1.4. Бэкэнд публичной песочницы и аутентификация#

Способ аутентификации фронтенда для бэкенда Taler зависит от конфигурации. См. Руководство оператора бэкенда Merchant.

Бэкенд публичной песочницы https://backend.demo.taler.net/instances/sandbox/ использует ключ API в заголовке Авторизация. Значение этого заголовка должно быть Bearer secret-token:sandbox для бэкенда публичной песочницы.

>>> import requests
>>> requests.get("https://backend.demo.taler.net/instances/sandbox/private/orders",
...              headers={"Authorization": "Bearer secret-token:sandbox"})
<Response [200]>

Если возвращается код состояния HTTP, отличный от 200, значит, что-то пошло не так. Вам следует выяснить, в чем проблема, прежде чем продолжать работу над этим уроком.

Бэкэнд песочницы https://backend.demo.taler.net/instances/sandbox/ использует KUDOS в качестве воображаемой валюты. Монеты, деноминированные в KUDOS, могут быть выведены с сайта https://bank.demo.taler.net/.

3.1.2.1.5. Торговые экземпляры#

Один сервер Taler merchant backend может использоваться несколькими мерчантами, которые являются отдельными бизнес-структурами. Каждому из этих отдельных хозяйствующих субъектов присваивается merchant instance, который идентифицируется буквенно-цифровым instance id. Если идентификатор экземпляра не указан, предполагается идентификатор экземпляра admin.

На сайте https://backend.demo.taler.net/ настроены следующие экземпляры торговцев:

Примечание

Это вымышленные торговцы, используемые для наших демонстраций, не связанные с соответствующими проектами и не утвержденные ими официально.

Все конечные точки для экземпляров предлагают один и тот же API. Таким образом, используемый экземпляр просто указывается в базовом URL-адресе бэкенда продавца.

3.1.2.2. Обработка торговых платежей#

3.1.2.2.1. Создание заказа на оплату#

Платежи в Taler осуществляются на основе заказа, который представляет собой машиночитаемое описание бизнес-операции, за которую должен быть произведен платеж. Прежде чем принимать платежи в Taler в качестве продавца, вы должны создать такой заказ.

Это делается путем отправки JSON-объекта в конечную точку API бэкенда /private/orders. Внутри поля order должны быть указаны как минимум следующие поля:

  • сумма: Сумма к оплате, в виде строки в формате CURRENCY:DECIMAL_VALUE, например EUR:10 для 10 евро или KUDOS:1.5 для 1.5 KUDOS.

  • ummary: Человекочитаемое резюме о том, что представляет собой платеж. Резюме должно быть достаточно коротким, чтобы поместиться в заголовки, хотя жестких ограничений нет.

  • fulfillment_url: URL-адрес, который будет отображаться после завершения платежа. Для цифровых товаров это должна быть страница, на которой отображается купленный товар. При успешной оплате кошелек автоматически добавляет order_id в качестве параметра запроса, а также ession_sig для платежей, привязанных к сессии (об этом ниже).

Заказы могут содержать гораздо больше полей, см. раздел Формат заказа Taler. При размещении заказа вы можете указать дополнительные детали, такие как переопределение срока возврата и инструкции по управлению запасами. Они требуются редко и не рассматриваются в этом руководстве; за подробностями обращайтесь к справочному руководству.

Минимальный фрагмент Python для создания заказа будет выглядеть следующим образом:

>>> import requests
>>> body = dict(order=dict(amount="KUDOS:10",
...                        summary="Donation",
...                        fulfillment_url="https://example.com/thanks.html"),
...             create_token=False)
>>> response = requests.post("https://backend.demo.taler.net/instances/sandbox/private/orders",
...               json=body,
...               headers={"Authorization": "Bearer secret-token:sandbox"})
<Response [200]>

Бэкэнд заполнит некоторые детали, отсутствующие в заказе, например, адрес инстанса продавца. Полная информация называется условиями договора.

Примечание

В приведенном выше запросе отключено использование токенов требований путем установки опции create_token в значение false. Если вам нужны токены утверждений, вы должны изменить код для построения URI taler://pay/, приведенного ниже, чтобы включить токен утверждения.

После успешного POST запроса к /private/orders будет возвращен JSON, содержащий только поле order_id со строкой, представляющей ID заказа. Если вы также получите токен утверждения, проверьте, что вы использовали запрос, как описано выше.

Вместе с инстансом торговца, идентификатор заказа однозначно идентифицирует заказ в бэкенде торговца. Используя идентификатор заказа, вы можете тривиально построить соответствующий URI taler://pay/, который должен быть предоставлен кошельку. Пусть example.com - это доменное имя, к которому доступны публичные конечные точки экземпляра. Тогда URI оплаты Taler - это просто taler://pay/example.com/$ORDER_ID/, где $ORDER_ID должен быть заменен на ID возвращенного ордера.

Вы можете поместить URI taler:// в качестве целевой ссылки, чтобы открыть кошелек Taler через схему taler://, или поместить его в QR-код. Однако для веб-магазина проще всего просто перенаправить браузер на страницу https://example.com/orders/$ORDER_ID. На этой странице будет запущен кошелек Taler. Здесь бэкэнд генерирует правильную логику для запуска кошелька, поддерживая различные типы существующих кошельков Taler. Вместо того чтобы создавать указанный выше URL вручную, его также можно получить, проверив статус платежа, как описано в следующем разделе.

При ручном создании этого URL убедитесь, что вы указали токен утверждения (если он не был отключен) и если бэкэнд работает без TLS, используйте taler+http:// (обратите внимание, что последнее поддерживается только кошельками, работающими в режиме отладки).

Примечание

Тривиальный способ получить правильный payment_redirect_url - проверить статус платежа (см. ниже). Так что если вы все еще не знаете, как его построить, вы можете просто попросить бэкенд сделать это за вас. Однако в продакшене вам, вероятно, стоит создать его вручную и избежать лишних запросов к бэкенду.

3.1.2.2.2. Проверка статуса платежа и запрос на оплату#

Учитывая идентификатор заказа, статус платежа можно проверить с помощью конечной точки /private/orders/$ORDER_ID. Если платеж еще не завершен клиентом, /private/orders/$ORDER_ID передаст фронтенду URL (под именем payment_redirect_url), который запустит кошелек клиента для выполнения платежа. По сути, это URL https://example.com/orders/$ORDER_ID, о котором мы говорили выше.

>>> import requests
>>> r = requests.get("https://backend.demo.taler.net/instances/sandbox/private/orders/" + order_id,
...                  headers={"Authorization": "Bearer secret-token:sandbox"})
>>> print(r.json())

Если поле order_status в ответе имеет значение paid, вы не получите payment_redirect_url, а вместо этого получите информацию о статусе платежа, включая:

  • contract_terms: Полные условия контракта по заказу.

  • refunded: true, если за эту покупку был произведен (возможно, частичный) возврат средств.

  • Возвращенная_сумма: Сумма, которая была возвращена

После того как фронтенд подтвердил, что платеж прошел успешно, ему обычно нужно запустить бизнес-логику для продавца, чтобы выполнить его обязательства по договору.

Примечание

Вам не нужно постоянно делать запросы, чтобы заметить изменения в статусе транзакции заказа. Конечные точки поддерживают длительный опрос, просто укажите в параметре запроса timeout_ms, сколько времени вы хотите ждать, пока статус заказа не изменится на paid.

3.1.2.3. Возврат денег#

Возврат в GNU Taler - это способ «отменить» платеж. Он должен быть авторизован продавцом. Возврат может быть осуществлен за любую часть первоначальной суммы, но не может превышать первоначальный платеж. Возврат ограничен по времени и возможен только в то время, когда биржа держит средства для конкретного платежа в условном депонировании. Время, в течение которого возможен возврат средств, можно контролировать, задав в ордере параметр refund_deadline. Значение по умолчанию для этого срока возврата указывается в конфигурации бэкенда продавца.

Фронтенд может дать команду бэкенду продавца разрешить возврат средств, обратившись к конечной точке /private/orders/$ORDER_ID/refund.

JSON-объект запроса на возврат содержит только два поля:

  • Возврат: Сумма, подлежащая возврату. Если предыдущий возврат был разрешен для этого же заказа, новая сумма должна быть больше, иначе операция не имеет эффекта. Значение указывает на общую сумму, подлежащую возврату, не на увеличение суммы возврата.

  • причина: Человекочитаемое обоснование возврата средств. Причина используется только в бэк-офисе и не передается клиенту.

Если запрос успешен (на это указывает HTTP-код состояния 200), ответ содержит taler_refund_uri. Фронтенд должен перенаправить браузер клиента на этот URL, чтобы кошелек смог обработать возврат средств.

Этот фрагмент кода иллюстрирует возврат средств:

>>> import requests
>>> refund_req = dict(refund="KUDOS:10",
...                   reason="Customer did not like the product")
>>> requests.post("https://backend.demo.taler.net/instances/sandbox/private/orders/"
...               + order_id + "/refund", json=refund_req,
...               headers={"Authorization": "Bearer secret-token:sandbox"})
<Response [200]>

Примечание

После предоставления возврата публичная конечная точка https://example.com/orders/$ORDER_ID изменит свое взаимодействие с кошельком с запроса платежа на предложение возврата. Таким образом, фронтенды могут снова перенаправлять браузеры на эту конечную точку. Однако для этого необходимо добавить поле h_contract (?h_contract=$H_CONTRACT), поскольку публичная конечная точка требует его для аутентификации клиента. Требуемое значение $H_CONTRACT возвращается в ответе на возврат в поле h_contract.

3.1.2.4. Обнаружение повторных покупок и URL-адреса для их совершения#

Возможная проблема для продавцов, продающих доступ к цифровым статьям, заключается в том, что покупатель может оплатить статью на одном устройстве, но затем захотеть прочитать ее на другом, возможно, на том, на котором даже не установлен кошелек Taler.

Естественно, в этот момент покупателю сначала все равно будет предложено оплатить статью еще раз. Если клиент откроет ссылку taler:// в кошельке, с которого ранее была произведена оплата статьи (например, отсканировав QR-код на рабочем столе с помощью приложения для Android), кошелек заявит о контракте, обнаружит, что URL-адрес выполнения идентичен тому, за который он уже производил оплату в прошлом, и инициирует перенаправление покупки: Здесь кошелек свяжется с продавцом и повторит предыдущий платеж, только на этот раз используя (текущий) идентификатор сессии браузера (он узнает идентификатор сессии из QR-кода).

Затем бэкэнд продавца обновляет идентификатор сессии существующего заказа до текущего идентификатора сессии браузера. Когда проверяется статус оплаты «нового» неоплаченного заказа (или уже в процессе длительного опроса), бэкэнд обнаруживает, что для идентификатора сессии и * URL-адреса выполнения* браузера существует уже оплаченный контракт. Затем он указывает браузеру немедленно перенаправить его на URL выполнения, где доступна уже оплаченная статья.

Чтобы этот механизм работал так, как задумано, продавцы должны следить за тем, чтобы не использовать один и тот же URL-адрес выполнения для разных товаров или для физических товаров, где покупатели, как ожидается, будут приобретать товар неоднократно. Аналогичным образом, очень важно, чтобы продавцы постоянно использовали один и тот же URL-адрес для одного и того же цифрового продукта, если требуется выявление повторных покупок.

Обратите внимание, что для смены идентификатора сессии на другом устройстве требуется участие кошелька, с которого был совершен платеж, что существенно ограничивает возможность широкого обмена цифровыми покупками. Обнаружение повторных покупок также только выполняется для HTTP(S) URL-адресов выполнения. В частности, это означает, что URI выполнения, такие как taler://fulfillment-success/$MESSAGE, не считаются идентификацией ресурса, за который можно заплатить, и поэтому не должны быть уникальными.

3.1.2.5. Продвинутые темы#

3.1.2.5.1. Платежи, связанные с сеансом#

Иногда проверки того, оплачен ли заказ, недостаточно. Например, при продаже доступа к онлайн-медиа издатель может захотеть, чтобы каждый покупатель платил именно за один и тот же продукт. Taler поддерживает эту модель, позволяя продавцу проверять, имеется ли «квитанция об оплате» на текущем устройстве пользователя. Это не позволяет пользователям легко делиться доступом к медиа, передавая ссылку на страницу выполнения заказа. Конечно, искушенные пользователи могут поделиться и платежными квитанциями, но это не так просто, как поделиться ссылкой, и в этом случае они скорее всего просто поделятся медиа напрямую.

Чтобы использовать эту функцию, торговец должен сначала присвоить текущему браузеру пользователя эфемерный session_id, обычно через сессионный cookie. При выполнении или повторном воспроизведении платежа кошелек получит дополнительную подпись (session_sig). Эта подпись удостоверяет, что кошелек показал платежную квитанцию для соответствующего заказа в текущей сессии.

Платежи с привязкой к сессии запускаются путем передачи параметра session_id` в конечную точку ``/check-payment. После этого кошелек будет перенаправлен на страницу выполнения платежа, но с дополнительным параметром session_sig. Фронтенд может запросить /check-payment как с session_id, так и с ession_sig, чтобы убедиться в правильности подписи.

Идентификатор последней сессии, который был успешно использован для подтверждения того, что платежная квитанция находится в кошельке пользователя, также доступен как last_session_id в ответе на /check-payment.

3.1.2.5.2. Идентификация продукта#

В некоторых ситуациях пользователь может заплатить за какой-либо цифровой товар, но фронтенд не знает точного идентификатора заказа и поэтому не может дать кошельку команду раскрыть имеющуюся платежную квитанцию. Это характерно для простых магазинов без системы авторизации. В этом случае пользователю будет предложено оплатить товар еще раз, даже если он уже его приобрел.

Чтобы кошелек мог найти существующую платежную квитанцию, магазин должен использовать уникальный URL-адрес выполнения для каждого товара. Затем фронтенд должен предоставить дополнительный параметр resource_url для /check-payment. Он должен определять этот уникальный URL-адрес выполнения для продукта. После этого кошелек проверит, оплачивал ли он ранее контракт с тем же resource_url, и если да, то воспроизведет предыдущий платеж.

3.1.2.5.3. Формат заказа Талера#

В заказе Taler можно указать множество подробностей о платеже. В этом разделе подробно описывается каждое из полей.

Финансовые суммы всегда указываются в виде строки в формате CURRENCY:DECIMAL_VALUE.

сумма

Указывает общую сумму, которую клиент должен заплатить продавцу.

максимальная плата

Это максимальная общая сумма комиссии за пополнение счета, которую готов заплатить торговец. Если комиссия за пополнение счета монетами превышает эту сумму, клиент должен включить ее в общую сумму платежа. Комиссия указывается в том же формате, что и для суммы.

max_wire_fee

Максимальная комиссия за перевод средств, принимаемая торговцем (доля клиента, которая делится на коэффициент wire_fee_amortization, и далее уменьшается, если комиссия по депозиту ниже max_fee). По умолчанию, если отсутствует, равна нулю.

амортизация_комиссии

За какое количество транзакций клиента торговец рассчитывает в среднем амортизировать комиссию за перевод? Если комиссия биржи за перевод средств превышает max_wire_fee, разница делится на это число, чтобы вычислить ожидаемый вклад клиента в комиссию за перевод средств. В дальнейшем вклад клиента может быть уменьшен на разницу между max_fee и суммой фактических комиссий по вкладам. Необязательно, значение по умолчанию, если отсутствует, равно 1. Нулевые и отрицательные значения недействительны и также интерпретируются как 1.

pay_url

URL, принимающий платежи. Это URL, на котором кошелек будет размещать монеты.

fulfillment_url

На какой URL-адрес должен перейти кошелек, чтобы получить исполнение, например, HTML или PDF-файл купленной статьи, или систему отслеживания заказов на поставку, или простую человекочитаемую веб-страницу, указывающую на статус контракта.

идентификатор заказа

Буквенно-цифровой идентификатор, свободно определяемый торговцем. Используется продавцом для уникальной идентификации транзакции.

резюме

Краткое, читаемое человеком резюме контракта. Используется при отображении контракта в одной строке, например, в истории операций клиента.

метка времени

Время, в которое было сгенерировано предложение.

срок_оплаты

Временная метка времени, к которому торговец хочет, чтобы биржа окончательно перевела деньги, причитающиеся по этому контракту. По истечении этого срока биржа объединит все депозиты, по которым контракты просрочены, и выполнит по ним один большой проводной платеж. Суммы будут округляться до единицы перевода; если общая сумма все еще меньше единицы перевода, она не будет выплачена.

срок_возврата

Временная метка, до которой торговец готов (и может) произвести возврат средств по контракту с помощью Taler. Обратите внимание, что биржа Taler будет держать платеж в условном депонировании как минимум до этого срока. До этого времени торговец сможет подписать сообщение, чтобы инициировать возврат средств клиенту. По истечении этого времени возврат средств покупателю будет невозможен. Должно быть меньше, чем pay_deadline.

продукция

Массив товаров, которые продаются покупателю. Каждая запись содержит кортеж со следующими значениями:

описание

Описание товара.

количество

Количество отправляемых предметов. Можно указать единицу измерения (например, 1 кг) или просто количество.

цена

Цена за количество единиц данного товара, поставляемого в указанное место_доставки. Обратите внимание, что обычно сумма всех цен должна равняться общей сумме контракта, но она может отличаться из-за скидок или недоступности отдельных цен.

идентификатор товара

Уникальный идентификатор продукта в каталоге продавца. Может быть выбран произвольно, так как имеет значение только для продавца, но должен быть числом в диапазоне [0,2^{51}).

налоги

Карта применимых налогов, которые должен уплатить продавец. Метка - это название налога, например НДС, налог с продаж или подоходный налог, а значение - сумма применимого налога. Обратите внимание, что допускаются произвольные обозначения, если они используются для идентификации применимого налогового режима. Детали могут быть указаны регулирующим органом. Эта информация используется для того, чтобы сообщить клиенту, какие налоги торговец намерен заплатить, и может быть использована клиентом в качестве квитанции. Эта информация также может быть использована при проведении налоговых проверок продавца.

дата доставки

Время, к которому товар должен быть доставлен в место_доставки.

место_доставки

Это должно дать метку в карте локаций, указывающую, куда будет доставлен предмет.

Значения могут быть опущены, если они неприменимы. Например, если покупка касается пакета продуктов, у которых нет индивидуальных цен или идентификаторов продуктов, product_id или price могут не указываться в контракте. Аналогично, для виртуальных продуктов, доставляемых напрямую через URI выполнения, не указывается место_доставки.

торговец
адрес

Это должно дать метку на карте локаций, указывающую, где находится торговец.

имя

Это должно быть человекочитаемое название торгового предприятия.

юрисдикция

Это должно привести к появлению метки в карте локаций, указывающей юрисдикцию, под которой будет рассматриваться этот контракт.

места

Ассоциативная карта мест, используемых в договоре. Метки для местоположений в этой карте можно выбирать произвольно и использовать всякий раз, когда местоположение требуется в других частях контракта. Таким образом, если одно и то же место требуется много раз (например, рабочий адрес клиента или продавца), его нужно указать (и передать) только один раз, а в остальных случаях на него можно ссылаться через метку. Неполный список атрибутов местоположения выглядит следующим образом:

имя

Имя получателя для доставки, либо имя компании, либо имя человека.

страна

Название страны доставки, указанное на почтовом пакете, например, «Франция».

государство

Название штата, в который осуществляется доставка, как указано на почтовом пакете, например, «NY».

регион

Название региона доставки, указанное на почтовом отправлении.

Провинции

Название провинции, в которую осуществляется доставка, как указано на почтовом пакете.

город

Название города для доставки, как указано на почтовом пакете.

почтовый индекс

Почтовый индекс доставки, указанный на почтовой посылке.

улица

Название улицы для доставки, как на почтовом пакете.

номер_улицы

Номер улицы (номер дома) для доставки, указанный на почтовом пакете.

Примечание

Локации не обязаны указывать все эти поля, также допускается наличие дополнительных полей. Рендеры контрактов должны отображать как минимум перечисленные выше поля, а поля, которые они не понимают, должны отображать в виде списка ключей-значений.