Оплата за допомогою додатку "Термінал"

З питань отримання ідентифікаторів зовнішніх сервісів, ключів для підпису, та адреси сервісу перевірки платежів звертатися:

Галушкін Максим Сергійович

E-mail: maksim.galushkin@privatbank.ua

  1. Підготовка параметрів та підпис даних запитів

  2. Сервіс токенізації операцій

  3. Виклик додатку "Термінал" для виконання операції (Android)

  4. Виклик додатку "Термінал" для виконання операції ( iOS )

  5. Сервіс отримання інформації щодо операції

  6. Опис  реверсу

  7. Коди відповідей

1.Підготовка параметрів та підпис даних запитів

Для правильного формування запиту на любой з сервісів ( token.php чи check.php) потрібно до скрипта додвати параметри clid, signed, signature

    url = {https://dio.privatbank.ua}
    * - запит token/check
    POST {url}/api/nfcpos/integrators/*.php?clid=test&signed=1476193110
    &signature=2b3a694f0fe2574bd85edb0f5e723af013c089d9
Атрибут Опис
clid ідентифікатор зовнішнього сервісу, попередньо узгоджується і видається банком. Додається  до адреси запиту.
secret ключ для підпису, видається разом з ідентифікатором зовнішнього сервісу
signed час відправлення запиту (кількість секунд, що минув з 1 січня 1970р 00: 00:00 GMT до моменту відправки запиту). Додається  до адреси  запиту
signature підпис сформований  на  основі  параметтрів  sercet, signed, data.  Додається до адреси запиту.
data тіло запиту
Параметр signature розраховується за формулою:

sha1( signed + secret + data + secret )

де:

secret - ключ для підпису, видається разом з ідентифікатором зовнішнього сервісу data - вміст тіла запиту

Параметри clid, signed, signature завжди передаються всередині url.

Приклад запиту

clid = test
secret = test
signed = 1697051765  (Wed Oct 11 2023 19:16:05 GMT+0000)
data =  {"operation" : "pay" ,"amount":1.0, "purpose" : "test"}

POST https://dio.privatbank.ua/api/nfcpos/integrators/token.php?clid=test
&signed=1697051765&signature=0c19f9efae8ce89efb542bd52c88954a99496126 

Content-Type: application/json
тіло запиту
{"operation" : "pay" ,"amount":1.0, "purpose" : "test"}

PHP приклад формування і підпісу праметрів запіту отрімання токену для операції Оплата

$clid = 'test';  
$secret = 'abcdef';  
//для тестування та перевірки
//в реальных запросах должно быть текущее в время серверв UTC в секундах 
$signed = '1624023225'; // (для UTC 2021-06-18 13:33:45 +0000)

/*** 
Pay     
Під час формування підпису в $data необхідно враховувати саме той набір
 параметрів, який буде передано в тілі запиту. Тобто, якщо запит містить
 необов'язкові поля, під час формування підпису потрібно використовувати
 всі поля, які фактично присутні в запиті. 
*/
$params = array(
    'operation' =>  'pay',
    'amount' =>  3.33,
    'purpose' =>  'Test'
);

$data = json_encode($params);

//формирование подписи sha1( signed + secret + data + secret )
$signature = sha1( $signed . $secret . $data . $secret );
//значення після обчислення для перевірки
// signature=896e7808ed56bde0e3966b46b30688e0715bb439

$url = 'https://dio.privatbank.ua/api/nfcpos/integrators/token.php?clid='
.$clid  . '&signed=' . $signed . '&signature=' . $signature;

$options = array( CURLOPT_HTTPHEADER => array( 'Content-Type' => 'application/json' ));

2.Сервіс токенізації операцій

Для взаємодії з платіжним застосунок Термінал та виконання фінансових операцій оплата/повернення необхідно використовувати API для отримання токенів. Залежно від типу виконуваної операції необхідно підготувати та передати у запиті відповідні параметри.

Коли клієнт надає агрегатору токен, то  агрегатор має додати  до  тіла  запита  ще  один  параметр, під назвою "client_token".

Якщо в запиті на токенізацію вказано phone та retailer_id, і цей термінал доступний у застосунку, то термінал буде обрано автоматично.

Запит на отримання токену:

POST https://dio.privatbank.ua/api/nfcpos/integrators/token.php? clid= {ID компанії яка створює токен}&signed={ключ}&signature={значення хешу}

Параметри тіла запитів:

    Оплата

{
  "operation": "pay",
  " amount": 3.33,
  "purpose": "Test",
  "client_token": "zgvIgAQ5jbXIsvDrAahW7MVKNXk7Tq1114SnG9UKYtyXTNZJD437xwmAnk4IYUyN"
}

     Повернення

{
  "operation": "refund",
  " amount": 3.33,
  "transaction_id": "Test",
  "client_token": "zgvIgAQ5jbXIsvDrAahW7MVKNXk7Tq1114SnG9UKYtyXTNZJD437xwmAnk4IYUyN"
}

Формат вхідних параметрів

Параметр Опис Формат даних
operation Операція (pay/refund).
Поле обов'язкове.'
String
amount Сума платежу/повернення (мінімальна сума для сплати 1.00 грн).
 Поле обов'язкове.
Double
purpose Призначення платежу (заборонені сурогатні та “Other Symbol”) Поле для  операції pay.
Значення параметра «призначення» повинно бути текстовим і не містити спеціальних символів, емодзі та знаків пунктуації. 
Поле не обов'язкове.
String
transaction_id Ідентифікатор оригінальної платіжної транзакціїю Обов'язкове поле  для операції refund
Поле не обов'язкове.
String
client_token Токен клієнта  для якого потрібно  створити  токен для  проведення операцій (pay або refund ). Якщо  параметр  не вказується то операційний токен (jwt)  створюється  для інтегратора, компанії, яка  ініціює  запит. 
Поле не обов'язкове.
Stirng(64)
phone Номер телефону касира (в форматі “+380 ** * ** **”). Обов'язковий, якщо вказати  retailer_id. 
Поле не обов'язкове.
String
retailer_id Ідентифікатор магазину. Обов'язковий, якщо вказати phone.
Поле не обов'язкове.
String

Успішна відповідь

{
 "success": true,
 "rid": "b3e195e0eba6d921a95231cd78937d6a",
 "jwt": "ey******kzMzZkOVGVzdCJ9.x-qLonr823cU2NK0ETJ1gaaUpmJ-WXov0",
 "status": 200
}

Опис Вихідних параметрів

Параметр Опис Формат даних
success признак успішної операції boolean
rid ідентифікатор запита string
jwt токен сформовааний для певної операції sting( BASE64)
status код HTTP відповіді int

3.Виклик додатку "Термінал" для здійснення оплати (Android)

Після успішного отримання токена для фінансової операції.

Приклад виклику та передачі параметрів

val jwtToken:String = "*****"   //JWT токен операциії (П. 1)
val callback = "https://******" //callback

startTerminalApp(jwtToken: String, callback:String)

Формат параметрів

Параметр Опис Формат даних Обов'язковий
jwtToken JWT токен операциії (П. 1) String Так
callback callback зовнішньої інтеграційної системи для передачі події (успішного/неуспішного) завершення операції String Ні

Функція виклику додатку

const val DEEP_LINK_URL = "nfcterminal://executor"

private fun startTerminalApp(jwtToken: String, callback:String) {
        val launchIntent =
            packageManager.getLaunchIntentForPackage("ua.privatbank.pterminal")
        if (launchIntent != null) {
            val intent = Intent(Intent.ACTION_VIEW)
            val jwtTokenEncode = URLEncoder.encode(jwtToken, "utf-8")
            intent.data = Uri.parse(DEEP_LINK_URL+"?token=$jwtTokenEncode&callback=$callback")
            try {
                startTerminalForResult.launch(intent)
            } catch (e: Exception) {
                e.printStackTrace()
            }

        } else {
            try {
                startActivity(
                    Intent(
                        Intent.ACTION_VIEW,
                        Uri.parse("market://details?id=ua.privatbank.pterminal")
                    )
                )
            } catch (e: android.content.ActivityNotFoundException) {
                try {
                    startActivity(
                        Intent(
                            Intent.ACTION_VIEW,
                            Uri.parse("https://play.google.com/store/apps/details?id=ua.privatbank.pterminal")
                        )
                    )
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }
    }

Отримання та обробка результату

private val startTerminalForResult =
  registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
      if (result.resultCode == AppCompatActivity.RESULT_OK) {
          val intent = result.data
          if (intent != null) {
              val tresult = intent.getStringExtra("result") //результат виконання операції
              Log.d(TAG,"Intent Transaction result = $tresult")
              binding.edResult.setText(tresult)
          } else {
              Log.d(TAG,"intent == NULL")
          }
      } else {
          Log.d(TAG, "result != RESULT_OK")
      }
  }

Результат виконання операції повертається у форматі JSON

Формат відповіді

Атрибут Опис Формат даних
result результат успішна / нi Boolean (true/false)
token інтеграційний JWT токен String
operation_id ідентифікатор операції String
date дата та час проведення String
error опис помилки String
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbG*******",
  "date": "2022-07-13 16:30:50",
  "error": "",
  "operation_id": "PAX62cec9140b7052.60786725",
  "result": true
}

4.Виклик додатку "Термінал" для здійснення оплати (iOS)

Після успішного отримання токена для фінансової операції.

Приклад виклику та передачі параметрів:

let jwtToken = "*****"  // JWT токен операциії (П. 1)
let callback = "https://******/?id=unique_identifier" // буде виклакано по завершеню операції, id=unique_identifier як приклад ідентифікації операції  

let terminalUrlScheme = "nfcterminal://executor"

let urlString = terminalUrlScheme + "?token=\(jwtToken)&callback=\(callbackUrl)"

if let url = URL(string: urlString) {
    UIApplication.shared.open(url)
}

Формат параметрів

Параметр Опис Формат даних Обов'язковий
jwtToken JWT токен операциії (П. 1) String Так
callback callback зовнішньої інтеграційної системи для передачі події завершення операції String Так

Для обробки виклику callback, застосунок може бути налаштовано одним із способів:

Отримання та обробка результату відбуваєтся через Сервіс отримання інформації щодо операції

5.Сервіс отримання інформації щодо операції

Використовується для отримання результату операції, зробленої за допомогою додатку "Термінал"

POST [https://dio.privatbank.ua/api/nfcpos/integrators/check.php?**clid=**{ID клієнта}&signed={ключ}&signature={значення хешу}

Параметри операції

*jwt *- токен, отриманий для виконання операції

{
 "jwt": "ey******kzMzZkOVGVzdCJ9.x-qLonr823cU2NK0ETJ1gaaUpmJ-WXov0"
}

Успішна відповідь для операції Оплата

{
  "success": true,
  "rid": "addc2a61a4d0d23abbdf09bf24b46bdd",
  "pay": {
    "code": "iso00_Approved",
    "user_message": "Успішно",
    "merchant": "M11302GG",
    "traceId": "0471edb9526efc43ad9e680c2dec0ee5",
    "approval_code": "131192",
    "processing_code": "000000",
    "response_code": "00",
    "rrn": "014722766522",
    "batch_id": 0,
    "discount_amount": 0,
    "discount_percent": 0,
    "discount_type": "no_discount",
    "amount_full": 3.33,
    "payment_system": "Visa",
    "masked_pan": "4149********1451",
    "qr_code": "",
    "receipt": "HTML чек ",
    "receipt_id": 0,
    "date": "20230705 08:20:09 +0000",
    "stan": "082597",
    "transaction_id": "PAX-TEST-64a527b82b7479.87095729"
  },
  "status": 200
}

Успішна відповідь для операції Повернення

{
  "success": true,
  "rid": "a60a7953e5678664398f15755b12410f",
  "pay": {
    "code": "iso00_Approved",
    "user_message": "Успішно",
    "merchant": "M11302GG",
    "traceId": "0471edb9526efc43ad9e680c2dec0ee5",
    "approval_code": "131192",
    "processing_code": "000000",
    "response_code": "00",
    "rrn": "014722766522",
    "batch_id": 0,
    "discount_amount": 0,
    "discount_percent": 0,
    "discount_type": "no_discount",
    "amount_full": 3.33,
    "payment_system": "Visa",
    "masked_pan": "4149********1451",
    "qr_code": "",
    "receipt": "HTML чек",
    "receipt_id": 0,
    "date": "20230705 08:20:09 +0000",
    "stan": "082597",
    "transaction_id": "PAX-TEST-64a527b82b7479.87095729"
  },
  "refunds": [
    {
      "amount": 3.33,
      "state": 1,
      "date": "2023-07-05T11:27:56.522626"
    }
  ],
  "status": 200
}

Успішна відповідь для операції Реверс

 {
  "success": true,
  "rid": "63f86ba5fd078c104afd9a86ba29df96",
  "pay": {
    "code": "iso00_Approved",
    "user_message": "Успішно",
    "merchant": "M11302EP",
    "approval_code": "900060",
    "processing_code": "000000",
    "response_code": "00",
    "rrn": "014724824026",
    "amount_full": 100,
    "payment_system": "Visa",
    "masked_pan": "4731********1548",
    "receipt": "    <div class=\"pb-receipt receipt\">\n            <div class=\"pb-logo\"> </div>\n    <div class=\"r-header\">\n            <p>Silpo Тестовий  мерчант (M11302EP)</p>\n    <p>область Дніпропетровська,район Дніпровський,місто Дніпро,вулиця Архітектора Заболотного,будинок 1</p>\n    <p>ЄДРПОУ/ІПН 31435622</p>\n    <p>'TEST TEST MCB'</p>\n    </div>\n    <div class=\"font-xl font-bold hl\">Оплата</div>\n\n    <div class=\"block i-block\">\n            <div class=\"flex-line \">\n        <p>Телефон підтримки</p>\n        <p>3700</p>\n    </div>\n    <div class=\"flex-line \">\n        <p>Сайт АТ КБ ПриватБанк</p>\n        <p>www.privatbank.ua</p>\n    </div>\n    </div>    <hr class=\"dot-line\">\n\n    <div class=\"block main-block\">\n            <div class=\"flex-line \">\n        <p>VISA</p>\n        <p>4731********1548</p>\n    </div>\n    <div class=\"flex-line font-n font-bold\">\n        <p>СУМА</p>\n        <p>100,00 грн</p>\n    </div>\n    </div>    <hr class=\"dot-line\">\n    <div class=\"block i-block\">\n            <p>Підпис клієнта не потрібен</p>\n    <p>Мерчант: M11302EP</p>\n    <p>Код авторизації: 900060</p>\n    <p>AID: A0000000031010</p>\n    <p>RRN: 014724824026</p>\n    <p>27.01.2025 16:39:11</p>\n    </div>\n    </div>                       ",
    "date": "20250127 14:39:11 +0000",
    "stan": "098801",
    "batch_id": 0,
    "discount_amount": 0,
    "discount_percent": 0,
    "discount_type": "no_discount",
    "qr_code": "",
    "receipt_id": 0,
    "transaction_id": "PAX-TEST-67979a8e722d30.66656501"
  },
  "reverses": [
    {
      "amount": 100,
      "id": 1901,
      "created": "2025-01-27 16:39:57",
      "updated": "2025-01-27 16:39:58",
      "reversed": null,
      "state": 0,
      "state_description": "InProgress"
    },
    {
      "amount": 100,
      "id": 1903,
      "created": "2025-01-28 11:31:24",
      "updated": "2025-01-28 11:31:24",
      "reversed": null,
      "state": 0,
      "state_description": "InProgress"
    },
    {
      "amount": 100,
      "id": 1939,
      "created": "2025-01-31 15:29:39",
      "updated": "2025-01-31 15:29:39",
      "reversed": null,
      "state": 2,
      "state_description": "Failed"
    }
  ],
  "status": 200

Опис вихідних параметрів

Атрибут Опис Формат даних
Блок pay ( інформація про оригінальну транзакцію)
transaction_id Ідентифікатор транзакції String
code Код відповіді у процесінгу String
merchant Мерчант під яким робиласть транзакція String
user_message Відповідь операції продажу процесінга String
traceId Ідентифікатор для відстеження String
approval_code Містить ідентифікаційний код відповіді , назначений авторизуючим інститутом String
responce_code Код відповіді авторизатора String
rrn RRN String
batch_id Ідентифікатор батча транзакції String
discount_amount Сума знижки float
discount_percent Процент знижки float
discount_type Тип знижки String
amount_full Повна сума транзакції (грн) float
payment_system Платіжна система String
masked_pan Маска платіжної картки String
qr_code QR коду String
receipt Чек в формате HTML String
receipt_id Ідентифікатор чека String
date Дата проведення транзакції String
stan Це не унікальне числове значення транзакції String
Блок refuns (заявки на повернення по платежу)
date Дата створення запиту на повернення String
amount Сума заявки на повернення у грн float
state Статус заявки ( 1-Запит обробляється, 2- Виконано, 3 - Відмова ) ) int
Блок reverses (заявки на реверси по платежу)
id Ідентифікатор заявки на реверс int
amount Сума заявки на повернення у грн float
created Дата створення заявки на реверс string
updated Дата останнього оновлення заявки на реверс string
reversed Дата реверсу в процессінгу string
state Статус заявки
(0 = InProgress, 1 = Reversed, 2 = Failed)
int
state_description Опис статусу
(0 = InProgress, 1 = Reversed, 2 = Failed)
string
status код стану http запиту int

6.Опис  реверсу

Використовується для для  формування реверсу операції, яка  буде  передана  у запиті.

POST [https://dio.privatbank.ua/api/nfcpos/integrators/reverse.php?clid={ID клієнта}&signed={ключ}&signature={значення хешу} .

Параметри операції:

transaction_id -  ідентифікатор  транзакції по якій намагаємось провести реверс. Приклад:

{
  "transaction_id": "PAX-TEST-67977ccf9d76d8.61511107"
}

Відповідь:

id - Ідентифікатор заявки на реверс , int

result - Результат виконання операції, string (TransactionResult) (Enum: "ok" "error" "retry")

code - Код виконання операції реверсу, string, 

user_message - Текстове повідомлення клієнту при помилці

merchant - Мерчант користувача

response_code - Код відповіді авторизацій ПЦ

date - Дата виконання транзакції у  форматі 20231103 17:40:50 +0000

status - статус  відповіді  200 - ОК 

{
  "success": true,
  "rid": "ebb621d7a87dfbe9524a0cb3b158aee4",
  "id": 1944,
  "result": "ok",
  "code": "guaranteeOffline",
  "user_message": null,
  "merchant": "M123456",
  "response_code": "00",
  "date": "20250206 20:32:05 +0000",
  "status": 200
}

Таблиця  з результатами  та кодами виконання операції

Числове представлення Текстове представлення result user_message Опис
1 sentOnline ok
Відправлено на ПЦ одразу та отримано успішну відповідь
2 alreadySavedRevers ok Реверсал знаходиться в обробці Вже збережено реверсал за цим trans_id
3 requestIsNotValid error Передана не валідна операція
(або опис помилки валідації)
Переданий параметр oper != reversal або не пройшла загальна валідація вхідних параметрів
4 cannotSaveReversal retry - Помилка збереження реверсу до бази
5 guaranteeOffline ok - Реверс збережено для подальних спроб відправки до ПЦ. Можна вважати обробленим.
6 incorrectDateForReversal ok - Закінчився час подачі автоматичного реверсу. Назаразі діє обмеження: 24 години від оригінальної операції (якщо без чайових) АБО той же каледарний день (якщо операція із чайовими)

7.Коди відповідей

HTTP code Опис
200 Успішна обробка
400 Неправильний запит
401 Неправильно сформовано підпис
418 Час який використовувася  у  параметрі  signed більший на 60 секунд ніж час  обробки запиту
500 Внутрішня помилка сервісу

Приклад відповіді з помилкою

{
 "success": false,
 "rid": "ce2f4a3454c35a6429adfd7a67f35ddc",
 "status": 400,
 "message" : "Невалідні дані запиту.",
 "error" : "IE_01"
}