В данном примере рассматривается процесс формирования ссылки на оплату, исходя из предоставленных параметров. При переходе по данной ссылке плательщик будет перенаправлен на страницу оплаты для завершения транзакции.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
import java.net.URLEncoder; import java.net.HttpURLConnection; import java.net.URL; import java.util.*; import java.io.*; import java.util.Base64; import org.json.JSONArray; import org.json.JSONObject; public class PaykeeperIntegration { public static void main(String[] args) throws Exception { // Доступы к Paykeeper String server = "https://xxxxxxxxxx.server.paykeeper.ru"; String user = "admin"; String password = "xxxxxxx"; // Данные клиента String email = ""; String phone = ""; // Номер заказа int orderId = 9999; // Массив товаров в заказе List<Map<String, Object>> basket = new ArrayList<>(); basket.add(Map.of( "name", "Тортилья, 20 см, 756 г", "count", 5, "price", 381.00 )); // Собираем массив товаров для Paykeeper double total = 0; JSONArray prods = new JSONArray(); for (Map<String, Object> row : basket) { double price = (double) row.get("price"); int count = (int) row.get("count"); double sum = price * count; JSONObject product = new JSONObject(); product.put("name", row.get("name")); product.put("price", price); product.put("quantity", count); product.put("sum", sum); product.put("tax", "none"); product.put("item_type", "goods"); prods.put(product); total += sum; } // Доставка JSONObject delivery = new JSONObject(); delivery.put("name", "Доставка"); delivery.put("price", 300); delivery.put("quantity", 1); delivery.put("sum", 300); delivery.put("tax", "none"); delivery.put("item_type", "service"); prods.put(delivery); total += 300; // Авторизация в Paykeeper String credentials = user + ":" + password; String base64Credentials = Base64.getEncoder().encodeToString(credentials.getBytes()); Map<String, String> headers = Map.of( "Content-Type", "application/x-www-form-urlencoded", "Authorization", "Basic " + base64Credentials ); // Получение токена String tokenUrl = server + "/info/settings/token/"; JSONObject tokenResponse = sendRequest(tokenUrl, "GET", headers, null); if (tokenResponse.has("token")) { String token = tokenResponse.getString("token"); // Готовим запрос на получение счета JSONObject paymentData = new JSONObject(); paymentData.put("pay_amount", total); paymentData.put("orderid", orderId); paymentData.put("service_name", ";PKC|" + prods.toString() + "|"); paymentData.put("client_email", email); paymentData.put("client_phone", phone); paymentData.put("token", token); // Преобразование JSON в формат URL-encoded String requestBody = jsonToUrlEncoded(paymentData); String invoiceUrl = server + "/change/invoice/preview/"; JSONObject invoiceResponse = sendRequest(invoiceUrl, "POST", headers, requestBody); if (invoiceResponse.has("invoice_id")) { String invoiceId = invoiceResponse.getString("invoice_id"); // Сохранение invoice_id в заказе (здесь только выводим на консоль) System.out.println("Invoice ID: " + invoiceId); // Редирект на форму оплаты String link = server + "/bill/" + invoiceId + "/"; System.out.println("Перейдите по ссылке: " + link); } else { System.out.println(invoiceResponse.getString("msg")); } } else { System.out.println("Не удалось получить токен."); } } private static JSONObject sendRequest(String urlString, String method, Map<String, String> headers, String body) throws Exception { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod(method); // Устанавливаем заголовки for (Map.Entry<String, String> header : headers.entrySet()) { connection.setRequestProperty(header.getKey(), header.getValue()); } if (body != null) { connection.setDoOutput(true); try (OutputStream os = connection.getOutputStream()) { os.write(body.getBytes("UTF-8")); // Используем кодировку UTF-8 для отправки os.flush(); } } int responseCode = connection.getResponseCode(); if (responseCode >= 200 && responseCode < 300) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"))) { StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } return new JSONObject(response.toString()); } } else { throw new IOException("HTTP Error: " + responseCode); } } private static String jsonToUrlEncoded(JSONObject json) throws UnsupportedEncodingException { StringBuilder result = new StringBuilder(); for (String key : json.keySet()) { if (result.length() > 0) { result.append("&"); } result.append(URLEncoder.encode(key, "UTF-8")); result.append("="); result.append(URLEncoder.encode(json.get(key).toString(), "UTF-8")); } return result.toString(); } } |
После перехода по ссылке link пользователь будет направлен на страницу оплаты.
На языке Java обработку запросов от PayKeeper об успешных платежах можно реализовать следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class PaymentNotificationHandler { // Секретный ключ private static final String SECRET = "********"; public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { // Фиксируем время запроса String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // Получаем параметры из запроса Map<String, String[]> parameterMap = request.getParameterMap(); String id = getParam(parameterMap, "id"); // Например 123 String sum = getParam(parameterMap, "sum"); // Например 1500.00 String clientId = getParam(parameterMap, "clientid"); // Например Иванов Иван String orderId = getParam(parameterMap, "orderid"); // Например 456 String key = getParam(parameterMap, "key"); // Например 2ba28d39e3836f4cc1bd96559417b4fb // Проверка обязательных параметров if (id == null || sum == null || key == null) { // Устанавливаем статус 400 (Bad Request) и отправляем сообщение об ошибке response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.setContentType("text/html; charset=UTF-8"); // Устанавливаем тип содержимого response.getWriter().write("Error! Missing or invalid parameters!"); // Сообщение об ошибке return; // Завершаем выполнение метода, чтобы не продолжать обработку } // Необязательные параметры (можно использовать аналогично) String serviceName = getParam(parameterMap, "service_name"); String clientEmail = getParam(parameterMap, "client_email"); String clientPhone = getParam(parameterMap, "client_phone"); String psId = getParam(parameterMap, "ps_id"); String batchDate = getParam(parameterMap, "batch_date"); String fopReceiptKey = getParam(parameterMap, "fop_receipt_key"); String bankId = getParam(parameterMap, "bank_id"); String cardNumber = getParam(parameterMap, "card_number"); String cardHolder = getParam(parameterMap, "card_holder"); String cardExpiry = getParam(parameterMap, "card_expiry"); // Устанавливаем тип содержимого ответа response.setContentType("text/html; charset=UTF-8"); // Устанавливаем статус 200 (OK) при успешной обработке response.setStatus(HttpServletResponse.SC_OK); // Вычисляем MD5-хэш String md5Hash = md5(id + sum + clientId + orderId + SECRET); if (md5Hash.equals(key)) { // Генерируем ответ String hash = md5(id + SECRET); String ret = "OK " + hash; // Отправляем ответ response.getWriter().write(ret); // Запись лога String logMessage = String.format("%s Ret=%s id=%s sum=%s key=%s, my key is %s%n", time, ret, id, sum, key, md5Hash); writeLog("/tmp/paykeeperlog.txt", logMessage); } else { // Возвращаем ошибочный ответ response.getWriter().write("Invalid key"); } } // Получение параметра с проверкой на null private String getParam(Map<String, String[]> parameterMap, String paramName) { String[] values = parameterMap.get(paramName); return (values != null && values.length > 0) ? values[0] : null; } // Вычисление MD5-хэша private String md5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5 algorithm not found", e); } } // Запись в лог-файл private void writeLog(String filePath, String logMessage) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true))) { writer.write(logMessage); } catch (IOException e) { System.err.println("Failed to write log: " + e.getMessage()); } } } |
Полный перечень параметров в POST-оповещении можно посмотреть здесь.
В ответ на данный запрос должна быть отправлена строка вида:
Важно отметить, что ответ данного скрипта должен быть исключительно в указанном формате и ни байтом больше, в противном случае PayKeeper будет считать, что ваш сайт неправильно обработал оповещение об успешном платеже.
В PayKeeper возможен полный возврат платежа с помощью метода /change/payment/reverse/. Возврат могут делать только пользователи с включённой функцией возврата. Подробнее можно ознакомиться в документации JSON API. Пример запроса на полный возврат:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
import java.io.*; import java.net.*; import java.util.Base64; import org.json.JSONObject; public class PaykeeperPayment { public static void main(String[] args) { // Номер платежа String invoiceId = "1234567890"; // Доступы к Paykeeper String server = "https://xxxxx.server.paykeeper.ru"; String user = "admin"; String password = "xxxxxxxxxx"; try { // Авторизация в Paykeeper String credentials = Base64.getEncoder().encodeToString((user + ":" + password).getBytes()); String[] headers = { "Content-Type: application/x-www-form-urlencoded", "Authorization: Basic " + credentials }; // Получение информации о платеже String infoUrl = server + "/info/invoice/byid/?id=" + invoiceId; JSONObject infoResponse = sendHttpRequest(infoUrl, "GET", headers, null); if (infoResponse.has("paymentid")) { String paymentId = infoResponse.getString("paymentid"); // Получение токена String tokenUrl = server + "/info/settings/token/"; JSONObject tokenResponse = sendHttpRequest(tokenUrl, "GET", headers, null); if (tokenResponse.has("token")) { String token = tokenResponse.getString("token"); // Подготовка данных для возврата платежа String requestData = String.format( "id=%s&amount=500&partial=false&token=%s", URLEncoder.encode(paymentId, "UTF-8"), URLEncoder.encode(token, "UTF-8") ); // Запрос на возврат платежа String reverseUrl = server + "/change/payment/reverse/"; JSONObject reverseResponse = sendHttpRequest(reverseUrl, "POST", headers, requestData); // Вывод ответа System.out.println(reverseResponse.toString(2)); } else { System.err.println("Ошибка: токен не получен."); } } else { System.err.println("Ошибка: платеж не найден."); } } catch (Exception e) { e.printStackTrace(); } } private static JSONObject sendHttpRequest(String urlString, String method, String[] headers, String requestBody) throws IOException { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod(method); for (String header : headers) { String[] parts = header.split(": ", 2); connection.setRequestProperty(parts[0], parts[1]); } if (requestBody != null && (method.equals("POST") || method.equals("PUT"))) { connection.setDoOutput(true); try (OutputStream os = connection.getOutputStream()) { byte[] input = requestBody.getBytes("UTF-8"); os.write(input, 0, input.length); } } int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"))) { StringBuilder response = new StringBuilder(); String responseLine; while ((responseLine = br.readLine()) != null) { response.append(responseLine.trim()); } return new JSONObject(response.toString()); } } else { throw new IOException("HTTP error: " + responseCode); } } } |
Менеджер перезвонит вам и расскажет про детали подключения