Skip to content

Instantly share code, notes, and snippets.

@dimitarminchev
Last active December 20, 2023 13:54
Show Gist options
  • Save dimitarminchev/34265c2c780ccff86a545eb4a1ffd88c to your computer and use it in GitHub Desktop.
Save dimitarminchev/34265c2c780ccff86a545eb4a1ffd88c to your computer and use it in GitHub Desktop.
Borica Response 4.0
<?php
/*
# Borica Response 4.0
Получаване и обработка на отговор от електронно плащане: [borica_response.php](https://gist.github.com/dimitarminchev/34265c2c780ccff86a545eb4a1ffd88c)
- Документация: [Borica APGW e-Gateway Resources](https://3dsgate-dev.borica.bg/)
- Автор: [доц. д-р Димитър Минчев](http://www.minchev.eu)
- Eлектронна поща: [mitko@bfu.bg](mailto:mitko@bfu.bg)
*/
// Проверка дали има обратно получената информация от БОРИКА?
if(empty($_REQUEST)) die("<h1>No Responce Error!</h1>");
// Формиране на сигнатурата за проверка
// MAC_GENERAL: ACTION, RC, APPROVAL, TERMINAL, TRTYPE, AMOUNT, CURRENCY, ORDER, RRN, INT_REF, PARES_STATUS, ECI, TIMESTAMP, NONCE, RFU
$ACTION = strlen($_REQUEST["ACTION"]) > 0 ? strlen($_REQUEST["ACTION"]).$_REQUEST["ACTION"] : "-";
$RC = strlen($_REQUEST["RC"]) > 0 ? strlen($_REQUEST["RC"]).$_REQUEST["RC"] : "-";
$APPROVAL = strlen($_REQUEST["APPROVAL"]) > 0 ? strlen($_REQUEST["APPROVAL"]).$_REQUEST["APPROVAL"] : "-";
$TERMINAL = strlen($_REQUEST["TERMINAL"]) > 0 ? strlen($_REQUEST["TERMINAL"]).$_REQUEST["TERMINAL"] : "-";
$TRTYPE = strlen($_REQUEST["TRTYPE"]) > 0 ? strlen($_REQUEST["TRTYPE"]).$_REQUEST["TRTYPE"] : "-";
$AMOUNT = strlen($_REQUEST["AMOUNT"]) > 0 ? strlen($_REQUEST["AMOUNT"]).$_REQUEST["AMOUNT"] : "-";
$CURRENCY = strlen($_REQUEST["CURRENCY"]) > 0 ? strlen($_REQUEST["CURRENCY"]).$_REQUEST["CURRENCY"] : "-";
$ORDER = strlen($_REQUEST["ORDER"]) > 0 ? strlen($_REQUEST["ORDER"]).$_REQUEST["ORDER"] : "-";
$RRN = strlen($_REQUEST["RRN"]) > 0 ? strlen($_REQUEST["RRN"]).$_REQUEST["RRN"] : "-";
$INT_REF = strlen($_REQUEST["INT_REF"]) > 0 ? strlen($_REQUEST["INT_REF"]).$_REQUEST["INT_REF"] : "-";
$PARES_STATUS = strlen($_REQUEST["PARES_STATUS"]) > 0 ? strlen($_REQUEST["PARES_STATUS"]).$_REQUEST["PARES_STATUS"] : "-";
$ECI = strlen($_REQUEST["ECI"]) > 0 ? strlen($_REQUEST["ECI"]).$_REQUEST["ECI"] : "-";
$TIMESTAMP = strlen($_REQUEST["TIMESTAMP"]) > 0 ? strlen($_REQUEST["TIMESTAMP"]).$_REQUEST["TIMESTAMP"] : "-";
$NONCE = strlen($_REQUEST["NONCE"]) > 0 ? strlen($_REQUEST["NONCE"]).$_REQUEST["NONCE"] : "-";
$RFU = "-"; // Резервирано поле за бъдеща употреба
$DATA = $ACTION.$RC.$APPROVAL.$TERMINAL.$TRTYPE.$AMOUNT.$CURRENCY.$ORDER.$RRN.$INT_REF.$PARES_STATUS.$ECI.$TIMESTAMP.$NONCE.$RFU;
$P_SIGN = hex2bin($_REQUEST["P_SIGN"]);
// Информация за цифровия публичен ключ
$public_key = '-----BEGIN CERTIFICATE-----
MIIGWjCCBEKgAwIBAgIIQSHpHDZ7ASAwDQYJKoZIhvcNAQELBQAwgYoxCzAJBgNVBAYTAkJHMRgw
FgYDVQRhDA9OVFJCRy0yMDEyMzA0MjYxIDAeBgNVBAoMF0JPUklDQSAtIEJBTktTRVJWSUNFIEFE
MRAwDgYDVQQLDAdCLVRydXN0MS0wKwYDVQQDDCRCLVRydXN0IFRFU1QgT3BlcmF0aW9uYWwgQWR2
YW5jZWQgQ0EwHhcNMjAwOTEwMDg0NzU5WhcNMjMwOTEwMDg0NzU5WjBkMRQwEgYDVQQDDAtNUEkg
T1cgQVBHVzELMAkGA1UECwwCSVMxEjAQBgNVBAoMCUJvcmljYSBBRDEOMAwGA1UECAwFU29maWEx
DjAMBgNVBAcMBVNvZmlhMQswCQYDVQQGEwJCRzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAMmtJ1gcFkdfY/wfEk3IbqAA1dveXj9J3dCNyliHoooj1ePsX86jlYLijrdPOgayESwH01OO
nVEbcF9z2qoicH12vJaa9ZEFgqkB+qv55erfQOTjgVhd+KRb8YES+uEGkIFE8D/peLMeKeiRSleN
corRa4J1ms/V/2Oklxg0xSnEXw8tRa0U2OoPlEwCbT01DgPMoud5EitpTvD9/gc69aWgVS477Erf
ro+CW89bLGNiHh6mmZt71uIXugNtGf2RhP59fmEKBKj+DSF1QI65SVvv2eYb6JBlhHX+hZss/oAN
xvqYFSG4k6L1tkoDwctB+q7p1EbWEuqDNxYT0RidkLkCAwEAAaOCAecwggHjMB0GA1UdDgQWBBTT
nQwEEjMqWryNqt8onGmGk6nm4DAfBgNVHSMEGDAWgBT1J8z325solCubZvApcg6KPWLcmDAgBgNV
HRIEGTAXhhVodHRwOi8vd3d3LmItdHJ1c3QuYmcwCQYDVR0TBAIwADBNBgNVHSAERjBEMEIGDCsG
AQQB+3YBBwEEAjAyMDAGCCsGAQUFBwIBFiRodHRwOi8vd3d3LmItdHJ1c3Qub3JnL2RvY3VtZW50
cy9jcHMwDgYDVR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBUBgNV
HR8ETTBLMEmgR6BFhkNodHRwOi8vY3JsdGVzdC5iLXRydXN0Lm9yZy9yZXBvc2l0b3J5L0ItVHJ1
c3RUZXN0T3BlcmF0aW9uYWxBQ0EuY3JsMIGHBggrBgEFBQcBAQR7MHkwJwYIKwYBBQUHMAGGG2h0
dHA6Ly9vY3NwdGVzdC5iLXRydXN0Lm9yZzBOBggrBgEFBQcwAoZCaHR0cDovL2NhdGVzdC5iLXRy
dXN0Lm9yZy9yZXBvc2l0b3J5L0ItVHJ1c3RUZXN0T3BlcmF0aW9uYWxBQ0EuY2VyMBYGA1UdEQQP
MA2CC01QSSBPVyBBUEdXMA0GCSqGSIb3DQEBCwUAA4ICAQAUJfDjTROuVORLojCzVQdppoiPs3hX
Ra/9MaNIUP5xlI0AamWmN7bTDQpnNfw5tlo8DPSBIMfP+5xJyfMTHAi43i+7vf1t1ZucEbVJ73FF
zdzZQaxw9NY0n0IBBz8WEnkaGewh45aQ6XMgNe5xcKbtP2vqq+qZiy0eyIHJwaQORKyZ9+jBlnVo
ZdzUbDrrSEMka98IQ52XO8EPbCmB/GhJlZ991yNo5/PVsFxT9sjG3VGm+sStD3G7+pjX+HsHLn65
gwWq2oRiQqe62W/HSNb5dnIWqIJldT4Zd0Ar97hQwU1ZQVnmL5ZjswsjafI7B/0N4U5QzbOvWX1W
oDXCCqmXAoTP1DDEWJ0vmvVDHGrrC0rIbluBdzQEK/D1f3A1jlCzQPyKOwUuafLlpCX17b09Zwxi
45prDk/LBqE6CI6CM+8nF0QyN3Th+r2IqUuhGpfLApGlp6sJvJdAhnqX1VCGJCdozIhzrEJ4oha3
/+HijQl+vUaYevk1d/EipZNHU1gkcocrj2qmTMOKzEw9zDs5jVSgtBZTUF5ORwUNiTXj7EZUnQUC
wANFl8k0EcWPhkU5L7v9/9rcGkMcm0S3bM5rbKksabvq01cvxkepS5qqvbxgugci/8sPCXMAThCK
eiJHilEt1uns+tFA+7RSVFKOpf07g3DBGYf5P8qKLQCFMg==
-----END CERTIFICATE-----';
/*
// Отваряне на файла съдържащ цифровия публичен ключ
$public_key_file = "public.cer";
$fp = fopen($public_key_file, "r");
$public_key = fread($fp, filesize($public_key_file));
fclose($fp);
*/
// Подписване на съобщението с цифров сертификат
$public_key_id = openssl_get_publickey($public_key);
$ssl_verification = openssl_verify($DATA, $P_SIGN, $public_key_id, OPENSSL_ALGO_SHA256);
openssl_free_key($public_key_id);
// Проверка за валидност?
if($ssl_verification != 1) die("<h1>SSL Verification Error!</h1><p>".openssl_error_string()."</p>");
// Записване на хронология в JavaScript Object Notation (JSON) файл
file_put_contents("$ORDER.json", json_encode($_REQUEST));
// Проверка за успешна транзакция?
if($_REQUEST["RC"] == "00") echo "<h1>Payment Successful</h1>";
else die("<h1>Payment Error ".$_REQUEST["RC"]."</h1>");
// RC Error Codes (ISO-8583): https://en.wikipedia.org/wiki/ISO_8583
// Отпечатване на обратно получената информация от БОРИКА
print_r($_REQUEST);
@dimitarminchev
Copy link
Author

