Withdraw cash from your bank account, check balance, and transfer funds to another bank account using your Aadhaar number and fingerprint scan
Transaction Flow
- Onboard your agents (users) by initiating Onboard Agent API.
- Activate AEPS services for your agents using Activate AePS Fingpay API.
- Perform your agent's one-time eKYC for Fingpay by calling the following APIs in the same order:
- Agents also need to perform Daily Authentication using Daily E-KYC (AePS Fingpay) API. Keep in mind that Daily KYC will only be successful after 3 days of performing the one-time eKYC.
- Once the agent has completed the one-time and Daily KYC successfully, they can proceed to perform a transaction.
Transaction (Service) Types
Following are the parameters for different transaction types:
- service_type = 2 (Cash Withdrawal)
- service_type = 3 (Balance Inquiry)
- service_type = 4 (Mini Statement)
- service_type = 5 (Aadhaar Pay)
Note:
- Due to recent compliance every Cash Withdrawal transaction would require 2FA
- In the Cash Withdrawal transaction a new parameter name as reference_id has to be passed as a parameter (returned in response to Merchant Authentication API)
List of banks available with bank ids and bank codes is available in the following sheet:
Encryption process for aadhaar parameter:
- Decode public key using base64 encoding technique
- Compute RSA encrypted signature using decoded key and message.
- Encode encrypted signature with base64 encoding to send message on API
Public key for aadhar encryption (PRODUCTION) -
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCaFyrzeDhMaFLx+LZUNOOO14Pj9aPfr+1WOanDgDHxo9NekENYcWUftM9Y17ul2pXr3bqw0GCh4uxNoTQ5cTH4buI42LI8ibMaf7Kppq9MzdzI9/7pOffgdSn+P8J64CJAk3VrVswVgfy8lABt7fL8R6XReI9x8ewwKHhCRTwBgQIDAQAB
Note:
If the merchant/retailer is not onboarded on Eko's system is not allowed to do the transaction. They need to enroll themselves followed by the service activation. Kindly refer to the section Agent Management API.
public static String calculateRSA( String salt ) throws InvalidKeyException, Exception {
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, getPublicKey());
byte[] secretMessageBytes = salt.getBytes("UTF-8");
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
String encodedMessage = Base64.encodeBase64String(encryptedMessageBytes);
return encodedMessage;
}
public static PublicKey getPublicKey() throws Exception {
String rawPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCXa63O/UXt5S0Vi8DM/PWF4yugx2OcTVbcFPLfXmLm9ClEVJcRuBr7UDHjJ6gZgG/qcVez5r6AfsYl2PtKmYP3mQdbR/BjVOjnrRooXxwyio6DFk4hTTM8fqQGWWNm6XN5XsPK5+qD5Ic/L0vGrS5nMWDwjRt59gzgNMNMpjheBQIDAQAB";
byte[] keyBytes = Base64.decodeBase64(rawPublicKey);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);// generatePrivate(spec);
}
<?php
$curl = curl_init();
$aadhar_no = "123412341234";
$amount = "500";
$pdata = $_POST['pdata'];
$lat = "81";
$lon = "81";
$txn_date = date("Y/m/d");
$txn_time = date("H:i:s");
$timestamp = date("Y-m-s H:i:s");
$MerchantKey = $_SESSION['MerchantKey'];
$SessionToken = $_SESSION['SessionToken'];
$UserName = $_SESSION['UserName'];
$user_key = "20810200";
$key = "f74c50a1-f705-4634-9cda-30a477df91b7";
$encodedKey = base64_encode($key);
$secret_key_timestamp = round(microtime(true) * 1000);
$data = $secret_key_timestamp.$aadhar_no.$amount.$user_key;
$signature_secret_key = hash_hmac('SHA256', $secret_key_timestamp, $encodedKey, true);
$signature_req_hash = hash_hmac('SHA256', $data, $encodedKey, true);
$secret_key = base64_encode($signature_secret_key);
$request_hash = base64_encode($signature_req_hash);
$public_key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCXa63O/UXt5S0Vi8DM/PWF4yugx2OcTVbcFPLfXmLm9ClEVJcRuBr7UDHjJ6gZgG/qcVez5r6AfsYl2PtKmYP3mQdbR/BjVOjnrRooXxwyio6DFk4hTTM8fqQGWWNm6XN5XsPK5+qD5Ic/L0vGrS5nMWDwjRt59gzgNMNMpjheBQIDAQAB';
$search = [
"-----BEGIN PUBLIC KEY-----",
"-----END PUBLIC KEY-----",
"\n",
"\r",
"\r\n"
];
$public_key_resource = $search[0] . PHP_EOL . wordwrap($public_key, 64, "\n", true) . PHP_EOL . $search[1];
openssl_public_encrypt($aadhar_no, $signature, $public_key_resource, OPENSSL_SSLV23_PADDING);
$encrypted_aadhar = base64_encode($signature);
echo $secret_key;
echo "<br />";
echo "<br />";
echo $secret_key_timestamp;
echo "<br />";
echo "<br />";
echo $request_hash;
echo "<br />";
echo "<br />";
echo $pdata;
echo "<br />";
echo "<br />";
echo "Encrypted Aadhar :";
echo "<br />";
echo $encrypted_aadhar;
echo "<br />";
echo "<br />";
echo "cURL Response :";
echo "<br />";
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://staging.eko.in:25004/ekoapi/v2/aeps',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS =>'{
"service_type": "2",
"initiator_id": "9962981729",
"user_code": "20810200",
"customer_id": "9999999999",
"bank_code": "SBIN",
"amount": "'.$amount.'",
"client_ref_id": "AEPS1",
"pipe": "0",
"aadhar": "'.$encrypted_aadhar.'",
"latlong":"81,81,12",
"notify_customer": "0",
"piddata": "<PidData> <Data"
}',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'developer_key: becbbce45f79c6f5109f848acd540567',
'secret-key: '.$secret_key.'',
'secret-key-timestamp: '.$secret_key_timestamp.'',
'request_hash: '.$request_hash.''
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
?>
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System.Security.Cryptography;
#region Aadhar Encryption Process
try
{
var keyBytes = Convert.FromBase64String("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCXa63O/UXt5S0Vi8DM/PWF4yugx2OcTVbcFPLfXmLm9ClEVJcRuBr7UDHjJ6gZgG/qcVez5r6AfsYl2PtKmYP3mQdbR/BjVOjnrRooXxwyio6DFk4hTTM8fqQGWWNm6XN5XsPK5+qD5Ic/L0vGrS5nMWDwjRt59gzgNMNMpjheBQIDAQAB
");
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
byte[] plaintext = Encoding.UTF8.GetBytes(ObjRequest.AadharNo);
byte[] ciphertext = rsa.Encrypt(plaintext, false);
string cipherresult = Convert.ToBase64String(ciphertext);
ObjRequestData.aadhar = cipherresult;
}
catch (Exception ex)
{ }
#endregion
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
public_key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCaFyrzeDhMaFLx+LZUNOOO14Pj9aPfr+1WOanDgDHxo9NekENYcWUftM9Y17ul2pXr3bqw0GCh4uxNoTQ5cTH4buI42LI8ibMaf7Kppq9MzdzI9/7pOffgdSn+P8J64CJAk3VrVswVgfy8lABt7fL8R6XReI9x8ewwKHhCRTwBgQIDAQAB'
# Decode public key and create a key object
public_key_bytes = base64.b64decode(public_key)
public_key_obj = RSA.import_key(public_key_bytes)
# Aadhar number to be encrypted
aadhar_no = '123456789012' # Replace with your Aadhar number
# Encrypt Aadhar number using public key
cipher = PKCS1_v1_5.new(public_key_obj)
signature = cipher.encrypt(aadhar_no.encode())
encrypted_aadhar = base64.b64encode(signature).decode()
print(f"Encrypted Aadhar: {encrypted_aadhar}")
/**
* Sample Function to capture fingerprint from RDService based biometric device in the browser (using JQuery).
*
* @param {string} capture_url RDService URL to call for capture. Eg: http://127.0.0.1:11100/capture or http://127.0.0.1:11100/rd/capture
* @param {function} callbackFunction The function to call back on capture success or failure
*
* @return {Object} response Object containing the response details.
* @return {boolean} response.httpSuccess Whether AJAX request successful.
* @return {boolean} response.captureSuccess Whether fingerprint capturing successful.
* @return {number} response.captureQuality Quality of fingerprint scan if captureSuccess is true.
* @return {number} response.errCode Error code returned by RDService if captureSuccess is false.
* @return {string} response.errInfo Error details returned by RDService if captureSuccess is false.
* @return {string} response.textStatus HTTP status if httpSuccess is false.
* @return {string} response.errorThrown Error details if httpSuccess is false.
*/
function capturefingerprint(capture_url, callbackFunction) {
var doc = document.implementation.createDocument("", "", null);
var pidOptionsElem = doc.createElement("PidOptions");
var optsElem = doc.createElement("Opts");
optsElem.setAttribute("fCount", 1);
optsElem.setAttribute("fType", 2);
optsElem.setAttribute("iCount", 0);
optsElem.setAttribute("pCount", 0);
optsElem.setAttribute("format", 0);
optsElem.setAttribute("pidVer", "2.0");
optsElem.setAttribute("timeout", 10000);
optsElem.setAttribute("posh", "UNKNOWN");
optsElem.setAttribute("env", "P");
optsElem.setAttribute("wadh", "E0jzJ/P8UopUHAieZn8CKqS4WPMi5ZSYXgfnlfkWjrc=");
pidOptionsElem.appendChild(optsElem);
doc.appendChild(pidOptionsElem);
$.ajax({
url: capture_url,
type: 'CAPTURE',
data: doc,
processData: false,
success: function (response) {
var doc2;
var result = {
httpSuccess: true
};
if (typeof response === 'string') {
doc2 = (new DOMParser()).parseFromString(response, 'text/xml');
result.pid_data = response;
} else {
doc2 = response;
result.pid_data = (new XMLSerializer()).serializeToString(response);
}
var resp = doc2.getElementsByTagName("Resp");
result.errCode = resp[0].getAttribute('errCode');
if (errCode != 0) {
result.captureSuccess = false;
result.errInfo = resp[0].getAttribute('errInfo');
} else {
result.captureSuccess = true;
result.captureQuality = parseInt(resp[0].getAttribute('qScore'));
}
callbackFunction(result);
},
error: function (jqXHR, textStatus, errorThrown) {
callbackFunction({
httpSuccess: false,
captureSuccess: false,
textStatus: textStatus,
errorThrown: errorThrown
});
}
});
}
Sample PidData after serialization
<PidData>
<Data type=\\\"X\\\">MjAyMS0xMi0wNlQwNjo0ODoyM51MswAVy/PSZSqgXRudRMXTP356NsUJ4q4qItIZDjYZfOD4zwDpqlKT4aU927NvMJm6+IxgTTZEFngZ0TY+ibqUeQQiB7JEwj+8xeeO+7Y+G4yJmL7FjqNfQG7O/AgMP8AXWFS522iUeGmqkyf7InpXIj7VCiKlNDu+QNu+donxOQ6hRlNJ5J0kc082Fct4hvEr4iFVL9UrAx1+kR8yDbLAISxc+37RI7cxn577uNsazb/jy7O+tTk9zE6Ia3uPY0uqKfMNqNhSTomHMeJo5S0wuOW7uyRYrvz8Q4hUlCuqsup0ZmKA5su1F7HU60q6DHkO0eH+mk8Q/gA8ymaEHeBadkd+i/8tTZnxDyp/gw+z3vT+MPZNLKT4WAE5w2nhOoOOS+F9NPpJ2mn+FqwkRQ7VnEv09g0/bTyAZHCvmhUAy/NHYVYEPaycRpoSIzGuA3REoX++HaOetubgBx9vVVIl5ZKAR4hlwzuGJbcFlyJh6uxjQIK1bJh/jPJH/m+WyJpZwc99IArg9hnBkUFTO42ZeaHXj7+4BNDahs6vFtoquJGFXHYkZUZ866itGA6Dh8iJCjIjd8f6IEah1U+SKX0mkAzh4IM91lw6sIeNpykMwk99FTDJ33vkW+ao1mf/LK8yWV3hisEGqQtCAhn3S71lpf/pJk4TOVLIJKwqnlcsin6mWcLr3Ve8xkWvJW8dRVfZ+KbQTpdOU4oQs3iv4jl0ORbuJJhVC2IEOGpUfpQyS8OO0x8D7RTvl+L3FmtKIXNZ7qlfkYdxKnH3a0QSvFY8KFpxOhUKcQXqIWU/Yuhtldgjb6D2Ys2QvgR99kRhDF7449fbqkQnnNF8uIGX3MIailsw/JFcTiko22s6in6gzPuv7AoPl1XIfDURnvQQnxlGb3MArO2rD48SVmNpwYGBnGTTyV1OYl9EbrJ1NPVcoXFCt6tcDDXwnhB09iJK7PWmKuIe5wP8dGWK/kgdYuEYRmsso+niYg2zUwdt41JGFPVJR0eCwNCOcmQ2uocPGmukB3h0h6dR1mMCTYBPUyNIxQznMxGAj72N0yHXwCS5FR6Qih8sYTDA/x1OOnuANK/m7CuCJ6H+tv48PU6vubtQAfxKCXQMiUZp0qbc</Data>
<DeviceInfo dc=\\\"79bfab62-5fd1-4ea1-bf5c-00cc714ae571\\\" dpId=\\\"MANTRA.MSIPL\\\" mc=\\\"MIIEGjCCAwKgAwIBAgIGAX0iDKcNMA0GCSqGSIb3DQEBCwUAMIHqMSowKAYDVQQDEyFEUyBNYW50cmEgU29mdGVjaCBJbmRpYSBQdnQgTHRkIDcxQzBBBgNVBDMTOkIgMjAzIFNoYXBhdGggSGV4YSBvcHBvc2l0ZSBHdWphcmF0IEhpZ2ggQ291cnQgUyBHIEhpZ2h3YXkxEjAQBgNVBAkTCUFobWVkYWJhZDEQMA4GA1UECBMHR3VqYXJhdDEdMBsGA1UECxMUVGVjaG5pY2FsIERlcGFydG1lbnQxJTAjBgNVBAoTHE1hbnRyYSBTb2Z0ZWNoIEluZGlhIFB2dCBMdGQxCzAJBgNVBAYTAklOMB4XDTIxMTEyNTEyMjEzNVoXDTIxMTIxNTA1MjMxMlowgbAxJTAjBgNVBAMTHE1hbnRyYSBTb2Z0ZWNoIEluZGlhIFB2dCBMdGQxHjAcBgNVBAsTFUJpb21ldHJpYyBNYW51ZmFjdHVyZTEOMAwGA1UEChMFTVNJUEwxEjAQBgNVBAcTCUFITUVEQUJBRDEQMA4GA1UECBMHR1VKQVJBVDELMAkGA1UEBhMCSU4xJDAiBgkqhkiG9w0BCQEWFXN1cHBvcnRAbWFudHJhdGVjLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALwP8kOTVlcHl1sOaXo3e1DLJJMRdrfrVOsAsgkrZQNnppVB8c9hY0OPP9ncWNl7SMobrrA6k2lSI0RDd1/4vd11ZUlic/IaHkNJQ6XJHE+Cwl8r2zVlAIO/cyibm7oPZRuf6TFzwrxltxuRL9J81lT/bm53injV8Six7Zg0pYqoJ1kwXqxF3ql+T3SCDp9zUfohwC60X3lFnqxOLgrIH430exaSsRl1UjnaDbU71ihme1yXDeVkKaQNIlTXbj1RJNXeHJpJMEpDyld54oHTmvSIsZip/QmT8rzUHcRjuois71J4ZLV836AyncYBvWqfdr4ETJ3DpSIA1gLw8w8lpw8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAG+TPUnji6Ziyge9bIr9fEQ/4rnB2JaJXrnUxkuL3WG/vpCHBwWuSw67nHo4CzQx7XUgrA8sWZPnL1JFViWaFN1kKy/4veUtuMF12gBDwAs9egUDqb1jijcg7V9gv6aegftq0cXH6mU6qhUCtDm8Q5jMBn2v3FpQBXcTNZa4jWUDy2TkrKwssMlBFB3Jz+k2rMe1jsFc0ZoFyCmAqU7Vb80tRO0/RC4zjmO3YQXpUYjxd6frh3LRSl6626P6Qpf/szX4bpZp3s4Kuf1J/M7wF2/pcQfcoypXPv3iDlaqOU3VbjOgNCj3OHmJjabmin0mFbwN08pFNthFGvv8Xxe6Hkw==\\\" mi=\\\"MFS100\\\" rdsId=\\\"MANTRA.AND.001\\\" rdsVer=\\\"1.0.5\\\">
<additional_info>
<Param name=\\\"srno\\\" value=\\\"2174371\\\"/>
<Param name=\\\"sysid\\\" value=\\\"356922093643968\\\"/>
<Param name=\\\"ts\\\" value=\\\"2021-12-06T06:48:25+05:30\\\"/>
</additional_info>
</DeviceInfo>
<Hmac>fTun00oL2ZVMzDRTWVcQA2mCbD6lk9JHLaBtOM3YiNrXUDvVdmemY9WplASeCJ8Z</Hmac>
<Resp errCode=\\\"0\\\" errInfo=\\\"Capture Success\\\" fCount=\\\"1\\\" fType=\\\"0\\\" iCount=\\\"0\\\" iType=\\\"0\\\" nmPoints=\\\"35\\\" pCount=\\\"0\\\" pType=\\\"0\\\" qScore=\\\"83\\\"/>
<Skey ci=\\\"20221021\\\">agdti+4wZR9bKRVEHGMknQRUqTiFbloF4HoY7ko1GoVTj7uDMklfgkS1C/NljxJgnYyPY3TRjpvBHtOOuYKxbN/nD1zZxKcGokwENOIyAU6Ac+jm+iqeuNA7AWS/l/fDNs+70pRNg5ZeVh5eDQ8p6mDnPxyu0V7YanOfyD8PDim3YgXiI2Isg4NFJAsY8vGAKBygp6+L7lg8vu9QPFS5vin1KE0eAUyruhrsoNl8r1a0O2tQUssra+JO4aqdjIlgtp+Xlgx9sot43UpCa7HVdF+ASJXojptQYaC6yAoZj9HgrBQm2SmV6U/5J5krcr/75LBxPKQumuiuh+3WktTdnQ==</Skey>
</PidData>
Things to consider for PidData
The PidData should contain Data type="X" (for XML). If this comes as P (for Protobuf), make sure you send pid format as 0 when sending a capture request.
The PidData should contain DeviceInfo with a mc parameter. The mc parameter is the device public key certificate signed by Device Provider Key (To verify your certificate, visit https://www.sslshopper.com/certificate-decoder.html).
For more details, refer Section 2.3 PID Creation – Signing and Encrypting Biometrics from UIDAI guidelines.
https://uidai.gov.in/images/resource/aadhaar_registered_devices_2_0_09112016.pdf
Update value of fType parameter from 0 to 2
As per the strict guidelines of NPCI on 'FIR-FMR single PID block implementation in Aadhar-based biometric authentication. We need to capture the fingerprint data of our end-user with an updated PID fType value. In order to do that Frontend code which is responsible to capture finger data, needs to pass value of fType = 2 rather than 0; this will return PID XML with the updated fingerprint data format and Same can be passed over in Eko's API. Please refer the updated sample code snippet of capture fingerprint code.
The list of banks are not live with the FMR+FIR and hence the value of fType =0 instead of 2 for the mentioned banks.
It is recommended to first try out the APIs using postman.
If you face any issues with the response of any of the APIs, only the response that you received in postman will be acceptable for debugging. Send your postman response to [email protected]