Skip to content

Commit

Permalink
Upgrade PHP SDK v12.40 (#53)
Browse files Browse the repository at this point in the history
* Committing 12.40 xsd changes

* Committing 12.40 unit test and functional test

* Pushing Encrypted changes

* Pushing EncryptedPayload changes changes

* committing code changes

* Pushing EncryptedPayload changes changes

* Revert "Pushing EncryptedPayload changes changes"

This reverts commit 36eb50f.

* Pushing EncryptedPayload with error changes

* Pushing EncryptionKeyRequest changes.

* Pushing setup.php changes.

* committing test changes

* committing code changes

* committing batch test changes

* committing code changes

* Pushing nueter.xml changes.

* Commiting code changes

* promoting changelog

* promoting code changes

---------

Co-authored-by: patilmadhuri1 <[email protected]>
Co-authored-by: e5715887 <[email protected]>
  • Loading branch information
3 people authored Oct 28, 2024
1 parent 39e90b2 commit fd78dec
Show file tree
Hide file tree
Showing 28 changed files with 1,389 additions and 144 deletions.
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
= CnpOnline CHANGELOG

==Version 12.40.0 (v12.40.0)(October 25, 2024)
* Change: [cnpAPI v12.40] In authorization,sale and captureGivenAuth request new elements added-> 'typeOfDigitalCurrency' and 'conversionAffiliateId'.
* Change: [cnpAPI v12.40] In existing simple type transactionAmountType range added between minInclusive value -999999999999 and maxInclusive value 999999999999
* Change: [cnpAPI v12.40] In existing type authenticationProtocolVersionType new values '3','4','5','6','7','8' and '9' are added

==Version 12.39.0 (v12.39.0) (October 25, 2024)
* Change: [cnpAPI v12.39] In 'subMerchantCredit' 'payFacCredit','reserveCredit', 'payoutOrgCreditRequest' base changed from 'transactionTypeWithReportGroup' to 'transactionTypeWithReportGroupAndRtp'.
* Change: [cnpAPI v12.39] To support new changed extension base for above txns ,New Complextype 'transactionTypeWithReportGroupAndRtp' added.
* Change: [cnpAPI v12.39] New element 'fundingTransactionReferenceNumber' in authorization and sale response.

==Version 12.38.0 (v12.38.0) (October 25, 2024)
* Change: [cnpAPI v12.38] In existing Enum 'ActionTypeEnum' new value 'FIVD' added
* Change: [cnpAPI v12.38] In existing complex element 'baseRequest' two more choices are added 'encryptionKeyRequest' and 'encryptedPayload'
* Change: [cnpAPI v12.38] In existing complex element 'cnpOnlineResponse' one more choice is added 'encryptionKeyResponse'
* Change: [cnpAPI v12.38] New element 'encryptionKeyRequest' is added of type 'encryptionKeyRequestEnum'
* Change: [cnpAPI v12.38] To support 'encryptionKeyRequest' new Enum 'encryptionKeyRequestEnum' added with values 'CURRENT' and 'PREVIOUS'
* Change: [cnpAPI v12.38] New complex element 'encryptionKeyResponse' is added with simple elements ->'encryptionKeySequence' of type 'encryptionKeySequenceType' and 'encryptionKey' of type 'encryptionKeyType'
* Change: [cnpAPI v12.38] To support 'encryptionKeySequence' new simple type 'encryptionKeySequenceType' added with total Digits 19
* Change: [cnpAPI v12.38] To support 'encryptionKey' new simple type 'encryptionKeyType' of type string with maxLength 15000
* Change: [cnpAPI v12.38] New complex element 'encryptedPayload' is added with simple elements ->'encryptionKeySequence' of type 'encryptionKeySequenceType' and 'payload' of type 'payloadType'
* Change: [cnpAPI v12.38] To support 'payload' new simple type 'payloadType' of type string with maxLength 15000
* Change: [cnpAPI v12.38] In capture Request new complex element 'partialCapture' is added with elements-> 'partialCaptureSequenceNumber' of type 'integer' and 'partialCaptureTotalCount' of type 'partialCaptureCount'

==Version 12.37.1 (v12.37.1)(July 29, 2024)
Fix: [cnpAPI v12.37.1] Fix for Ordering issue for Fast Access Funding

Expand Down
2 changes: 1 addition & 1 deletion cnp/sdk/Checker.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Checker
public static function validateXML($request){
$xml = new DOMDocument();
$xml->loadXML($request);
$filepath = __DIR__ . "/schema/SchemaCombined_v12.37.xsd";
$filepath = __DIR__ . "/schema/SchemaCombined_v12.40.xsd";
$result = $xml->schemaValidate( $filepath);

if(!$result)
Expand Down
6 changes: 3 additions & 3 deletions cnp/sdk/CnpOnline.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
namespace cnp\sdk;
define('CURRENT_XML_VERSION', '12.37');
define('CURRENT_SDK_VERSION', 'PHP;12.37.1');
define('CURRENT_XML_VERSION', '12.40');
define('CURRENT_SDK_VERSION', 'PHP;12.40.0');
define('MAX_TXNS_PER_BATCH', 100000);
define('MAX_TXNS_PER_REQUEST', 500000);
define('CNP_CONFIG_LIST', 'user,password,merchantId,timeout,proxy,reportGroup,version,url,cnp_requests_path,batch_requests_path,sftp_username,sftp_password,batch_url,tcp_port,tcp_ssl,tcp_timeout,print_xml,vantivPublicKeyID,gpgPassphrase,useEncryption,deleteBatchFiles,multiSite,multiSiteErrorThreshold,maxHoursWithoutSwitch,printMultiSiteDebug,multiSiteUrl1,multiSiteUrl2,sftp_timeout');
define('CNP_CONFIG_LIST', 'user,password,merchantId,timeout,proxy,reportGroup,version,url,cnp_requests_path,batch_requests_path,sftp_username,sftp_password,batch_url,tcp_port,tcp_ssl,tcp_timeout,print_xml,neuter_xml,vantivPublicKeyID,gpgPassphrase,useEncryption,deleteBatchFiles,multiSite,multiSiteErrorThreshold,maxHoursWithoutSwitch,printMultiSiteDebug,multiSiteUrl1,multiSiteUrl2,sftp_timeout,oltpEncryptionKeySequence,oltpEncryptionPayload,oltpEncryptionKeyPath');

123 changes: 118 additions & 5 deletions cnp/sdk/CnpOnlineRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
*/

namespace cnp\sdk;
use DOMDocument;
use XSLTProcessor;
use SimpleXMLElement;
use SplFileInfo;
use Exception;
require_once realpath(dirname(__FILE__)) . '/CnpOnline.php';

class CnpOnlineRequest
Expand Down Expand Up @@ -148,7 +153,10 @@ public function authorizationRequest($hash_in)
'passengerTransportData' => XmlFields::passengerTransportData(XmlFields::returnArrayValue($hash_in, 'passengerTransportData')),
'authIndicator' => XmlFields::returnArrayValue($hash_in, 'authIndicator'),
'accountFundingTransactionData' => XmlFields::accountFundingTransactionData(XmlFields::returnArrayValue($hash_in, 'accountFundingTransactionData')),
'fraudCheckAction' => XmlFields::returnArrayValue($hash_in, 'fraudCheckAction')
'fraudCheckAction' => XmlFields::returnArrayValue($hash_in, 'fraudCheckAction'),
'typeOfDigitalCurrency' => XmlFields::returnArrayValue($hash_in, 'typeOfDigitalCurrency'),
'conversionAffiliateId' => XmlFields::returnArrayValue($hash_in, 'conversionAffiliateId'),

);
}
$choice_hash = array(XmlFields::returnArrayValue($hash_out, 'card'), XmlFields::returnArrayValue($hash_out, 'paypal'), XmlFields::returnArrayValue($hash_out, 'token'), XmlFields::returnArrayValue($hash_out, 'paypage'), XmlFields::returnArrayValue($hash_out, 'applepay'), XmlFields::returnArrayValue($hash_out, 'mpos'));
Expand Down Expand Up @@ -230,7 +238,10 @@ public function saleRequest($hash_in)
'passengerTransportData' => XmlFields::passengerTransportData(XmlFields::returnArrayValue($hash_in, 'passengerTransportData')),
'foreignRetailerIndicator' => XmlFields::returnArrayValue($hash_in, 'foreignRetailerIndicator'),
'accountFundingTransactionData' => XmlFields::accountFundingTransactionData(XmlFields::returnArrayValue($hash_in, 'accountFundingTransactionData')),
'fraudCheckAction' => XmlFields::returnArrayValue($hash_in, 'fraudCheckAction')
'fraudCheckAction' => XmlFields::returnArrayValue($hash_in, 'fraudCheckAction'),
'typeOfDigitalCurrency' => XmlFields::returnArrayValue($hash_in, 'typeOfDigitalCurrency'),
'conversionAffiliateId' => XmlFields::returnArrayValue($hash_in, 'conversionAffiliateId'),

);