dimitarminchev commented Oct 2, 2020

Borica Response 4.0

Получаване и обработка на отговор от електронно плащане: borica_response.php

Responce Codes

Code Meaning
0 Successful approval/completion or that VIP PIN verification is valid
1 Refer to card issuer
2 Refer to card issuer, special condition
3 Invalid merchant or service provider
4 Pickup
5 Do not honor
6 General error
7 Pickup card, special condition (other than lost/stolen card)
8 Honor with identification
9 Request in progress
10 Partial approval
11 VIP approval
12 Invalid transaction
13 Invalid amount (currency conversion field overflow) or amount exceeds maximum for card program
14 Invalid account number (no such number)
15 No such issuer
16 Insufficient funds
17 Customer cancellation
18 Customer dispute
19 Re-enter transaction
20 Invalid response
21 No action taken (unable to back out prior transaction)
22 Suspected Malfunction
23 unacceptable transaction fee
24 File update not supported by receiver
25 Unable to locate record in file, or account number is missing from the inquiry
26 Duplicate file update record, old record replaced
27 File update field edit error
28 File is temporarily unavailable
29 File update not successful, contact acquirer
30 Format error
31 Bank not supported
32 Completed partially
33 Expired card
34 Suspected fraud
35 Card acceptor contact acquirer
36 Restricted card
37 Card acceptor call acquirer security
38 Allowed PIN tries exceeded
39 No credit account
40 Requested function not supported
41 Merchant should retain card (card reported lost)
42 No universal account
43 Merchant should retain card (card reported stolen)
51 Insufficient funds
52 No checking account
53 No savings account
54 Expired card
55 Incorrect PIN
56 No Card Record
57 Transaction not permitted to cardholder
58 Transaction not allowed at terminal
59 Suspected fraud
61 Activity amount limit exceeded
62 Restricted card (for example, in country exclusion table)
63 Security violation
65 Activity count limit exceeded
68 Response received too late
75 Allowable number of PIN-entry tries exceeded
76 Unable to locate previous message (no match on retrieval reference number)
77 Previous message located for a repeat or reversal, but repeat or reversal data are inconsistent with original message
78 ’Blocked, first used’—The transaction is from a new cardholder, and the card has not been properly unblocked.
80 Visa transactions: credit issuer unavailable. Private label and check acceptance: Invalid date
81 PIN cryptographic error found (error found by VIC security module during PIN decryption)
82 Negative CAM, dCVV, iCVV, or CVV results
83 Unable to verify PIN
85 No reason to decline a request for account number verification, address verification, CVV2 verification; or a credit voucher or merchandise return
91 Issuer unavailable or switch inoperative (STIP not applicable or available for this transaction)
92 Destination cannot be found for routing
93 Transaction cannot be completed, violation of law
94 Duplicate transmission
95 Reconcile error
96 System malfunction, System malfunction or certain field error conditions
B1 Surcharge amount not permitted on Visa cards (U.S. acquirers only)
N0 Force STIP
N3 Cash service not available
N4 Cashback request exceeds issuer limit
N7 Decline for CVV2 failure
P2 Invalid biller information
P5 PIN change/unblock request declined
P6 Unsafe PIN
Q1 Card authentication failed
R0 Stop payment order
R1 Revocation of authorization order
R3 Revocation of all authorizations order
XA Forward to issuer
XD Forward to issuer
Z3 Unable to go online
-1 A mandatory request field is not filled in
-2 Bad CGI request
-3 Acquirer host (NS) does not respond or wrong format of e-gateway response template file
-4 No connection to the acquirer host (NS)
-9 Error in the "Card expiration date" request field
-11 Error in the "Currency" request field
-12 Error in the "Merchant ID" request field
-15 Error in the "RRN" request field
-17 The terminal is denied access to the e-Gateway
-19 Error in the authentication information request or authentication failed
-20 A permitted time interval (1 hour by default) between the transaction Time Stam prequest field and the e-Gateway time is exceeded
-21 The transaction has already been executed
-24 Transaction context mismatch
-25 Transaction canceled (e.g. by user)
-27 Invalid merchant name
-32 Duplicate declined transaction

