Hahn Air NDC 2017.2 API – Payment Data Encryption

 

The documentation below applies to all transactions where a payment card is used.

 

The sensitive credit card information sent in the NDC request has to be encrypted in order to be accepted and processed. This includes the Credit Card Number (CCN) and Card Security Code (CSC) when applicable, which have to be converted into a Base64-encoded string.

 

In case the payment card has a Card Security Code (CVC/CVV), both the CCN and the CSC have to be encrypted and sent with a comma ',' separator:

encryptedCCN,encryptedCSC
 

In case the payment card does not have a Card Security Code (CVC/CVV), do not encrypt and send the CSC as an empty string; encrypt only the CCN and send it as a single encrypted string:

encryptedCCN
 

To encrypt the sensitive payment card information, use the provided public key and RSA.

If you have not received your public key, please contact us at: ndc@hahnair.com

 

Java Example

1) Create a PublicKey instance

#1 Java Example
String cert = "yourCertInTextFormat";
byte[] keyBytes = Base64.getDecoder().decode(cert);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey = kf.generatePublic(spec);

2) Use the PublicKey instance to encrypt the data

#2 Java Example
String ccn = "creditcardnumbertoencrypt";
String csc = "cardsecuritycodetoencrypt";
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
String encryptedCCN = Base64.getEncoder().encodeToString(cipher.doFinal(ccn.getBytes(StandardCharsets.UTF_8)));
String encryptedCSC = Base64.getEncoder().encodeToString(cipher.doFinal(csc.getBytes(StandardCharsets.UTF_8)));

3) Concatenate to correct format

#3 Java Example
StringBuilder encryptedResult = new StringBuilder();
String encryptedRes = encryptedResult.append(encryptedCCN).append(",").append(encryptedCSC).toString();
 

Python Example

1) Import the Public Key from a string or a file

#1 Python Example
key = base64.b64decode(keyString)
key = RSA.importKey(key)

2) Use PyCryptodome and the imported Public Key to encrypt the data

#2 Python Example
encryptor = PKCS1_v1_5.new(key=key)
encrypted_ccn = encryptor.encrypt(ccn.encode('utf-8'))
encrypted_csc = encryptor.encrypt(csc.encode('utf-8'))
encoded_encrypted_ccn = base64.b64encode(encrypted_ccn)
encoded_encrypted_csc = base64.b64encode(encrypted_csc)

3) Concatenate to correct format

#3 Python Example
print encoded_encrypted_ccn + "," + encoded_encrypted_csc
 

Usage

The encrypted data for a payment card with a Card Security Code is then sent in the request as shown below:

Sample: Payment Card with a Card Security Code
...
<PaymentCard>
	<CardCode>CA</CardCode>
	<CardHolderName>Cardholder Name</CardHolderName>
	<CardHolderBillingAddress>
		<Street>House on a Street</Street>
		<Street>200 Street Lane</Street>
		<CityName>London</CityName>
		<PostalCode>AB12CD</PostalCode>
		<CountryCode>GB</CountryCode>
	</CardHolderBillingAddress>
	<EffectiveExpireDate>
		<Effective>1212</Effective>
		<Expiration>0125</Expiration>
	</EffectiveExpireDate>
	<TokenizedCardNumber>
Ss24+d85nZ7cYz4efQLTJg/k6g45sur0PNr5ObIp2b4+RB09JucFBfFGRVKdK+WWMEJ+13WFA9/6/KFbSmqbQSp4tv35PYcbwcWbNZP01ZpT0kugM/K7WV76xueNyLr1tbCIAoQ4Ej0NJQ3HBrGSp28Fild2flggMFuCYY1S+eElpyzLB5wE2X2OEBBMGsv/RYE7VGh+2zNq6+6ic0hSECRhqOqZpCcmND3+30ulYbuF8QUcANUr8ofarlk/fwRa4lZE0dU3L7uQAzGLSinzwYE7hcm8cUZVXxoKZwkktBJlxriaBcaWFeEdcq3xeM3TK1XtnFOByNfzUSnJq0XUuw==,WC5imjdN/k/GPRad3tTYtA04107FwpfcXJfc25jMutyyOl073rKOStsJUYq03aDYg0FwmkfpiiWH/AMplCKoA4pFOa2Pe5+ueT1b9sTWb9GGSMf9gDvOrMJkmr8G/xI1FDUXGD5vRswqV3n+ga2LzCIf6P7932wPRvVPycjOQq3zWD85+CLeBdSr/bYjHX/sN96QCUoma37C8vn1RizE0iVVpZGYYx51IGFIabIx0WB9UmVLdCNTHoEeTP4BiOPBSYECz2yh3U65Z9lWcD79gRRKTvtWNSUw2+4A+nPHi9kJD76IFFn8HuCZXx5AOZalXC7c7ADKcURjB3DRLsMYrw==
	</TokenizedCardNumber>
</PaymentCard>
...

The encrypted data for a payment card without a Card Security Code is then sent in the request as shown below:

Sample: Payment Card without a Card Security Code
...
<PaymentCard>
	<CardCode>CA</CardCode>
	<CardHolderName>Cardholder Name</CardHolderName>
	<CardHolderBillingAddress>
		<Street>House on a Street</Street>
		<Street>200 Street Lane</Street>
		<CityName>London</CityName>
		<PostalCode>AB12CD</PostalCode>
		<CountryCode>GB</CountryCode>
	</CardHolderBillingAddress>
	<EffectiveExpireDate>
		<Effective>1212</Effective>
		<Expiration>0125</Expiration>
	</EffectiveExpireDate>
	<TokenizedCardNumber>
TSjYjp3J78ZvMiygBpqmUKcudWj8dDP8nN8fGs8z2+aGY9Ah2X+MAeWRjyUb87hB/Cg0Oeg9BmZREBR8t60wzjd9Qqwvi3NZAQz7qo46Ol/GNrGdbB5Od2EQYWzr+e66l4gmbeG/V9DkA7aMrK5yFCm9IGaSluPqc53uU5CNBoBYUAxLigeXUTseXFH3xtagQb0OzCqj2k2o8Gw8Lm9WTML++dvUmYeuP8CSwN13v64pKtA5/IX9Uhk61I2KKCgP6U5NEc8HsgO3iOtO3k617TpTlW36gTgV34bWk2Kd520kAljvZr9mA6KKnHEWWkLsksjCehmj+dLwIySiQSUF3w==
	</TokenizedCardNumber>
</PaymentCard>
...

 

Test Credit Cards

You can use the following test credit cards in our test system:

Card CodeCard NumberSecurity Code
VI (Visa)4444333322221111123
CA (MasterCard)5555555555554444123
AX (American Express)3434343434343431234
DC (Diners Club)36148900647913*NONE*
TP (Airplus/UATP)122000000000003*NONE*

You can use any expiry date within 7 years in the future for these test credit cards.

 

Along with the test credit cards, you can also force a specific behavior in our test system with the following cardholder names:

Cardholder NameExpected behavior
AUTHORISEDAuthorization successful (any other cardholder name will have the same result)
REFUSEDAuthorization refused
ERRORAuthorization error