$choice_hash = array($hash_out['card'], $hash_out['paypal'], $hash_out['token'], $hash_out['paypage'], $hash_out['applepay'], $hash_out['mpos']);
Expand Down Expand Up @@ -468,7 +479,9 @@ public function captureRequest($hash_in)
'lodgingInfo' => XmlFields::lodgingInfo(XmlFields::returnArrayValue($hash_in, 'lodgingInfo')),
'pin' => XmlFields::returnArrayValue($hash_in, 'pin'),
'passengerTransportData' => XmlFields::passengerTransportData(XmlFields::returnArrayValue($hash_in, 'passengerTransportData')),
'foreignRetailerIndicator' => XmlFields::returnArrayValue($hash_in, 'foreignRetailerIndicator')
'foreignRetailerIndicator' => XmlFields::returnArrayValue($hash_in, 'foreignRetailerIndicator'),
'partialCapture' => XmlFields::partialCapture(XmlFields::returnArrayValue($hash_in, 'partialCapture')),

);
$captureResponse = $this->processRequest($hash_out, $hash_in, 'capture');

Expand Down Expand Up @@ -516,7 +529,10 @@ public function captureGivenAuthRequest($hash_in)
'crypto' => XmlFields::returnArrayValue($hash_in, 'crypto'),
'passengerTransportData' => XmlFields::passengerTransportData(XmlFields::returnArrayValue($hash_in, 'passengerTransportData')),
'foreignRetailerIndicator' => XmlFields::returnArrayValue($hash_in, 'foreignRetailerIndicator'),
'accountFundingTransactionData' => XmlFields::accountFundingTransactionData(XmlFields::returnArrayValue($hash_in, 'accountFundingTransactionData'))
'accountFundingTransactionData' => XmlFields::accountFundingTransactionData(XmlFields::returnArrayValue($hash_in, 'accountFundingTransactionData')),
'typeOfDigitalCurrency' => XmlFields::returnArrayValue($hash_in, 'typeOfDigitalCurrency'),
'conversionAffiliateId' => XmlFields::returnArrayValue($hash_in, 'conversionAffiliateId'),

);