@nstoichkov7
Copy link

nstoichkov7 commented Oct 23, 2020

Здравейте,
Първо искам да Ви благодаря за имплеметацията мисля че ще е от полза на много хора.
Второ благодаря за линка с документацията. От банката са ми изпратили версия 2.0 (не знам защо) докато тази е версия 2.2. Тъй като има промени между двете версии новата документация ми беше много полезна. Някак си с грешна документация е една идея по тегаво :D

Имам следния въпрос:
Доколкото разбрах от последната версия на документацията, когато Борика ни върне 0 байта за някое поле например (PARES_STATUS, ECI) го заменяме с "-" (както Вие сте направили),
НО в низът $DATA който проверяваме с openssl_verify мисля че те тези полета трябва да участавт БЕЗ дължианта им преди тях. В примера от документацията на страница 18 PARES_STATUS и ECI са с 0 байта и участват само с - без 1 за дължина отпред.

Пробвах и двата варианта но не успявам да получа успешна верификация openssl_verify винаги ми връща 0
Дали имате идея какъв може да е проблема

@dimitarminchev
Copy link
Author

Действително, ако някое поле е празно то участва във формирането на сигнатурата като се записва само със знак минус. Примера който е даден тук на PHP верификацията минава.

PS. Добавил съм нещо което още не е документирано, а именно: $DATA = rtrim($DATA,"-"); // Fix FW: update Borica EMV 3DS ver 2.2 from 22.10.2020 г.

