Как добавить на свой сайт прием оплаты через Google Pay

Вчера узнал, что Goolge Pay теперь можно использовать не только в приложениях, но и на Web-сайтах, и решил разобраться, как это делать. Как выяснилось, с технической точки зрения там все достаточно просто. Подключение состоит из трех этапов:
  1. выбрать платежный шлюз — либо один из тех, которые сотрудничают с Google, либо какой-то сторонний, и получить от него merchantID, и, возможно, другие параметры для подключения. В случае проведения оплат через сторонние шлюзы также потребуется их публичный ключ.
  2. добавить на страницу оплаты контейнер для кнопки оплаты и необходимые скрипты, и проверить работу Google Pay в тестовом режиме,
  3. подать заявку в Google на проверку и подключение сайта. После ее прохождения поменять в скриптах некоторые настройки с тестовых на рабочие.
Рассмотрим подробнее второй этап. Допустим, у нас изначально есть простейшая форма оплаты:

<!DOCTYPE html>
<html lang="ru">
<title>Тестируем Android Pay</title>

<div id="container" style="width: 600px; margin: 10px auto"><form action="#" id="cart" method="post">

 <p>Это форма приема платежа.</p>
 <p>Сумма добровольного пожертвования: <input type="number" value="2.00" id="total" /> руб.</p>
 <p>Существующая кнопка Заказать нужна для примера и ничего не делает</p>
 <button>Заказать</button>
</form>
</div>

Добавим в нее блок div, который будет контейнером для кнопки Google Pay и назовем его gpay_container, а также скрытое поле, в которое будем помещать результат, который вернет нам Google Pay после обработки платежа. Затем подключим с сервера Google скрипт pay.js и добавим еще один скрипт, в котором будут задаваться параметры, необходимые для проведения платежа: merchantID, сумма, типы принимаемых карт и т.п. В этом же скрипте определена функция processPayment, которая вызывается в случае успешного прохождения платежа, и в которую нужно прописать соответствующий обработчик.
Все те места, которые требуют правки вручную, помечены комментариями, начинающимися со звездочек (***).


<!DOCTYPE html>
<html lang="ru">
<title>Тестируем Android Pay</title>

<div id="container" style="width: 600px; margin: 10px auto"><form action="#" id="cart" method="post">

 <p>Это форма приема платежа.</p>
 <p>Сумма добровольного пожертвования: <input type="number" value="2.00" id="total" /> руб.</p>
 <p>Существующая кнопка Заказать нужна для примера и ничего не делает</p>
 <button>Заказать</button>
 <!-- добавляем для приема Google Pay: -->
 <div id="gpay_container"><!-- контейнер для кнопки Google Pay --></div>
 <input type="hidden" name="gpay_data" value="" id="gpay_data" />
<!-- скрытое поле для передачи данных от Google Pay на сервер -->
</form>
</div>

<script async  src="https://pay.google.com/gp/p/js/pay.js"  onload="onGooglePayLoaded()"></script>

<script>
// принимаемые способы оплаты: CARD — обычный ввод данных карты,
// TOKENIZED_CARD — карта, привязанная к Android Pay в телефоне
var allowedPaymentMethods = ['CARD', 'TOKENIZED_CARD'];
// принимаемые типы карт: для России имеет смысл только MASTERCARD и VISA
// МИР и Маэстро не поддерживаются
var allowedCardNetworks = ['MASTERCARD', 'VISA'];

// настройки приема платежей
var tokenizationParameters = {
 tokenizationType: 'PAYMENT_GATEWAY', // *** в случае приема оплаты через один из шлюзов,
// указанных в Google. Если шлюз другой, то нужно использовать DIRECT
 parameters: {
   'gateway': 'example', // ***заменить на название реального шлюза, например,
   'gatewayMerchantId': 'abc123' // ***заменить на идентификатор, выданный ШЛЮЗОМ
 }
}

// Создание объекта PaymentsClient
function getGooglePaymentsClient() {
 return (new google.payments.api.PaymentsClient({environment: 'TEST'}));
// *** TEST заменить на PRODUCTION при начале приема реальных платежей
}

// Обработчик, который вызывается, когда скрипт Google Pay загрузился
// В нем создается объект PaymentsClient и делается запрос
function onGooglePayLoaded() {
 var paymentsClient = getGooglePaymentsClient();
 paymentsClient.isReadyToPay({allowedPaymentMethods: allowedPaymentMethods})
     .then(function(response) {
       if (response.result) {
         addGooglePayButton();
         prefetchGooglePaymentData();
       }
     })
     .catch(function(err) {
       // вывод даных для отладки
       console.error(err);
     });
}

