Webhooks для платежей

Обновление информации о транзакциях в режиме реального времени

Webhooks для платежей (ранее уведомления в режиме реального времени) — это очень полезный метод, который позволяет информировать вас об изменениях в заказах, сделанных через платежи на Facebook в вашем приложении.

Обзор

Webhooks — это система обмена информацией между Facebook и вашим сервером. Она работает по подписке. Ваше приложение подписывается на получение обновлений от Facebook через определенную конечную точку HTTPS. Когда в заказе, сделанном в вашем приложении, происходят изменения, мы отправляем запрос HTTPS POST к этой конечной точке, чтобы уведомить сервер об изменениях.

Обновления отправляются на сервер в трех основных ситуациях:

Подписка на Webhooks

Чтобы подписаться на Webhooks для платежей, создайте общедоступный URL конечной точки, получающей запросы HTTPS GET (для подтверждения подписки) и POST (для запросов на изменение данных). Структура запросов обоих типов показана ниже. Затем настройте подписку для объекта payment в приложении. Это можно сделать двумя способами:

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

Подписка через Панель приложений

Проще всего настроить получение обновлений Webhooks для приложения через панель "Платежи" на Панели приложений. Найдите приложение на панели и откройте вкладку Payments (Платежи). Раздел Webhooks находится сразу под разделом "Настройки" компании.

Webhooks для платежей

На экране появится информация о статусе подписки приложения (вне зависимости от того, было ли приложение подписано через Панель приложений или через API). Здесь также можно изменить URL обратного вызова подписки и протестировать его.

В поле "Обратный вызов" нужно указать действующую и общедоступную конечную точку сервера. Мы будем использовать этот адрес, чтобы подтвердить подписку и отправлять обновления. Он должен отвечать так, как описано в разделе о сервере обратных вызовов.

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

Проверка настроек

Перед сохранением подписки проверьте настройки обратных вызовов. При этом к конечной точке будет отправлен запрос подтверждения GET с параметрами hub.mode, hub.challenge и hub.verify_token. Это позволит убедиться, что вы правильно обрабатываете эти параметры. Например, конечная точка должна возвращать полученное от Facebook значение hub.challenge:

Проверка настроек

Указав данные о подписке, нажмите "Сохранить изменения" внизу страницы. Чтобы отредактировать подписку, измените содержимое полей, повторите проверку и сохраните форму.

Подписка через API Graph

Настроить подписку также можно программным способом через API Graph. Вам потребуется маркер доступа приложения access token, который можно получить с помощью инструмента маркеров доступа или конечной точки /oauth API Graph.

API подписки доступен на конечной точке https://graph.facebook.com/[APP_ID]/subscriptions.

С его помощью можно выполнять три действия:

  • добавлять или изменять подписку (отправив запрос HTTPS POST);
  • получать список существующих подписок (отправив запрос HTTPS GET).

Добавление и изменение подписок

Чтобы настроить подписку, отправьте запрос POST с перечисленными далее параметрами. Обратите внимание: они соответствуют полям в форме, показанной выше.

  • object — как и выше, это тип объекта, о котором вы хотите получать уведомления. Задайте значение payments.
  • fields — список разделенных запятыми свойств типа объекта, об изменениях в котором вы хотите получать уведомления. Задайте значения actions (действия) и disputes (споры).
  • callback_url — действующая общедоступная конечная точка сервера.
  • verify_token — произвольная строка. Отправляется на конечную точку при подтверждении подписки.

Получив этот запрос (как и в случае с формой выше), мы отправляем запрос GET к вашему обратному вызову, чтобы убедиться, что он действителен и может получать обновления. В частности, конечная точка должна возвращать полученное от Facebook значение hub.challenge.

Обратите внимание: у приложения может быть только одна подписка на каждый тип объекта. Если подписка на определенный тип объекта уже существует, новые данные заменят существующие.

Список подписок

При отправке запроса HTTP GET к API подписки возвращается список подписок в кодировке JSON. Пример:

[
  {
    "object": "payments",
    "callback_url": "https://www.friendsmash.com/rtu.php",
    "fields": ["actions", "disputes"],
    "active": true
  }
]

Чтобы непосредственно поэкспериментировать с API, воспользуйтесь Graph API Explorer (не забудьте использовать маркер доступа своего приложения).

Сервер обратных вызовов

Ваш сервер обратных вызовов должен обрабатывать два вида запросов. Чтобы мы могли отправлять эти запросы, сервер должен находиться на общедоступном URL.

Подтверждение подписки

Когда вы добавляете или изменяете подписку, сервер Facebook отправляет один запрос HTTPS GET к вашему URL обратного вызова. К этому URL обратного вызова добавляется строка запроса со следующими параметрами:

Параметр Описание

hub.mode

В этом параметре передается строка subscribe.

hub.challenge

Случайная строка.

hub.verify_token

Значение verify_token, указанное при создании подписки.

Конечная точка должна в первую очередь подтвердить hub.verify_token. Это нужно, чтобы сообщить серверу, что запрос отправлен Facebook и связан с только что настроенной подпиской.

Затем конечная точка должна отправить только значение hub.challenge: таким образом Facebook узнает, что сервер может принимать обратные вызовы и опасность атак типа "отказ в обслуживании" (DDoS) устраняется.