@nstoichkov7
Copy link

Благодаря Ви за отговора помогна ми, мина и при мен
Интересно е че когато ползвам публичният ключ, който е във вашият пример минава, когато ползвам този който са ми дали не :)

@budiony
Copy link

budiony commented Jan 7, 2021

Също така Borica Responce 2.2 се пише синтактично коректно Borica Response 2.2.

@dimitarminchev
Copy link
Author

Благодаря @budiony бележката е взета под внимание и информацията е коригирана!

@GeorgyPorgy
Copy link

Някаква идея защо Борика очаква всички търговци да използват един споделен публичен ключ за верификация на съобщенията от платежният сървър , вместо предоставените им индивидуални ?

@dimitarminchev
Copy link
Author

@GeorgyPorgy БОРИКА няма да очакват всички търговци да използват един и същи споделен публичен ключ за верификация на съобщенията от платежният сървър. Това е просто един пример.

@dimitarminchev
Copy link
Author

За валидация на отговора се използва публичния ключ от:
https://3dsgate-dev.borica.bg/MPI_OW_APGW.zip

@dimitarminchev
Copy link
Author

Както БОРИКА, така и Търговеца имате двойка ключове публичен и частен.

БОРИКА подписва съобщенията с частния ключ, а вие ги верифицирате с публичният им ключ:
https://3dsgate-dev.borica.bg/MPI_OW_APGW.zip

