├── GoogePay.js └── README.md /GoogePay.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ОПЛАТА GooglePay 3 | * 4 | * Сначала в showPaymentButtons() API GooglePay делает запрос на сервер Google, который отвечает, может ли этот браузер производить оплату. Далее показывается кнопка GooglePay 5 | * При нажатии на кнопку GooglePay API посылает запрос на сервер Google, открывается фрейм с сохранёнными картами, после завершения операции фрейм закрывается 6 | * При успешном ответе делаем запрос в бэк, передаём токен, полученный от Google 7 | * После успешного потверждения от бэка делаем запрос на оплату в наше API 8 | * 9 | * https://developers.google.com/pay/api/web/guides/tutorial , https://developers.google.com/pay/api/web/reference/client , https://developers.google.com/pay/api/web/reference/object 10 | */ 11 | 12 | /** 13 | * ПРОЦЕССИНГ Google Pay 14 | * 15 | * @param options.googlePayPublicKey - приходит от бэка в шаблон 16 | * @param options.googleBaseCardPaymentMethod - устанавливается в шаблоне либо берётся дефолтный 17 | * @param options.merc_id - приходит от бэка в шаблон 18 | * @param options.merc_name - приходит от бэка в шаблон 19 | */ 20 | 21 | const setGoogleBaseCardPaymentMethod = () => { 22 | return { 23 | type: "CARD", 24 | parameters: { 25 | allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"], 26 | allowedCardNetworks: ["AMEX", "DISCOVER", "JCB", "MASTERCARD", "VISA"] 27 | } 28 | } 29 | }; 30 | 31 | const checkParams = (type, options, callbacks) => { 32 | 33 | // общие проверки 34 | if (!options) { 35 | console.log("Параметры для " + type + " отсутствуют"); 36 | return false; 37 | } 38 | if (!callbacks) { 39 | console.log("Коллбэки для GooglePayProcessing отсутствуют"); 40 | return false; 41 | } 42 | if (callbacks && (!callbacks.fail || typeof callbacks.fail !== "function")) { 43 | console.log("Коллбэк fail для " + type + " отсутствует"); 44 | return false; 45 | } 46 | if (callbacks && (!callbacks.success || typeof callbacks.success !== "function")) { 47 | console.log("Коллбэк success для " + type + " отсутствует"); 48 | return false; 49 | } 50 | // дефолтный googleBaseCardPaymentMethod для обеих функций 51 | if (!options.googleBaseCardPaymentMethod) { 52 | options.googleBaseCardPaymentMethod = setGoogleBaseCardPaymentMethod(); 53 | } 54 | 55 | if (type == "GooglePayProcessing") { 56 | // paymentsClient должен быть создан при включении кнопки Google Pay и передан на процессинг 57 | if (!options.paymentsClient) { 58 | console.log("Передайте объект paymentsClient"); 59 | return false; 60 | } 61 | // валюта по умолчанию рубль 62 | if (!options.currency) { 63 | options.currency = "RUB"; 64 | } 65 | 66 | } 67 | if (type == "showGooglePayButton") { 68 | // по умолчанию считаем, что используем код на проде 69 | if (!options.environment) { 70 | options.environment = "PRODUCTION"; 71 | } 72 | if (callbacks && (!callbacks.setPaymentClient || typeof callbacks.setPaymentClient !== "function")) { 73 | console.log("Коллбэк setPaymentClient для " + type + " отсутствует"); 74 | return false; 75 | } 76 | } 77 | 78 | return { 79 | options 80 | } 81 | }; 82 | 83 | export function GooglePayProcessing(options, callbacks) { 84 | 85 | // проверка параметров 86 | const check = checkParams("GooglePayProcessing", options, callbacks); 87 | if (!check) { 88 | return false; 89 | } else { 90 | options = check.options; 91 | } 92 | 93 | const token = { 94 | /* for demo (enviroment TEST): 95 | parameters: { 96 | "protocolVersion": "ECv1", 97 | "publicKey": "your test public key" 98 | } 99 | */ 100 | /* for prod (enviroment PRODUCTION): 101 | parameters: { 102 | "protocolVersion": "ECv1", 103 | "publicKey": params.googlePayPublicKey 104 | } 105 | */ 106 | type: 'DIRECT', 107 | parameters: { 108 | "protocolVersion": "ECv1", 109 | "publicKey": options.googlePayPublicKey 110 | } 111 | }; 112 | 113 | const baseMethod = options.googleBaseCardPaymentMethod; 114 | 115 | const getGooglePaymentDataRequest = () => { 116 | const cardPaymentMethod = Object.assign( 117 | {}, 118 | baseMethod, 119 | { 120 | tokenizationSpecification: token 121 | } 122 | ); 123 | 124 | const paymentDataRequest = { 125 | apiVersion: 2, 126 | apiVersionMinor: 0, 127 | allowedPaymentMethods : [cardPaymentMethod], 128 | /* for demo (enviroment TEST): 129 | merchantInfo : { 130 | merchantId: '12345678901234567890', 131 | merchantName: 'JOHN SMITH' 132 | }, 133 | */ 134 | /* for prod (enviroment PRODUCTION): 135 | merchantInfo : { 136 | merchantId: options.merc_id, 137 | merchantName: options.merc_name 138 | }, 139 | */ 140 | merchantInfo : { 141 | merchantId: options.merc_id, 142 | merchantName: options.merc_name 143 | }, 144 | transactionInfo : { 145 | currencyCode: options.currency, 146 | totalPriceStatus: 'FINAL', 147 | totalPrice: "" + options.sum 148 | } 149 | }; 150 | 151 | return paymentDataRequest; 152 | }; 153 | 154 | // объект уже был создан при проверке возможности оплаты и включении кнопки 155 | const paymentsClient = options.paymentsClient; 156 | const paymentDataRequest = getGooglePaymentDataRequest(); 157 | 158 | // GooglePay API посылает запрос на сервер Google, открывается фрейм с сохранёнными картами, после завершения операции фрейм закрывается 159 | paymentsClient.loadPaymentData(paymentDataRequest) 160 | .then(function(paymentData) { 161 | const googleToken = JSON.parse(paymentData.paymentMethodData.tokenizationData.token); 162 | 163 | // примерный запрос клиент-сервер, обязательно нужно передать googleToken и googlePayPublicKey 164 | $.ajax({ 165 | url: options.request_url + "?form_request_id=" + options.form_request_id, 166 | method: 'POST', 167 | dataType: "json", 168 | data: JSON.stringify({ 169 | googleToken, 170 | pub_key: options.googlePayPublicKey, 171 | link_id: options.link_id, 172 | sum: options.sum, 173 | refill_id: options.refill_id, 174 | apikey: options.apikey, 175 | client_id: options.client_id 176 | }), 177 | 178 | success: (response) => { 179 | if (response.code != undefined && response.code == "0") { 180 | callbacks.success(); 181 | } else { 182 | console.log("Запрос на оплату закончился неудачно"); 183 | callbacks.fail(); 184 | } 185 | }, 186 | error: () => { 187 | console.log("Запрос на оплату закончился неудачно"); 188 | callbacks.fail(); 189 | } 190 | }) 191 | 192 | }) 193 | .catch(function(err) { 194 | // сообщение об ошибке отображается во фрейме оплаты автоматически 195 | console.log("GooglePayProcessing ERROR"); 196 | console.error(err); 197 | }); 198 | } 199 | 200 | /** 201 | * ПОКАЗ КНОПКИ Google Pay 202 | * 203 | * @param options - здесь передаём googleBaseCardPaymentMethod, environment 204 | * @param callbacks - здесь передаём коллбэк успеха, неуспеха, а также сеттер для запоминания экземпляра платёжного клиента 205 | */ 206 | export function showGooglePayButton(options, callbacks) { 207 | // проверка параметров 208 | const check = checkParams("showGooglePayButton", options, callbacks); 209 | if (!check) { 210 | return false; 211 | } else { 212 | options = check.options; 213 | } 214 | 215 | const paymentsClient = new google.payments.api.PaymentsClient({environment: options.environment}); 216 | // в приложении запоминаем экземпляр платёжного клиента, который создало API 217 | callbacks.setPaymentClient(paymentsClient); 218 | const request = { 219 | apiVersion: 2, 220 | apiVersionMinor: 0, 221 | allowedPaymentMethods: [options.googleBaseCardPaymentMethod] 222 | }; 223 | paymentsClient.isReadyToPay(request) 224 | .then(function(response) { 225 | if (response.result) { 226 | callbacks.success(); 227 | return true; 228 | } else { 229 | console.log("Запрос на показ кнопки Google Pay закончился неудачно"); 230 | callbacks.fail(); 231 | } 232 | }) 233 | .catch(function(err) { 234 | console.log("showGooglePayButton ERROR"); 235 | callbacks.fail(); 236 | }); 237 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # googlepayjs 2 | Client JavaScript-module for Googe Pay processing. 3 | Статья на Хабре с пояснениями: https://habr.com/post/433140/ 4 | --------------------------------------------------------------------------------