secretKey = $secret; // HMAC key } public function setCryptKey($secret) { $this->cryptKey = $secret; // blowfish crypt key } /** hack to retrieve response field **/ public function setReponse($encrypt='encrypt') { $this->parameters['Response'] = $encrypt; } /** HMAC compute and store in MAC field**/ public function shaCompose(array $parameters) { // compose SHA string $shaString = ''; foreach($parameters as $key) { if(array_key_exists($key, $this->parameters) && !empty($this->parameters[$key])) { $value = $this->parameters[$key]; $shaString .= $value; } $shaString .= (array_search($key, $parameters) != (count($parameters)-1)) ? '*' : ''; } $this->parameters['MAC'] = hash_hmac('sha256', $shaString, $this->secretKey); return $this->parameters['MAC']; } /** @return string */ public function getShaSign() { $this->validate(); return $this->shaCompose($this->QHMACFields); } public function BfishCompose(array $parameters) { // compose Blowfish hex string $blowfishString = ''; foreach($parameters as $key) { if(array_key_exists($key, $this->parameters) && !empty($this->parameters[$key])) { $value = $this->parameters[$key]; $blowfishString .= $key.'='.$value.'&'; } } $blowfishString = rtrim($blowfishString,'&'); $this->parameters['Debug'] = $blowfishString; $this->parameters['Len'] = strlen($blowfishString); $this->parameters[self::DATA_FIELD] = bin2hex($this->encrypt($blowfishString,$this->cryptKey)); return $this->parameters[self::DATA_FIELD]; } /** @return string */ public function getBfishCrypt() { $this->validate(); return $this->BFishCompose($this->BfishFields); } private function encrypt($data, $key) { $l = strlen($key); if ($l < 16) $key = str_repeat($key, ceil(16/$l)); if ($m = strlen($data)%8) $data .= str_repeat("\x00", 8 - $m); if (function_exists('mcrypt_encrypt')) $val = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB); else $val = openssl_encrypt($data, 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING); return $val; } private function decrypt($data, $key) { $l = strlen($key); if ($l < 16) $key = str_repeat($key, ceil(16/$l)); if (function_exists('mcrypt_encrypt')) $val = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB); else $val = openssl_decrypt($data, 'BF-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING); return rtrim($val, "\0"); } /** @return string */ public function getUrl() { return $this->pspURL; } public function setUrl($pspUrl) { $this->validateUri($pspUrl); $this->pspURL = $pspUrl; } public function setURLSuccess($url) { $this->validateUri($url); $this->parameters['URLSuccess'] = $url; } public function setMsgVer($m) { $this->parameters['MsgVer'] = $m; } public function setUserData($data) { $this->parameters['UserData'] = $data; } public function setURLFailure($url) { $this->validateUri($url); $this->parameters['URLFailure'] = $url; } public function setURLNotify($url) { $this->validateUri($url); $this->parameters['URLNotify'] = $url; } public function setTransID($transactionReference) { if(preg_match('/[^a-zA-Z0-9_-]/', $transactionReference)) { throw new \InvalidArgumentException("TransactionReference cannot contain special characters"); } $this->parameters['TransID'] = $transactionReference; } /** * Set amount in cents, eg EUR 12.34 is written as 1234 */ public function setAmount($amount) { if(!is_int($amount)) { throw new \InvalidArgumentException("Integer expected. Amount is always in cents"); } if($amount <= 0) { throw new \InvalidArgumentException("Amount must be a positive number"); } $this->parameters['Amount'] = $amount; } public function setCaptureDay($number) { if (strlen($number) > 2) { throw new \InvalidArgumentException("captureDay is too long"); } $this->parameters['captureDay'] = $number; } // Methodes liees a la lutte contre la fraude public function setFraudDataBypass3DS($value) { if(strlen($value) > 128) { throw new \InvalidArgumentException("fraudData.bypass3DS is too long"); } $this->parameters['fraudData.bypass3DS'] = $value; } // Methodes liees au paiement one-click public function setMerchantWalletId($wallet) { if(strlen($wallet) > 21) { throw new \InvalidArgumentException("merchantWalletId is too long"); } $this->parameters['merchantWalletId'] = $wallet; } // instalmentData.number instalmentData.datesList instalmentData.transactionReferencesList instalmentData.amountsList paymentPattern // Methodes liees au paiement en n-fois public function setInstalmentDataNumber($number) { if (strlen($number) > 2) { throw new \InvalidArgumentException("instalmentData.number is too long"); } if ( ($number < 2) || ($number > 50) ) { throw new \InvalidArgumentException("instalmentData.number invalid value : value must be set between 2 and 50"); } $this->parameters['instalmentData.number'] = $number; } public function setInstalmentDatesList($datesList) { $this->parameters['instalmentData.datesList'] = $datesList; } public function setInstalmentDataTransactionReferencesList($transactionReferencesList) { $this->parameters['instalmentData.transactionReferencesList'] = $transactionReferencesList; } public function setInstalmentDataAmountsList($amountsList) { $this->parameters['instalmentData.amountsList'] = $amountsList; } public function setPaymentPattern($paymentPattern) { $this->parameters['paymentPattern'] = $paymentPattern; } public function __call($method, $args) { if(substr($method, 0, 3) == 'set') { // $field = lcfirst(substr($method, 3)); $field = substr($method, 3); if(in_array($field, $this->pspFields)) { $this->parameters[$field] = $args[0]; return; } } if(substr($method, 0, 3) == 'get') { // $field = lcfirst(substr($method, 3)); $field = substr($method, 3); if(array_key_exists($field, $this->parameters)) { return $this->parameters[$field]; } } throw new \BadMethodCallException("Unknown method $method"); } public function toArray() { return $this->parameters; } public function toParameterString() { $parameterString = ""; foreach($this->parameters as $key => $value) { $parameterString .= $key . '=' . $value; $parameterString .= (array_search($key, array_keys($this->parameters)) != (count($this->parameters)-1)) ? '|' : ''; } return $parameterString; } /** @return PaymentRequest */ public static function createFromArray(ShaComposer $shaComposer, array $parameters) { $instance = new static($shaComposer); foreach($parameters as $key => $value) { $instance->{"set$key"}($value); } return $instance; } public function validate() { foreach($this->requiredFields as $field) { if(empty($this->parameters[$field])) { throw new \RuntimeException($field . " can not be empty"); } } } protected function validateUri($uri) { if(!filter_var($uri, FILTER_VALIDATE_URL)) { throw new \InvalidArgumentException("Uri is not valid"); } if(strlen($uri) > 200) { throw new \InvalidArgumentException("Uri is too long"); } } // Traitement des reponses d'Axepta // ----------------------------------- /** @var string */ const SHASIGN_FIELD = "MAC"; /** @var string */ const DATA_FIELD = "Data"; public function setResponse(array $httpRequest) { // use lowercase internally // $httpRequest = array_change_key_case($httpRequest, CASE_UPPER); // set sha sign // $this->shaSign = $this->extractShaSign($httpRequest); // filter request for Sips parameters $this->parameters = $this->filterRequestParameters($httpRequest); } /** * @var string */ private $shaSign; private $dataString; /** * Filter http request parameters * @param array $requestParameters */ private function filterRequestParameters(array $httpRequest) { //filter request for Sips parameters $parameters = $this->parameters; if(!array_key_exists(self::DATA_FIELD, $httpRequest) || $httpRequest[self::DATA_FIELD] == '') { // throw new InvalidArgumentException('Data parameter not present in parameters.'); $parameters['Debug'] = implode('&',$httpRequest); foreach($httpRequest as $key=>$value) { $key = ($key=='mid')? 'MerchantID':$key; $parameters[$key]=$value; } } else { $parameters[self::DATA_FIELD] = $httpRequest[self::DATA_FIELD]; $this->dataString = $this->decrypt(hex2bin($parameters[self::DATA_FIELD]),$this->cryptKey); $parameters['Debug'] = $this->dataString; $dataParams = explode('&', $this->dataString); foreach($dataParams as $dataParamString) { $dataKeyValue = explode('=',$dataParamString,2); $key = ($dataKeyValue[0]=='mid')?'MerchantID':$dataKeyValue[0]; $parameters[$key] = $dataKeyValue[1]; } } return $parameters; } public function getSeal() { return $this->shaSign; } private function extractShaSign(array $parameters) { if(!array_key_exists(self::SHASIGN_FIELD, $parameters) || $parameters[self::SHASIGN_FIELD] == '') { throw new \InvalidArgumentException('SHASIGN parameter not present in parameters.'); } return $parameters[self::SHASIGN_FIELD]; } /** * Checks if the response is valid * @param ShaComposer $shaComposer * @return bool */ public function isValid() { // return $this->shaCompose($this->RHMACFields) == $this->shaSign; return $this->shaCompose($this->RHMACFields) == $this->parameters['MAC']; } /** * Retrieves a response parameter * @param string $param * @throws \InvalidArgumentException */ public function getParam($key) { if(method_exists($this, 'get'.$key)) { return $this->{'get'.$key}(); } // always use uppercase // $key = strtoupper($key); // $parameters = array_change_key_case($this->parameters,CASE_UPPER); $parameters = $this->parameters; if(!array_key_exists($key, $parameters)) { throw new \InvalidArgumentException('Parameter ' . $key . ' does not exist.'); } return $parameters[$key]; } /** * @return int Amount in cents */ public function getAmount() { $value = trim($this->parameters['Amount']); return (int) ($value); } public function isSuccessful() { return in_array($this->getParam('Status'), array("OK", "AUTHORIZED")); } public function getDataString() { return $this->dataString; } } ?>