$choice_hash = array($hash_out['card'], $hash_out['token'], $hash_out['paypage'], $hash_out['mpos']);
Expand Down Expand Up @@ -1473,6 +1489,35 @@ public function finicityAccountRequest($hash_in)
return $finicityAccountResponse;
}

/**
* @param $hash_in
* @return \DOMDocument|\SimpleXMLElement
* @throws exceptions\cnpSDKException
*/
public function encryptionKeyRequest($hash_in)
{
$hash_out = array(XmlFields::returnArrayValue($hash_in,'encryptionKeyRequest'));
$encryptionKeyResponse = $this->processRequest($hash_out, $hash_in, 'encryptionKeyRequest');

return $encryptionKeyResponse;
}

/**
* @param $hash_in
* @return \DOMDocument|\SimpleXMLElement
* @throws exceptions\cnpSDKException
*/
public function encryptedPayload($hash_in)
{
$hash_out = array(
'encryptionKeySequence' => (XmlFields::returnArrayValue($hash_in, 'encryptionKeySequence')),
'payload' => (XmlFields::returnArrayValue($hash_in, 'payload')),
);
$encryptionKeyResponse = $this->processRequest($hash_out, $hash_in, 'encryptedPayload');

return $encryptionKeyResponse;
}

/**
* @param $hash_in
* @return array
Expand Down Expand Up @@ -1528,20 +1573,88 @@ private static function getOptionalAttributes($hash_in, $hash_out)
private function processRequest($hash_out, $hash_in, $type, $choice1 = null, $choice2 = null)
{
$hash_config = CnpOnlineRequest::overrideConfig($hash_in);
$hash_config= Obj2xml::getConfig($hash_config);
$hash = CnpOnlineRequest::getOptionalAttributes($hash_in, $hash_out);
$request = Obj2xml::toXml($hash, $hash_config, $type);


if (Checker::validateXML($request)) {
$request = str_replace("submerchantDebitCtx", "submerchantDebit", $request);
$request = str_replace("submerchantCreditCtx", "submerchantCredit", $request);
$request = str_replace("vendorCreditCtx", "vendorCredit", $request);
$request = str_replace("vendorDebitCtx", "vendorDebit", $request);

// If oltpEncryptionPayload enabled then send reuqest for encryption.
if (isset($hash_config['oltpEncryptionPayload']) and (int)$hash_config['oltpEncryptionPayload'] == 1){
$request = CnpOnlineRequest::getEncryptedPayload($request,$hash_config);
}
$cnpOnlineResponse = $this->newXML->request($request, $hash_config, $this->useSimpleXml);
}

return $cnpOnlineResponse;
}

public function getEncryptedPayload($request, $hash_config)
{
try {
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->loadXML($request);
$root = $doc->documentElement;

// Find the second child element
$secondChild = $root->childNodes->item(1);

if ($secondChild !== null) {

// Skip the encrypt payload part for encryptionKeyRequest
if ($secondChild->nodeName == 'encryptionKeyRequest') {
return $request;

} else {
$path = null;
if(isset($hash_config['oltpEncryptionKeyPath'])){
$path = $hash_config['oltpEncryptionKeyPath'];
}
if ($path == null) {
throw new Exception('Problem in reading the Encryption Key path. Provide the Encryption key path.');
} else {
$path = new SplFileInfo($path);
if (!$path->isFile()) {
throw new Exception("The provided path is not a valid file path or the file does not exist.");
}
}

$output = $doc->saveXML($secondChild);

// removing the child element which needs to be encrypted.
$root->removeChild($secondChild);

//Send payload for encryption
$payload = PgpHelper::encryptPayload($output, $path);

$encryptedPayloadElement = $doc->createElement('encryptedPayload');

// Create and append the encryptionKeySequence element
$encryptionKeySequenceElement = $doc->createElement('encryptionKeySequence');
if(isset($hash_config['oltpEncryptionKeySequence']) and $hash_config['oltpEncryptionKeySequence'] != null ) {
$encryptionKeySequenceElement->nodeValue = (int)$hash_config['oltpEncryptionKeySequence'];
$encryptedPayloadElement->appendChild($encryptionKeySequenceElement);
} else{
throw new Exception('Problem in reading the Encryption Key Sequence ...Provide the Encryption key Sequence');
}
// Create and append the payload element
$payloadElement = $doc->createElement('payload');
$payloadElement->nodeValue = $payload;
$encryptedPayloadElement->appendChild($payloadElement);

// adding new element after encryption
$root->appendChild($encryptedPayloadElement);
$xmlRequest = $doc->saveHTML();
return $xmlRequest;
}
}
} catch (Exception $e) {
throw new Exception('Error processing XML request. Please reach out to SDK Support team.', 0, $e);
}
}
}

35 changes: 32 additions & 3 deletions cnp/sdk/Communication.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ public static function httpRequest($req,$hash_config=NULL)
$config = Obj2xml::getConfig($hash_config);


if ((int) $config['print_xml']) {
echo $req;
if ((int)$config['print_xml']) {
Communication::print_xml($req, $config);
#echo $req;
}
$ch = curl_init();

Expand All @@ -55,7 +56,6 @@ public static function httpRequest($req,$hash_config=NULL)
$output = curl_exec($ch);



$responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (! $output) {
if ($responseCode == 'CURLE_OPERATION_TIMEDOUT'){
Expand All @@ -75,4 +75,33 @@ public static function httpRequest($req,$hash_config=NULL)
}

}

public static function neuter_xml($xml)
{
$neuter_str = "NEUTERED";
if ($xml === null) {
return $xml;
}
$xml = preg_replace("/<accNum>.*?<\/accNum>/", "<accNum>$neuter_str</accNum>", $xml);
$xml = preg_replace("/<user>.*?<\/user>/", "<user>$neuter_str</user>", $xml);
$xml = preg_replace("/<password>.*?<\/password>/", "<password>$neuter_str</password>", $xml);
$xml = preg_replace("/<track>.*?<\/track>/", "<track>$neuter_str</track>", $xml);
$xml = preg_replace("/<number>.*?<\/number>/", "<number>$neuter_str</number>", $xml);
$xml = preg_replace("/<cardValidationNum>.*?<\/cardValidationNum>/", "<cardValidationNum>$neuter_str</cardValidationNum>", $xml);

return $xml;
}

//If neuter_xml flag enabled then neuter the sensitive data from the request xml
public static function print_xml($xml_request, $config)
{
$xml_to_log = $xml_request;
if (isset($config['neuter_xml']) and (int)$config['neuter_xml'] == 1) {
$xml_to_log = Communication::neuter_xml($xml_to_log);
}
echo "Request XML: $xml_to_log\n";

return $xml_to_log;
}

}
13 changes: 10 additions & 3 deletions cnp/sdk/Obj2xml.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ public static function toXml($data, $hash_config, $type, $rootNodeName = 'cnpOnl
$authentication->addChild('user',$config["user"]);
$authentication->addChild('password',$config["password"]);


// If request type is encryptionKeyRequest then return xml with encryptionKeyRequest value.
if ($type == "encryptionKeyRequest"){
$xml->addChild($type,$data[0]);
return $xml->asXML();
}

$transacType = $xml->addChild($type);

Expand Down Expand Up @@ -335,10 +339,13 @@ public static function getConfig($data, $type=NULL)
} elseif ($name == 'sftp_timeout') {
$config['sftp_timeout'] = isset($config_array['sftp_timeout'])? $config_array['sftp_timeout']:'720';
} else {
if ((!isset($config_array[$name])) and ($name != 'proxy')) {
if ((!isset($config_array[$name])) and ($name != 'proxy') and ($name != 'oltpEncryptionPayload') and ($name != 'oltpEncryptionKeySequence') and ($name != 'oltpEncryptionKeyPath') and ($name != 'neuter_xml')) {
throw new \InvalidArgumentException("Missing Field /$name/");
}
$config[$name] = $config_array[$name];
// If encryptionPayload properties are not available in config file then add other properties.
if(isset($config_array[$name])){
$config[$name] = $config_array[$name];
}
}
}
}
Expand Down
37 changes: 37 additions & 0 deletions cnp/sdk/PgpHelper.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

namespace cnp\sdk;
use Exception;

class PgpHelper
{
Expand Down Expand Up @@ -56,4 +57,40 @@ public static function importKey($keyFile){
$split = explode(" ", $output[0]);
return rtrim($split[2], ":");
}

public static function encryptPayload($encryptInput, $publicKey)
{
// Open a process to gpg with pipes
$descriptorspec = [
0 => ["pipe", "r"], // stdin is a pipe that the child will read from
1 => ["pipe", "w"], // stdout is a pipe that the child will write to
2 => ["pipe", "w"] // stderr is a pipe that the child will write to
];

$process = proc_open("gpg --batch --yes --quiet --no-secmem-warning --armor --trust-model always --recipient-file $publicKey --encrypt", $descriptorspec, $pipes);

if (is_resource($process)) {
// Write the input string to the stdin of the process
fwrite($pipes[0], $encryptInput);
fclose($pipes[0]);

// Read the encrypted content from the stdout of the process
$encryptedString = stream_get_contents($pipes[1]);
fclose($pipes[1]);

// Read any errors from the stderr of the process
$errors = stream_get_contents($pipes[2]);
fclose($pipes[2]);

// Close the process
$return_value = proc_close($process);

if ($return_value != 0) {
throw new \RuntimeException("Encrypting the payload has failed" . $errors);
}
return $encryptedString;
} else {
throw new \RuntimeException("Encrypting the payload has failed. Please check the Encryption key or key path, is correct!\n");
}
}
}
Loading

0 comments on commit fd78dec

Please sign in to comment.