Примечание для разработчиков на PHP. На языке PHP точки и пробелы в именах параметров запроса автоматически преобразуются в знаки подчеркивания. Если ваша конечная точка обратных вызовов написана на этом языке, обращайтесь к параметрам как $_GET['hub_mode'], $_GET['hub_challenge'] и $_GET['hub_verify_token']. Подробнее см. в этом примечании в руководстве по языку PHP.

Получение обновлений

После успешной подписки мы отправляем к конечной точке вашего сервера запрос HTTPS POST каждый раз, когда происходят какие-либо изменения (в выбранных полях или подключениях). На этот запрос нужно ответить кодом HTTP 200.

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

Запрос будет иметь тип контента application/json, а тело будет состоять из строки в кодировке JSON, содержащей обновления.

Примечание для разработчиков на PHP. Чтобы получить закодированные данные на PHP, используйте этот код:

$data = file_get_contents("php://input");
$json = json_decode($data);`

Примечание. Параметры hub.mode, hub.challenge и hub.verify_token не отправляются заново после подтверждения подписки.

Вот пример стандартного обратного вызова, выполненного для подписки на объект payments:

{
  "object": "payments",
  "entry": [
    {
      "id": "296989303750203",
      "time": 1347996346,
      "changed_fields": [
        "actions"
      ]
    }
  ]
}

Примечание. Обновления Webhook уведомляют вас только о том, что для конкретного платежа, определяемого по полю id, внесены изменения. Получив обновление, отправьте запрос к API Graph, чтобы получить информацию о транзакции и правильно обработать ее.

Примечание. Вызовы Webhook для объектов других типов можно объединить в пакеты, однако обновления о платежах никогда не объединяются.

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

В случае сбоя отправки обновления на сервер мы сразу же повторим попытку. Затем в течение следующих 24 часов мы отправим обновления ещё несколько раз, постепенно увеличивая интервалы времени между попытками.

В каждом запросе мы отправляем заголовок HTTP X-Hub-Signature-256, содержащий подпись полезной нагрузки запроса, хэшированную с применением алгоритма SHA256 (в качестве ключа используется секрет приложения). Подпись содержит префикс sha256=. Конечная точка обратного вызова может использовать эту подпись, чтобы подтвердить целостность полезных данных и их источник.

Ответ на обновления

Получив обновление, отправьте запрос к API Graph и укажите поле id, чтобы получить информацию о новом статусе транзакции. Затем выполните действия в зависимости от статуса.

В следующих разделах перечислены все возможные изменения статуса, из-за которых отправляются обновления. В целом они делятся на:

  • Изменения массива actions (действий). Они происходят, когда платеж выполняется асинхронно, отправлен возврат платежа (вами или Facebook) или транзакция была оспорена.
  • Изменения массива disputes (споров). Они происходят, когда потребитель создает спор по заказу.

Действия

Каждый объект payment содержит массив actions. В нем указана история изменений состояния транзакции. У каждого элемента массива actions есть свойство type, которое описывает тип произошедшего действия. Свойство type может иметь одно из следующих значений: charge, refund, chargeback, chargeback_reversal и decline. Подробное описание этих значений см. в этом разделе.

Пример ответа API Graph на объект платежа со связанными действиями:

{
   "id": "3603105474213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      },
      {
         "type": "refund",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-23T21:18:54+0000",
         "time_updated": "2013-03-23T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.00"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,}`

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

Списание средств

Изначально все заказы содержат элемент списания со статусом "status": "initiated". Если платеж запущен (имеет статус "initiated"), это означает, что он ещё не выполнен. Мы не будем отправлять обновления для платежей в этом состоянии.

Когда платеж будет выполнен, "status": "initiated" изменится на "status": "completed" и мы отправим обновление. Получив обновление, проверьте данные о платежах и определите, новая это транзакция или существующая. После этого выполните соответствующие действия:

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

Вы также будете получать уведомления о платежах со статусом "status": "failed". Их не нужно выполнять.

Возврат

Каждый раз при создании возврата с помощью API Graph вы будете получать обновление. Как и в случае типа "type": "charge", у возврата могут быть разные статусы. Важно: возврат может завершиться ошибкой (обычно из-за проблем с обработкой или подключением). В этом случае вам следует отправить возврат повторно.

Оспаривание транзакции, отмена оспаривания и отклонение транзакции

Если произошло оспаривание транзакции, отмена оспаривания или отклонение транзакции, вы получите уведомление, как и в случае с возвратом. При этом в массив действий в ответе API Graph для платежа будет добавлен соответствующий объект.

Споры

При создании спора мы также отправим обновление. В этом случае в объекте payment будет новый массив "disputes". Он будет содержать время создания спора, причину, по которой клиент создал спор, и электронный адрес клиента, по которому вы можете связаться с ним и разрешить спор.

Пример полного ответа API Graph на оспоренную транзакцию:

{
   "id": "990361254213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.99"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,
   "disputes": [
      {
         "user_comment": "I didn't receive my item! I want a refund, please!",
         "time_created": "2013-03-24T18:21:02+0000",
         "user_email": "email\u0040domain.com",
         "status": "resolved", 
         "reason": "refunded_in_cash"
      }
   ]
}

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