Quick Start

Last updated: Mars 03th, 2019

System Overview


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

API Methods

Method Name:

Redeem voucher (Secure)


Method Scenario
  • The “Redeem Voucher” method allows the User to redeem a voucher. The merchant’s system sends the required parameters for redeeming the voucher to Bitaqaty Business who will check the voucher status, perform the redeem process and mark the voucher as redeemed or respond with an error message
  • For more security, The request includes hashcode (generated due to secret key shared between Bitaqaty Business and the merchant) to ensure that the relative merchant is the only entity that can redeem relative voucher
  • It is essential to apply “Redeem Voucher” method before giving the user the desired service in order to verify the voucher code entered, and mark it as redeemed in our system in order to be able to differentiate between vouchers redeemed before and voucher that haven’t been redeemed yet so as not to give a user the service twice for the same voucher

Request Flow
  • Merchant sends the “Redeem Voucher” request to Bitaqaty Business website with the required parameters
  • Bitaqaty Business sends back the redeeming results encrypted
  • If the response is “success”, the voucher has been redeemed successfully, the merchant is expected to give the product or service to the user
  • If the response is “test”, same as “success” but Bitaqaty Business user who purchased the voucher is as test user in Bitaqaty Business, and thus this voucher is not billable, the merchant is expected to handle this as a test not a real voucher
  • The response can also be “error” (Please refer to Response Codes reference for error details)

Technical Considerations


Request URLs :
Request Parameters :

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 parameters are required.

Response Parameters

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


  • Sucess :

    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
    Example of sucess Response
    ACK=success&TRANSACTIONTIME=1226047488422&TRXREFNUMBER=8888

    Note :
    It is expected to give the user a fake service upon redeeming a “test” voucher, because “test” vouchers are not billable


  • Error :

    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
    Example of Error Response
    EX: ACK=error&ERRORCODE= 102&TRANSACTIONTIME=1226047488422
Method Name:

Get Voucher Information


Method Scenario
  • The “Get Voucher Information” method enables a merchant to get the status of any given voucher. The Merchant sends the required parameters to Bitaqaty Business which responds to the merchant’s request by the current status of the voucher; whether it was redeemed or not and the date of redeeming, the date of purchase and the expiry date if any. This method only returns the voucher status without carrying out any action based on it.

Request Flow
  • Merchant sends the “Get Voucher Information” request to Bitaqaty Business website with the required parameters
  • Bitaqaty Business sends back the voucher status details encrypted
    • “success”: the voucher is redeemable (to redeem it, call the Redeem Voucherservice)
    • “test”: same as “success” but Bitaqaty Business user who purchased the voucher is a test userin Bitaqaty Business, and thus this voucher is not billable
    • “error” (Please refer to the Error codes Section for error details)

Technical Considerations


Request URLs :

Request Parameters:

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 parameters are required.

Response Parameters :

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


  • Sucess :

    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
    Example of Sucess Response
    ACK=success&CREATED=1227607686&EXPIRS=1228627964&REDEEMED=0&REDEEMEDTIME=0&TARAN SACTIONTIME=1226047488422 &TRXREFNUMBER=8888

    Note :
    It is expected to give the user a fake service upon redeeming a “test” voucher, because “test” vouchers are not billable


  • Error :

    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
    Example of Error Response
    ACK=error&ERRORCODE=301&TARANSACTIONTIME=1226047488422

Code Examples :




     

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);

}} 

Encryption :

Response codes are encrypted by a private key, and then encoded using Base64 encoding

  • For parsing the response:
    • Decode the response using base64 encoding
    • Decrypt the result using the provided public key (Provided upon integration)

DashBoard URL

Response Codes


Valid Requests

Response Description
00 VALID_REQUEST


Missing Parameters

Response Description
101 MISSING_MERCHANTID (Plain Text, i.e. Not Encrypted)
102 MISSING_VOUCHERCODE
103 MISSING_REDEEM_REFNUMBER
109 MISSING_HASHKEY


Invalid Parameters

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


Business Errors

Response Description
301 VOUCHERCODE_NOTMATCH
302 VOUCHERCODE_REDEEMED
303 VOUCHERCODE_EXPIRED
304 : PRODUCT_DOESNOTBELONGTOMERCHANT
306 VOUCHERCODE_REVOKED
307 VOUCHERCODE_REVOKE_EXPIRED
13 INSUFFICIENT_BALANCE


Server Errors

Response Description
501 INTERNAL_SERVER_ERROR (Plain Text, i.e. Not Encrypted)
502 Service Not Available