// добавление кнопки Google Pay на страницу
function addGooglePayButton() {
 var button = document.createElement('button');
 button.className = 'google-pay';
 button.type = 'button'; // чтобы кнопка не работала как submit сразу
 button.appendChild(document.createTextNode('Google Pay'));
 button.addEventListener('click', onGooglePaymentButtonClicked);
 document.getElementById('gpay_container').appendChild(button); // тут мы добавляем кнопку
 //в созданный для нее контейнер gpay_container в форме приема платежа
}

// формирование платежных данных
function getGooglePaymentDataConfiguration() {
 return {
   merchantId: '01234567890123456789', // *** MerchantID, полученный непосредственно от GOOGLE после проверки сайта.
  //Не путать с MerchantID, выдаваемым шлюзом!
   paymentMethodTokenizationParameters: tokenizationParameters,
   allowedPaymentMethods: allowedPaymentMethods,
   cardRequirements: {
     allowedCardNetworks: allowedCardNetworks,
     billingAddressRequired : false, // не требовать от пользователя ввода адреса регистрации
   },
   emailRequired : false, // не требовать ввода EMail
   phoneNumberRequired : false // не требовать номера телефона
};
}

// формирование данных о транзакции (сумма и валюта)
function getGoogleTransactionInfo() {
 return {
   currencyCode: 'RUB', // *** код валюты, если у вас не рубли — замените на нужный
   totalPriceStatus: 'FINAL',
  // FINAL означает, что цена не будет изменяться в зависимости от региона доставки и тому подобных параметров
   totalPrice: document.getElementById('total').value // *** сюда подставляется сумма платежа.
  //В нашем примере сумма берется из поля формы, но реально ее должен считать скрипт магазина и подставлять в шаблон
 };
}

// предварительное получение данных для платежа
function prefetchGooglePaymentData() {
 var paymentDataRequest = getGooglePaymentDataConfiguration();
 paymentDataRequest.transactionInfo = {
   totalPriceStatus: 'NOT_CURRENTLY_KNOWN',
   currencyCode: 'RUB'
 };
 var paymentsClient = getGooglePaymentsClient();
 paymentsClient.prefetchPaymentData(paymentDataRequest);
}

// обработчик нажатия кнопки Google Pay
function onGooglePaymentButtonClicked() {
 var paymentDataRequest = getGooglePaymentDataConfiguration();
 paymentDataRequest.transactionInfo = getGoogleTransactionInfo();

 var paymentsClient = getGooglePaymentsClient();
 paymentsClient.loadPaymentData(paymentDataRequest)
     .then(function(paymentData) {
       processPayment(paymentData);
     })
     .catch(function(err) {
       console.error(err); // *** здесь необходимо добавить обработчик ситуаций, когда платеж не прошел.
     });
}

// обработчик данных об успешно прошедшем платеже
function processPayment(paymentData) {
 // *** его содержимое нужно заменить на свое!
 console.log('Платеж прошел!');
 console.log(paymentData);
 // мы для примера просто сохраняем все данные в скрытое поле и отправляем на сервер
 document.getElementById('gpay_data').value=JSON.stringify(paymentData);
 // document.getElementById('cart').submit(); // и отправляем форму на сервер
 // при желании можно было бы сделать это и через AJAX
}

</script>

Что еще нужно иметь в виду при настройке:
  1. сайт обязательно должен работать через https
  2. внешний скрипт pay.js нужно загружать до встроенного в страницу (не очень понятно, чем это вызвано, но иначе у меня просто не появлялась кнопка оплаты)
  3. на данный момент (4 мая 2018 года) сервера с Google Pay часто попадают под блокировки, связанные с Telegram, поэтому лучше сразу принять меры для избежания этих проблем
  4. в скрипте при подключении через шлюз есть два merchantID: один выдается непосредственно Google при проверке сайта, второй — платежным шлюзом. Важно их не перепутать!
  5. на этапе тестирования merchantID можно указывать любой, а в качестве шлюза прописывать example. Этого вполне достаточно для корректной работы.
И не забудьте сделать проверку оплаты на стороне сервера. Ее реализация зависит от конкретного платежного шлюза.
Посмотреть готовый тестовый код можно тут: https://socionics.me/static/gpay.html