The Predefined Voucher API document describes the API requirements for Predefined Voucher to integrate with Bitaqaty Business to redeem vouchers and get info about it
Predefined Voucher is Provide that have two main methods via URLs
1-Redeem voucher : that let you redeem vouchers via Bitaqaty Business
2-Get Voucher Information: that let you check status of request that you perform to redeem voucher usig Redeem voucher depending on request number that created aopn request
Bitaqaty Business voucher system allows Bitaqaty Business merchants who don’t have Epins to get paid for their services through a voucher system. Bitaqaty Business will generate the voucher codes - which have predefined prices - which Bitaqaty Business users will then be able to use on the merchant website and get the desired service. This document illustrates the technical considerations that need to be understood by the merchant’s technical team before implementing the voucher system
Environment | URL | Method |
---|---|---|
Staging | https://www.ocstaging.net/remote/voucher/secureRedeemVoucher | |
Production | https://www.netader.com/remote/voucher/secureRedeemVoucher |
Note:All parameters are required (NOT case sensitive)
Parameter Name | Data Type | Description | Validation |
---|---|---|---|
merchantId | Long | The merchant identifier given to the merchant upon registration i.e. will be provided upon integration | |
voucherCode | MD5 Hash (NOT Case sensitive) | The MD5 hash of the voucher code that the user entered, and the merchant would like to redeem | |
redeemRefNumber | String | This is the transaction reference number generated by merchant, and can contain any string up to 250 characters long | |
hashKey | MD5 hash | MD5 digest for a string, composed as follows in order:
MD5(merchantId+ voucherCode+ redeemRefNumber+Transkey) |
Note:All the response messages will be encrypted unless otherwise specified in the error codes reference, for error codes details refer to the Response Codes reference
Parameter Name | Data Type | Description |
---|---|---|
ACK | String | Acknowledgement for the request status (“success”, or “test”) |
TRANSACTIONTIME | Long | Date and Time of the Redeem Voucher process |
AMOUNT | Double | Value of the voucher |
CURRENCY | String | Currency of the voucher value |
TRXREFNUMBER | String | Transaction Reference Number which was previously sent by merchant |
Note :
It is expected to give the user a fake service upon redeeming a “test” voucher, because “test” vouchers are not billable
Parameter Name | Data Type | Description |
---|---|---|
ACK | String | Acknowledgement for the request status (“error”) |
ERRORCODE | Integer | For error codes details, seeResponse Codes |
TRANSACTIONTIME | Long | Date and Time of the Redeem Voucher process |
Environment | URL | Method |
---|---|---|
Staging | https://www.ocstaging.net/remote/voucher/getVoucherInfo |
GET |
Production | https://www.netader.com/remote/voucher/getVoucherInfo |
GET |
Note:All parameters are required (NOT case sensitive)
Parameter Name | Data Type | Description | Validation |
---|---|---|---|
merchantId | Long | The merchant identifier given to the merchant upon registration i.e. will be provided upon integration | |
voucherCode | MD5 Hash (NOT Case sensitive) | The hashed voucher code that the user entered |
Note:All the response messages will be encrypted unless otherwise specified in the error codes reference (For error codes details, Refer to theResponse Codes reference
Parameter Name | Data Type | Description |
---|---|---|
ACK | String | Acknowledgement for the request status (“success”, or “test”) |
CREATED | UNIX Timestamp (Seconds) | Date and Time of the generated voucher (The voucher is generated upon purchasing) |
EXPIRES | UNIX Timestamp (Seconds) | Expiry date (Defined in the product form) |
REDEEMED | Integer | Voucher redeem status (Boolean: 0=Not redeemed, 1=Redeemed) |
REDEEMEDTIME | UNIX Timestamp (Seconds) | Date and Time of redeeming the voucher, or 0 if the voucher is not Redeemed |
REVOKED | Integer | Voucher revoke status (Boolean: 0=Not revoked, 1=Revoked) |
REVOKETIME | UNIX Timestamp (Seconds) | Date and Time of revoking the voucher, and 0 if the voucher has not been revoked |
TRANSACTIONTIME | Long | Date and Time of the Get Voucher Information process |
AMOUNT | Double | Value of the voucher |
CURRENCY | String | Transaction Reference Number which was previously sent by merchant |
Note :
It is expected to give the user a fake service upon redeeming a “test” voucher, because “test” vouchers are not billable
Parameter Name | Data Type | Description |
---|---|---|
ACK | String | Acknowledgement for the request status (“error”) |
ERRORCODE | Integer | For error codes details, seeResponse Codes |
TRANSACTIONTIME | Long | Date and Time of the Redeem Voucher process |
from base64 import b64decode
from Crypto.PublicKey.RSA import construct
from Crypto.Cipher import PKCS1_v1_5
import urllib.parse
def get_encrypted_password(Message, modulus, exponent):
Message = password.encode('utf-8')
# decode base64 string to be used as modulus(n) and exponent(e) components for
# constructing the RSA public key object
modulus = b64decode(modulus)
exponent = b64decode(exponent)
n = int.from_bytes(modulus, byteorder='big')
e = int.from_bytes(exponent, byteorder='big')
pubkey = construct((n, e))
pubkey = PKCS1_v1_5.new(pubkey)
decrypted = pubkey.decrypt(Message)
# url encode the decrypted Message
decrypted = urllib.parse.quote_plus(decrypted)
return decrypted
using System;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
public class Decrypter_Demo
{
public static void Main()
{
String cipherBase64 =
"ftDu+uCnuLuUT3B4LRda4/AGytikCeqjMRiYXMQpD6ke/q34gNpKGSoy6LE3iWZsqmRjJvwmLlOL/18xBL8
Husbrznr49VInOAoQdDuz6hlOFc0ZKkIXwqL85+VXtvcrI7hFIpMx6GH6g3JFwl/onPrgpwXPSKv0QSUVU7Zts
SmZIB+N3iPSOExql3pEMlevJHlfRORLyWm89c+XAJOBpJ/zrySU8TJFQQCG8yC2Xgs9jId/XiWotbxxzKkrlV+C
LMdJPB8ssdZvL1G10bRSaMsvXwfjI0++lULlP1wxODLK/uKIPIn2fO74lVDLpypIccZ6V8uIH3Npvzby9Yk3EA==
";
byte[] toDecrypt = Convert.FromBase64String(cipherBase64);
String exponent = "65537";
String modulus =
"17351647085162023880524140696821193391617234522467416119520279846722377200624264360
33452546726020208213238917042559286607517857798798282878100571141399934349567741035
87381069300730783417658817177569243359846409782398780544082493130842876815624423879
54293952190814392698109440470743173876829707713309095085970816477219720193060852821
28250993147526994453891316833558033578249821239277175524233289625457897591806136265
75993645851062268797729847807560222165608442670025802446441279650279312221651705423
62134383685646432419214629632003145781051846091263095608139390995055600410646785041
443097434851817549490172865418409139";
BigInteger m = new BigInteger(modulus,10);
BigInteger e = new BigInteger(exponent,10);
RsaKeyParameters publicKey = new RsaKeyParameters(false, m, e);
IAsymmetricBlockCipher theEngine = new RsaEngine();
theEngine.Init(false, publicKey);
String plainText = Encoding.UTF8.GetString((theEngine.ProcessBlock(toDecrypt, 0, toDecrypt.Length)));
Console.WriteLine(plainText);
}
}
<?php
function rsa_encrypt($message, $public_key, $modulus, $keylength)
{
$padded = add_PKCS1_padding($message, true, $keylength / 8);
$number = binary_to_number($padded);
$encrypted = pow_mod($number, $public_key, $modulus);
$result = number_to_binary($encrypted, $keylength / 8);
return $result;
}
function rsa_decrypt($message, $private_key, $modulus, $keylength)
{
$number = binary_to_number($message);
$decrypted = pow_mod($number, $private_key, $modulus);
$result = number_to_binary($decrypted, $keylength / 8);
return remove_PKCS1_padding($result, $keylength / 8);
}
function rsa_sign($message, $private_key, $modulus, $keylength)
{
$padded = add_PKCS1_padding($message, false, $keylength / 8);
$number = binary_to_number($padded);
$signed = pow_mod($number, $private_key, $modulus);
$result = number_to_binary($signed, $keylength / 8);
return $result;
}
function rsa_verify($message, $public_key, $modulus, $keylength)
{
return rsa_decrypt($message, $public_key, $modulus, $keylength);
}
function rsa_kyp_verify($message, $public_key, $modulus, $keylength)
{
$number = binary_to_number($message);
$decrypted = pow_mod($number, $public_key, $modulus);
$result = number_to_binary($decrypted, $keylength / 8);
return remove_KYP_padding($result, $keylength / 8);
}
/*
* Some constants
*/
define("BCCOMP_LARGER", 1);
/*
* The actual implementation.
* Requires BCMath support in PHP (compile with --enable-bcmath)
*/
//--
// Calculate (p ^ q) mod r
//
// We need some trickery to [2]:
// (a) Avoid calculating (p ^ q) before (p ^ q) mod r, because for typical RSA
// applications, (p ^ q) is going to be _WAY_ too large.
// (I mean, __WAY__ too large - won't fit in your computer's memory.)
// (b) Still be reasonably efficient.
//
// We assume p, q and r are all positive, and that r is non-zero.
//
// Note that the more simple algorithm of multiplying $p by itself $q times, and
// applying "mod $r" at every step is also valid, but is O($q), whereas this
// algorithm is O(log $q). Big difference.
//
// As far as I can see, the algorithm I use is optimal; there is no redundancy
// in the calculation of the partial results.
//--
function pow_mod($p, $q, $r)
{
// Extract powers of 2 from $q
$factors = array();
$div = $q;
$power_of_two = 0;
while (bccomp($div, "0") == BCCOMP_LARGER) {
$rem = bcmod($div, 2);
$div = bcdiv($div, 2);
if ($rem) {
array_push($factors, $power_of_two);
}
$power_of_two++;
}
// Calculate partial results for each factor, using each partial result as a
// starting point for the next. This depends of the factors of two being
// generated in increasing order.
$partial_results = array();
$part_res = $p;
$idx = 0;
foreach ($factors as $factor) {
while ($idx < $factor) {
$part_res = bcpow($part_res, "2");
$part_res = bcmod($part_res, $r);
$idx++;
}
array_push($partial_results, $part_res);
}
// Calculate final result
$result = "1";
foreach ($partial_results as $part_res) {
$result = bcmul($result, $part_res);
$result = bcmod($result, $r);
}
return $result;
}
//--
// Function to add padding to a decrypted string
// We need to know if this is a private or a public key operation [4]
//--
function add_PKCS1_padding($data, $isPublicKey, $blocksize)
{
$pad_length = $blocksize - 3 - strlen($data);
if ($isPublicKey) {
$block_type = "";
$padding = "";
for ($i = 0; $i < $pad_length; $i++) {
$rnd = mt_rand(1, 255);
$padding .= chr($rnd);
}
} else {
$block_type = "";
$padding = str_repeat("ÿ", $pad_length);
}
return "" . $block_type . $padding . "" . $data;
}
//--
// Remove padding from a decrypted string
// See [4] for more details.
//--
function remove_PKCS1_padding($data, $blocksize)
{
assert(strlen($data) == $blocksize);
$data = substr($data, 1);
// We cannot deal with block type 0
if ($data[0] == '\\0') {
die("Block type 0 not implemented.");
}
// Then the block type must be 1 or 2
assert($data[0] == "" || $data[0] == "");
// Remove the padding
@ $offset = strpos($data, "", 1);
return substr($data, $offset + 1);
}
//--
// Remove "kyp" padding
// (Non standard)
//--
function remove_KYP_padding($data, $blocksize)
{
assert(strlen($data) == $blocksize);
$offset = strpos($data, "");
return substr($data, 0, $offset);
}
//--
// Convert binary data to a decimal number
//--
function binary_to_number($data)
{
$base = "256";
$radix = "1";
$result = "0";
for ($i = strlen($data) - 1; $i >= 0; $i--) {
$digit = ord($data[$i]);
$part_res = bcmul($digit, $radix);
$result = bcadd($result, $part_res);
$radix = bcmul($radix, $base);
}
return $result;
}
//--
// Convert a number back into binary form
//--
function number_to_binary($number, $blocksize)
{
$base = "256";
$result = "";
$div = $number;
while ($div > 0) {
$mod = bcmod($div, $base);
$div = bcdiv($div, $base);
$result = chr($mod) . $result;
}
// return str_pad($result, $blocksize, "", STR_PAD_LEFT);
return $result;
}
class Decryptor
{
function decr ($message){
// $message ="";
$exponent ="65537";
$modulus ="21650920717861820200309457818827666830515852541149207679712388457073109773891339987025281468722461832325381340320397482789126363789856598447052958284180470492747489234630818039536632113542364241672892541562479704609485200396003818918925229967777934252843480951220660278279267492610531785287944504141673671501379673568122185476028960979647660932408455489675388159044654907976747223595352885497122178261219223568129259280443830090030468542683267766957704969457260019785799629518263274096531060604970796123544173853692472206375529282083870622580953560577998514170492073707224441008438464866094124933850412658664409250107";
// echo();
return rsa_decrypt(base64_decode($message), $exponent, $modulus, 2048);
// echo("<br><br><br>");
// echo(base64_decode($message));
}
$Merchant ='12151656';
$voucherCode = 2126516151;
$redeemRefNumber = 'FR51456156';
$hashKey = 's1c4dsc45d1c1ds65c1asc1s1c65sa';
//The URL with parameters / query string.
$url = 'https://www.ocstaging.com/remote/voucher/secureRedeemVoucher?merchantId='.$Merchant.'&voucherCode='.$voucherCode.'&redeemRefNumber='.$redeemRefNumber.'&hashKey='.$hashKey.'';
//Once again, we use file_get_contents to GET the URL in question.
$contents = file_get_contents($url);
//If $contents is not a boolean FALSE value.
if($contents !== false){
echo '<div id="containex" class="form-group">
<label for="exampleFormControlTextarea3">Pure Response</label>
<textarea class="form-control" id="exampleFormControlTextarea2" rows="7">'.$contents.'</textarea>
</div>';
echo "<br>";
include('decryption.php');
$obj =new Decryptor();
echo '<div id="containex" class="form-group">
<label for="exampleFormControlTextarea3">Decrypted Response</label>
<textarea class="form-control" id="exampleFormControlTextarea2" rows="2">'.$obj->decr($contents).'</textarea>
</div>';
// echo $obj->decr($contents);
}
}else{
echo "where is parameters";
}
?>
package com.egyptdc.utilities;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
public class DecryptionSample {
public static void main(String[] args) throws Exception {
//Add Security Provider. You can use any security provider, but we encourage the use of
bouncy castle
//since it's open-source, has no legal restrictions, and performs fast
Security.addProvider(new BouncyCastleProvider());
//Create the key factory that is going to be used to generate the public key
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
//The encrypted message sent by Bitaqaty Business
String message =
"df/qLoL5EpOD161jWX7HK3G+89yPTzqK6HW2BeOGv9QuSmUjWN7bhveUf0nVOYF+UT8sWR8PvT7vGjfi
Bo68Td83APFBLDGfkIUxYgupnDO7qTKh75
KSFi+rrqx15369ymiyqwuFYQ7Zfu4Q/G5AQPNpbuEdyx0wssrz1EPZzDHYWzBhZHjLpkqwKfB0ggSXSoVQCZ
PiP93thsRKR2g2yOcJMGlllXQSPaRKn69RS3
XKRb3JliVppqSPmyBxMOU4A0KP0aqUyIpSyUG3qUnE1tnet0HTTxJPtZazgdJ7NyYNhExuwIripBI4jAYuYeUo
vpY/GAeF0IPMGJT4hrD7QQ==";
//The modulus and the exponent handed to the merchant
BigInteger modulus = new
BigInteger("16710289668368412059178504818442594594200759261915121282976481033795814865
89570555674853940359883307591397271585968105044414785898456074693211978562592963669
08584222721237433773892005465187191316859570361575879755693787197554942072570008692
60074793075532795960464126867814527242695057668290161302626846629207438495016379827
54551787035704672967981126579877081355538159927854424153576719134719690854608910320
68872161050739679144337448168873468023769235514177864683849678399191739514709542818
66572655698033962263517464758144028279628992096495704278328617390576343568285064548
251633116565437468284305547090306791461634479");
BigInteger publicExponent = new BigInteger("65537");
//Create the public key from the modulus and exponent
RSAPublicKeySpec merchantKeySpec = new RSAPublicKeySpec(modulus,
publicExponent);
}}
Response codes are encrypted by a private key, and then encoded using Base64 encoding
Environment | URL |
---|---|
Staging | https://www.ocstaging.net/lmm/index.html |
Production | https://www.netader.com/lmm/index.html |
Response | Description |
---|---|
00 | VALID_REQUEST |
Response | Description |
---|---|
101 | MISSING_MERCHANTID (Plain Text, i.e. Not Encrypted) |
102 | MISSING_VOUCHERCODE |
103 | MISSING_REDEEM_REFNUMBER |
109 | MISSING_HASHKEY |
Response | Description |
---|---|
201 | INVALID_MERCHANTID (Plain Text, i.e. Not Encrypted) |
202 | INVALID_ REDEEM_REFNUMBER_LENGTH |
207 | INVALID_PURCHASING_CHANNEL |
2 | INVALID_SELLABLE_PRODUCT |
212 | INVALID_HASHKEY |
Response | Description |
---|---|
301 | VOUCHERCODE_NOTMATCH |
302 | VOUCHERCODE_REDEEMED |
303 | VOUCHERCODE_EXPIRED |
304 | : PRODUCT_DOESNOTBELONGTOMERCHANT |
306 | VOUCHERCODE_REVOKED |
307 | VOUCHERCODE_REVOKE_EXPIRED |
13 | INSUFFICIENT_BALANCE |
Response | Description |
---|---|
501 | INTERNAL_SERVER_ERROR (Plain Text, i.e. Not Encrypted) |
502 | Service Not Available |