Търговеца подписва с частният ключ (.key), а Борика верифицира с публичния му ключ (.cer)

@top20oferti
Copy link

Здравейте,
пробваме да имплентираме вашият код, borica_request.php работи успешно, но при borica_response.php получаваме следната грешка:
SSL Verification Error!
error:04091077:rsa routines:INT_RSA_VERIFY:wrong signature length
Кода се ползва 1:1 без да се променя нещо по него.

Ще може ли да ни дадете някакви насоки от какво може да се получава тази грешка?

Предварително благодаря!

@dimitarminchev
Copy link
Author

Документацията на БОРИКА е достъпна в Интернет на адрес:
https://3dsgate-dev.borica.bg/

@dimitarminchev
Copy link
Author

dimitarminchev commented May 13, 2021 via email

@stafanakos
Copy link

stafanakos commented May 14, 2021 via email

@stefanvel
Copy link

Здравейте, след изпозването на кода от вас имам следния проблем.

дава ми грешка Payment Error -19
[STATUSMSG] => Authentication failed. The error has occurred on ACS or Directory Server

Това е опит за плащане с тестовите карти които не изискват тество ACS

При опит с карта която изисква ACS всички минава коректно.

можете ли да помогнете?

@budiony
Copy link

budiony commented Aug 19, 2021

Здравейте, след изпозването на кода от вас имам следния проблем.

дава ми грешка Payment Error -19
[STATUSMSG] => Authentication failed. The error has occurred on ACS or Directory Server

Това е опит за плащане с тестовите карти които не изискват тество ACS

При опит с карта която изисква ACS всички минава коректно.

можете ли да помогнете?

При някои от тестовите карти се получава този проблем, не е нищо обезпокоително - на продукционната среда нещата работят нормално. Ползвайте друга тестова карта.

@stefanvel
Copy link

Здравейте, след изпозването на кода от вас имам следния проблем.
дава ми грешка Payment Error -19
[STATUSMSG] => Authentication failed. The error has occurred on ACS or Directory Server
Това е опит за плащане с тестовите карти които не изискват тество ACS
При опит с карта която изисква ACS всички минава коректно.
можете ли да помогнете?

При някои от тестовите карти се получава този проблем, не е нищо обезпокоително - на продукционната среда нещата работят нормално. Ползвайте друга тестова карта.

Благодаря и от банката това ми казаха, че имало проблеми с карти на тестовата среда. Благодаря много!

@ljube
Copy link

ljube commented Sep 13, 2021

Здравейте,
благодаря Ви за свършената работа и насоките!
При мен работи нормално всичко > request - стигам до терминала, плащам и ме връща обратно нa request url, както е зададен предварително от банката. Даже минава бързо през екран, който закачам за справка.
Screenshot 2021-09-13 at 8 23 49

Проблемът е, че post масивът е празен след като стигнем на request url-a! Да сте имали такъв случай?
Поздрав

@ValentinDamyanov
Copy link

Здравейте,
благодаря Ви за свършената работа и насоките!
При мен работи нормално всичко > request - стигам до терминала, плащам и ме връща обратно нa request url, както е зададен предварително от банката. Даже минава бързо през екран, който закачам за справка.
Screenshot 2021-09-13 at 8 23 49

Проблемът е, че post масивът е празен след като стигнем на request url-a! Да сте имали такъв случай?
Поздрав

Понякога на dev средата предупреждава, че връзката не е secure и прави повече от 1 redirect.
Възможно е и при вас да се е получило.
При мен го хванах директно с var_dump($_REQUEST); die;

@ljube
Copy link

ljube commented Sep 20, 2021

Здравейте, Валентин.
Благодаря ви за този отговор. Пуснахме успешно терминала. Да, в последствие и аз засякох повече от един рикуест. Трябваха за съжаление обаче повече тестове, за да се забележи. :(
Поздрав и чуден ден.

@dimitarminchev
Copy link
Author

Актуализация за версия 4.0.
За повече информация: https://3dsgate-dev.borica.bg/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment