From 6d178bae5564f08f2b818b46bfeb529106825bb0 Mon Sep 17 00:00:00 2001 From: Dinh Nho Tuong Date: Fri, 13 Sep 2019 14:53:19 +0700 Subject: [PATCH] Add classes --- Classes/Constants.php | 121 +++ Classes/SagepayAbstractApi.php | 360 +++++++ Classes/SagepayApiException.php | 18 + Classes/SagepayApiManager.php | 40 + Classes/SagepayBasket.php | 710 +++++++++++++ Classes/SagepayCardDetails.php | 155 +++ Classes/SagepayCommon.php | 396 +++++++ Classes/SagepayCustomer.php | 288 +++++ Classes/SagepayCustomerDetails.php | 254 +++++ Classes/SagepayDirectApi.php | 150 +++ Classes/SagepayDiscount.php | 58 ++ Classes/SagepayFormApi.php | 81 ++ Classes/SagepayItem.php | 647 ++++++++++++ Classes/SagepayServerApi.php | 118 +++ Classes/SagepaySettings.php | 1566 ++++++++++++++++++++++++++++ Classes/SagepaySurcharge.php | 130 +++ Classes/SagepayToken.php | 180 ++++ Classes/SagepayUtil.php | 350 +++++++ Classes/SagepayValid.php | 225 ++++ Classes/SagepayValidator.php | 94 ++ README.md | 3 +- composer.json | 17 + registration.php | 8 + 23 files changed, 5967 insertions(+), 2 deletions(-) create mode 100644 Classes/Constants.php create mode 100644 Classes/SagepayAbstractApi.php create mode 100644 Classes/SagepayApiException.php create mode 100644 Classes/SagepayApiManager.php create mode 100644 Classes/SagepayBasket.php create mode 100644 Classes/SagepayCardDetails.php create mode 100644 Classes/SagepayCommon.php create mode 100644 Classes/SagepayCustomer.php create mode 100644 Classes/SagepayCustomerDetails.php create mode 100644 Classes/SagepayDirectApi.php create mode 100644 Classes/SagepayDiscount.php create mode 100644 Classes/SagepayFormApi.php create mode 100644 Classes/SagepayItem.php create mode 100644 Classes/SagepayServerApi.php create mode 100644 Classes/SagepaySettings.php create mode 100644 Classes/SagepaySurcharge.php create mode 100644 Classes/SagepayToken.php create mode 100644 Classes/SagepayUtil.php create mode 100644 Classes/SagepayValid.php create mode 100644 Classes/SagepayValidator.php create mode 100644 composer.json create mode 100644 registration.php diff --git a/Classes/Constants.php b/Classes/Constants.php new file mode 100644 index 0000000..4c53e2e --- /dev/null +++ b/Classes/Constants.php @@ -0,0 +1,121 @@ +config = $config; + $this->_createBasket(); + } + + /** + * Initialize Basket for current instance + */ + private function _createBasket() + { + $this->basket = new SagepayBasket(); + $this->basket->setAgentId($this->config->getVendorName()); + } + + /** + * Get config + * + * @return SagepaySettings + */ + public function getConfig() + { + return $this->config; + } + + /** + * Get integrationMethod + * + * @return string + */ + final public function getIntegrationMethod() + { + return $this->integrationMethod; + } + + /** + * Get basket + * + * @return SagepayBasket + */ + final public function getBasket() + { + return $this->basket; + } + + /** + * Set basket + * + * @param SagepayBasket $basket + */ + final public function setBasket(SagepayBasket $basket) + { + $this->basket = $basket; + } + + /** + * Set txType + * + * @param string $txType + */ + final public function setTxType($txType) + { + $this->txType = $txType; + } + + /** + * Get txType + * + * @return string + */ + final public function getTxType() + { + return $this->config->getTxType(); + } + + /** + * Get addressList + * + * @return SagepayCustomerDetails[] + */ + final public function getAddressList() + { + return $this->addressList; + } + + /** + * Set addressList + * + * @param SagepayCustomerDetails[] $addressList + */ + final public function setAddressList($addressList) + { + $this->addressList = $addressList; + } + + /** + * Add a set of customer details to addressList + * + * @param SagepayCustomerDetails $address + */ + final public function addAddress(SagepayCustomerDetails $address) + { + $this->addressList[] = $address; + } + + /** + * Get paneValues + * + * @return array + */ + final public function getPaneValues() + { + return $this->paneValues; + } + + /** + * Set paneValues + * + * @param array $paneValues + */ + final public function setPaneValues($paneValues) + { + $this->paneValues = $paneValues; + } + + /** + * Get Customer information + * + * @return SagepayCustomer + */ + public function getCustomer() + { + return $this->customer; + } + + /** + * Set Customer information + * + * @param SagepayCustomer $customer + */ + public function setCustomer(SagepayCustomer $customer) + { + $this->customer = $customer; + } + + /** + * Set data + * + * @param array $data + */ + final public function setData(array $data) + { + $this->data = $data; + } + + /** + * Get data + * + * @return array + */ + final public function getData() + { + return $this->data; + } + + /** + * Add a field to data + * + * @param string $field + * @param mixed $value + */ + final public function setDataField($field, $value = null) + { + $this->data[$field] = $value; + } + + /** + * Update data values + * + * @param array $data + */ + final public function updateData(array $data) + { + $this->data = array_merge($this->data, $data); + } + + /** + * Sort data in required order + */ + protected function sortData() + { + $unsorted = $this->data; + $data = array(); + foreach ($this->mandatory as $key) + { + $data[$key] = $unsorted[$key]; + unset($unsorted[$key]); + } + $data += $unsorted; + $this->data = $data; + } + + /** + * Check required fields that will be sent to gateway + * + * @throws SagepayApiException + */ + protected function checkMandatoryFields() + { + $emptyFields = array(); + + foreach ($this->mandatory as $value) + { + if (is_null($this->data[$value])) + { + $emptyFields[] = $value; + } + } + + if (count($emptyFields)) + { + $fields = implode(', ', $emptyFields); + $beVerb = count($emptyFields) == 1 ? 'is' : 'are'; + throw new \SagepayLib\classes\SagepayApiException($fields . ' ' . $beVerb . " empty"); + } + $this->sortData(); + } + + /** + * Populate data that is read from configurations + */ + protected function addConfiguredValues() + { + $data = array( + 'Vendor' => $this->config->getVendorName(), + 'VPSProtocol' => $this->config->getProtocolVersion(), + 'Currency' => $this->config->getCurrency(), + 'TxType' => $this->config->getTxType(), + 'SendEMail' => $this->config->getSendEmail(), + 'VendorEMail' => $this->config->getVendorEmail(), + 'Apply3DSecure' => $this->config->getApply3dSecure(), + 'ApplyAVSCV2' => $this->config->getApplyAvsCv2(), + ); + + $partnerId = $this->config->getPartnerId(); + if (!empty($partnerId)) + { + $data['ReferrerID'] = $partnerId; + } + if ($data['SendEMail'] == 1) + { + $data['eMailMessage'] = $this->config->getEmailMessage(); + } + $allowGiftAid = $this->config->getAllowGiftAid(); + if (!isset($this->data['AllowGiftAid'])) + { + $data['AllowGiftAid'] = $allowGiftAid; + } + + $this->updateData($data); + } + + /** + * Generate values for payment + * + * @return string[] + */ + abstract public function createRequest(); + + /** + * Generate UrlEncoded values + * + * @return string urlencoded data of request + */ + abstract public function getQueryData(); +} diff --git a/Classes/SagepayApiException.php b/Classes/SagepayApiException.php new file mode 100644 index 0000000..efd063c --- /dev/null +++ b/Classes/SagepayApiException.php @@ -0,0 +1,18 @@ +_id = (string) time(); + } + + /** + * Get basket ID + * + * @return string + */ + public function getId() + { + return $this->_id; + } + + /** + * Set basket ID + * + * @param string $id + */ + public function setId($id) + { + if (SagepayValid::digit($id)) + { + $this->_id = (string) $id; + } + } + + /** + * Get description of goods purchased is displayed on the Sage Pay Form payment page as the customer enters their card details. + * + * @return string + */ + public function getDescription() + { + return $this->_description; + } + + /** + * Set basket description + * + * @param string $description + */ + public function setDescription($description) + { + $this->_description = substr($description, 0, 100); + } + + /** + * Get ID of the seller if using a phone payment. + * + * @return string + */ + public function getAgentId() + { + return $this->_agentId; + } + + /** + * Set ID of the seller if using a phone payment. + * + * @param string $agentId + */ + public function setAgentId($agentId) + { + if (SagepayValid::regex($agentId, '/^[a-zA-Z0-9\ ]{1,16}$/')) + { + $this->_agentId = $agentId; + } + } + + /** + * Get list of items + * + * @return SagepayItem[] + */ + public function getItems() + { + return $this->_items; + } + + /** + * Set the list of items + * + * @param SagepayItem[] $items + */ + public function setItems(array $items) + { + $this->_items = $items; + } + + /** + * Add the item to basket + * + * @param SagepayItem $item + */ + public function addItem(SagepayItem $item) + { + $this->_items[] = $item; + } + + /** + * Get delivery net amount + * + * @return type + */ + public function getDeliveryNetAmount() + { + return number_format($this->_deliveryNetAmount, 2, ".", ""); + } + + /** + * Set delivery net amount + * + * @param float $deliveryNetAmount + */ + public function setDeliveryNetAmount($deliveryNetAmount) + { + $this->_deliveryNetAmount = $deliveryNetAmount; + } + + /** + * Get delivery tax + * + * @return float + */ + public function getDeliveryTaxAmount() + { + return number_format($this->_deliveryTaxAmount, 2, ".", ""); + } + + /** + * Set delivery tax + * + * @param float $deliveryTaxAmount + */ + public function setDeliveryTaxAmount($deliveryTaxAmount) + { + $this->_deliveryTaxAmount = $deliveryTaxAmount; + } + + /** + * Get delivery gross amount + * + * @return float + */ + public function getDeliveryGrossAmount() + { + return number_format($this->_deliveryNetAmount + $this->_deliveryTaxAmount, 2, ".", ""); + } + + /** + * Get list of discounts + * + * @return array + */ + public function getDiscounts() + { + return $this->_discounts; + } + + /** + * Set list of discounts + * + * @param array $discounts + */ + public function setDiscounts(array $discounts) + { + $this->_discounts = $discounts; + } + + /** + * Get shipping ID + * + * @return string + */ + public function getShipId() + { + return $this->_shipId; + } + + /** + * Set shipping ID + * + * @param string $shipId + */ + public function setShipId($shipId) + { + $this->_shipId = $shipId; + } + + /** + * Get shipping method + * + * @return string + */ + public function getShippingMethod() + { + return $this->_shippingMethod; + } + + /** + * Set shipping method + * + * @param string $shippingMethod + */ + public function setShippingMethod($shippingMethod) + { + $this->_shippingMethod = $shippingMethod; + } + + /** + * Get shipping fax number + * + * @return string + */ + public function getShippingFaxNo() + { + return $this->_shippingFaxNo; + } + + /** + * Set shipping fax number + * + * @param string $shippingFaxNo + */ + public function setShippingFaxNo($shippingFaxNo) + { + $this->_shippingFaxNo = $shippingFaxNo; + } + + /** + * Get tour operator structure + * + * @return array + */ + public function getTourOperator() + { + return $this->_tourOperator; + } + + /** + * Set tour operator structure + * + * @param array $tourOperator + */ + public function setTourOperator(array $tourOperator) + { + $this->_tourOperator = $tourOperator; + } + + /** + * Get car rental structure + * + * @return array + */ + public function getCarRental() + { + return $this->_carRental; + } + + /** + * Set car rental structure + * + * @param array $carRental + */ + public function setCarRental(array $carRental) + { + $this->_carRental = $carRental; + } + + /** + * Get hotel structure + * + * @return array + */ + public function getHotel() + { + return $this->_hotel; + } + + /** + * Set hotel structure + * + * @param array $hotel + */ + public function setHotel(array $hotel) + { + $this->_hotel = $hotel; + } + + /** + * Get cruise structure + * + * @return array + */ + public function getCruise() + { + return $this->_cruise; + } + + /** + * Set cruise structure + * + * @param array $cruise + */ + public function setCruise(array $cruise) + { + $this->_cruise = $cruise; + } + + /** + * Get airline structure + * + * @return array + */ + public function getAirline() + { + return $this->_airline; + } + + /** + * Set airline structure + * + * @param array $airline + */ + public function setAirline(array $airline) + { + $this->_airline = $airline; + } + + /** + * get dinerCustomerRef + * + * @return string + */ + public function getDinerCustomerRef() + { + return $this->_dinerCustomerRef; + } + + /** + * Set dinerCustomerRef + * + * @param string $dinerCustomerRef + */ + public function setDinerCustomerRef($dinerCustomerRef) + { + $this->_dinerCustomerRef = $dinerCustomerRef; + } + + /** + * Get the total amount of basket + * + * @return float + */ + public function getAmount() + { +// $amount = $this->getDeliveryGrossAmount(); +// foreach ($this->_items as $item) +// { +// $amount += $item->getTotalGrossAmount(); +// } +// return $amount; + return $this->amount; + $amount = $this->getDeliveryGrossAmount(); + foreach ($this->_items as $item) + { + $amount += $item->getTotalGrossAmount(); + } + foreach ($this->_discounts as $discount){ + $amount -= $discount->getFixed(); + } + return $amount; + } + + public function setAmount($amount){ + $this->amount = $amount; + } + + /** + * Return xml structured or serialized string depends on $asXml + * + * @param bool $asXml + * + * @return string + */ + public function exportAsXml($asXml = true) + { + if ($asXml) + { + return $this->_toXml(); + } + return $this->_serialize(); + } + + /** + * Export as string with Sagepay specific format + * + * @return type + */ + private function _serialize() + { + $values = array(count($this->_items)); + foreach ($this->_items as $item) + { + $itemArr = $item->asArray(); + foreach ($this->_struct as $key) + { + $values[] = is_null($itemArr[$key]) ? '---' : $itemArr[$key]; + } + } + if ($this->getDeliveryGrossAmount() > 0) + { + $values[0]++; + $values[] = 'Delivery'; + $values[] = 1; + $values[] = number_format($this->getDeliveryNetAmount(), 2, ".", ""); + $values[] = number_format($this->getDeliveryTaxAmount(), 2, ".", ""); + $values[] = number_format($this->getDeliveryGrossAmount(), 2, ".", ""); + $values[] = number_format($this->getDeliveryGrossAmount(), 2, ".", ""); + } + + return implode(':', $values); + } + + /** + * Export Basket as XML + * + * @return string + */ + private function _toXml() + { + $dom = new \DOMDocument(); + $dom->formatOutput = false; + $dom->loadXML(''); + foreach ($this->_exportFields as $name) + { + $value = NULL; + $getter = "get" . ucfirst($name); + if (method_exists($this, $getter)) + { + $value = $this->$getter(); + } + + if (empty($value)) + { + continue; + } + + $node = $this->_createDomNode($dom, $value, $name); + if ($node instanceof \DOMNode) + { + $dom->documentElement->appendChild($node); + } + else if ($node instanceof \DOMNodeList) + { + for ($i = 0, $n = $node->length; $i < $n; $i++) + { + $child = $node->item(0); + if ($child instanceof \DOMNode) + { + $dom->documentElement->appendChild($child); + } + } + } + } + return $dom->saveXML($dom->documentElement); + } + + /** + * Create a DOMNode from property + * + * @param \DOMDocument $dom + * @param string $name + * @param mixed $value + * @return \DOMNode|\DOMNodeList + */ + private function _createDomNode($dom, $value, $name = null) + { + if ($value instanceof SagepayItem) + { + return $value->asDomElement($dom); + } + else if ($name === null) + { + return $dom->createElement($value); + } + else if (is_string($value) || is_int($value)) + { + return $dom->createElement($name, trim($value)); + } + else if (is_float($value)) + { + return $dom->createElement($name, number_format($value, 2, '.', '')); + } + else if (is_array($value)) + { + if (count($value) === 0) + { + return null; + } + $base = $dom->createElement($name); + if($name === 'discounts'){ + foreach ($value as $_val){ + if ($_val instanceof SagepayDiscount) { + $base->appendChild( $_val->asDomElement($dom)); + } + } + return $base; + } + if (array_keys($value) !== range(0, count($value) - 1)) + { + // For Associative Array + foreach ($value as $_key => $_val) + { + $node = $this->_createDomNode($dom, $_val, $_key); + $base->appendChild($node); + } + return $base; + } + else + { + foreach ($value as $_val) + { + $node = $this->_createDomNode($dom, $_val); + $base->appendChild($node); + } + return $base->childNodes; + } + } + } + +} diff --git a/Classes/SagepayCardDetails.php b/Classes/SagepayCardDetails.php new file mode 100644 index 0000000..b8c5577 --- /dev/null +++ b/Classes/SagepayCardDetails.php @@ -0,0 +1,155 @@ + array( + array('notEmpty'), + array('creditCard'), + ), + 'cardHolder' => array( + array('notEmpty'), + array('maxLength', array(20)), + array('regex', array("/^[a-zA-Z\xC0-\xFF0-9\s\\\\\/&\.\']*$/")), + ), + 'startDate' => array( + array('regex', array("/^([0-9]{4})*$/")), + ), + 'expiryDate' => array( + array('notEmpty'), + array('regex', array("/^([0-9]{4})*$/")), + ), + 'cv2' => array( + array('notEmpty'), + ), + ); + + /** + * Reading data from inaccessible properties. + * + * @param string $name + * @return string + */ + public function __get($name) + { + $privateName = "_" . $name; + if (property_exists($this, $privateName)) + { + return $this->$privateName; + } + return null; + } + + /** + * Writing data to inaccessible properties + * + * @param string $name + * @param string $value + */ + public function __set($name, $value) + { + $privateName = "_" . $name; + if (property_exists($this, $privateName)) + { + $this->$privateName = $value; + } + } + + + /** + * Validates values using validation rules and return the result + * + * @return array + */ + public function validate() + { + if ($this->cardType == 'AMEX') + { + $this->rules['cv2'][] = array('exactLength', array(3, 4)); + } + else + { + $this->rules['cv2'][] = array('exactLength', array(3)); + } + + $errors = array(); + foreach ($this->rules as $key => $rule) + { + $propertyValue = $this->$key; + $validator = new SagepayValidator($propertyValue, $rule); + if (!$validator->isValid()) + { + $errors[$key] = $validator->getErrors(); + } + } + return $errors; + } + +} diff --git a/Classes/SagepayCommon.php b/Classes/SagepayCommon.php new file mode 100644 index 0000000..a9570fd --- /dev/null +++ b/Classes/SagepayCommon.php @@ -0,0 +1,396 @@ + 40) + { + array_shift($parts); + $vendorTxCode = implode('-', $parts); + } + return $vendorTxCode; + } + + /** + * Extract an order id from a VendorTxCode + * + * @param string $vendorTxCode a valid VendorTxCode + * + * @return boolean|string Returns the Order Id or boolean false + */ + static function vendorTxCode2OrderId($vendorTxCode) + { + $orderId = false; + + if (!empty($vendorTxCode)) + { + $parts = explode('-', $vendorTxCode); + // at the very least there should be 2 parts + if (count($parts) >= 2) + { + $orderId = $parts[count($parts) - 2]; + } + } + + return $orderId; + } + + /** + * Send a POST request to SagePay and return the response as an array. + * + * @param string $url The url to POST to. + * @param array $data The data to post. + * @param int $ttl cURL time of execution + * @param string $caCertPath path to SSL certificate + * + * @return array The response from Sage Pay. + */ + static public function requestPost($url, $data, $ttl = 30, $caCertPath = '') + { + set_time_limit(60); + $output = array(); + $curlSession = curl_init(); + + curl_setopt($curlSession, CURLOPT_URL, $url); + curl_setopt($curlSession, CURLOPT_HEADER, 0); + curl_setopt($curlSession, CURLOPT_POST, 1); + curl_setopt($curlSession, CURLOPT_POSTFIELDS, SagepayUtil::arrayToQueryString($data)); + curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curlSession, CURLOPT_TIMEOUT, $ttl); + curl_setopt($curlSession, CURLOPT_SSL_VERIFYHOST, 2); + + if (!empty($caCertPath)) + { + curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, 1); + curl_setopt($curlSession, CURLOPT_CAINFO, $caCertPath); + } + else + { + curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, 0); + } + + $rawresponse = curl_exec($curlSession); + if (curl_getinfo($curlSession, CURLINFO_HTTP_CODE) !== 200) + { + $output['Status'] = "FAIL"; + $output['StatusDetails'] = "Server Response: " . curl_getinfo($curlSession, CURLINFO_HTTP_CODE); + $output['Response'] = $rawresponse; + + return $output; + } + if (curl_error($curlSession)) + { + $output['Status'] = "FAIL"; + $output['StatusDetail'] = curl_error($curlSession); + $output['Response'] = $rawresponse; + return $output; + } + + curl_close($curlSession); + + $requestForLog= SagepayUtil::arrayToQueryStringRemovingSensitiveData($data,self::$nonSensitiveRequestDataArray ) ; + $response = SagepayUtil::queryStringToArray($rawresponse, "\r\n"); + $responseForLog= SagepayUtil::queryStringToArrayRemovingSensitiveData($rawresponse, "\r\n", self::$nonSensitiveResponseDataArray ); + + SagepayUtil::log("Request:" . PHP_EOL . $requestForLog); + SagepayUtil::log("Response:" . PHP_EOL . json_encode($responseForLog)); + + return array_merge($output, $response); + } + + + /** + * Encrypt the order details ready to send to SagePay Server. + * + * @param SagepayAbstractApi $request The request instance. + * @throws SagepayApiException + * + * @return array|string Returns a String for Form integration method or an array for Server / Direct. + */ + static public function encryptedOrder(SagepayAbstractApi $request) + { + $settings = $request->getConfig(); + $basket = $request->getBasket(); + $address = $request->getAddressList(); + $integrationMethod = $request->getIntegrationMethod(); + $paneValues = $request->getPaneValues(); + // Determine the transaction type based on the payment gateway settings. + $txType = $settings->getTxType(); + $amount = $basket->getAmount(); + $billingAddress = $address[0]; + $deliveryAddress = isset($address[1]) ? $address[1] : null; + + $query = array( + 'VPSProtocol' => $settings->getProtocolVersion(), + 'Vendor' => $settings->getVendorName(), + 'VendorTxCode' => self::vendorTxCode($basket->getId(), $txType, $settings->getVendorName()), + 'Amount' => number_format($amount, 2, '.', ''), + 'Currency' => $settings->getCurrency(), + 'Description' => $basket->getDescription(), + 'CustomerName' => $billingAddress->firstname . ' ' . $billingAddress->lastname, + 'CustomerEMail' => $billingAddress->email, + 'VendorEMail' => $settings->getVendorEmail(), + 'SendEMail' => $settings->getSendEmail(), + 'eMailMessage' => $settings->getEmailMessage(), + 'BillingSurname' => $billingAddress->lastname, + 'BillingFirstnames' => $billingAddress->firstname, + 'BillingAddress1' => $billingAddress->address1, + 'BillingAddress2' => $billingAddress->address2, + 'BillingCity' => $billingAddress->city, + 'BillingPostCode' => $billingAddress->getPostCode(), + 'BillingCountry' => $billingAddress->country, + 'BillingPhone' => $billingAddress->phone, + 'ApplyAVSCV2' => $settings->getApplyAvsCv2(), + 'Apply3DSecure' => $settings->getApply3dSecure(), + 'AllowGiftAid' => $settings->getAllowGiftAid(), + 'BillingAgreement' => $settings->getBillingAgreement(), + 'FailureURL' => $settings->getFullFormFailureUrl(), + 'SuccessURL' => $settings->getFullFormSuccessUrl(), + 'BrowserJavascriptEnabled' => $settings->getBrowserJavascriptEnabled(), + 'BrowserUserAgent' => $settings->getBrowserUserAgent(), + 'BrowserAcceptHeader' => $settings->getBrowserAcceptHeader(), + 'BrowserLanguage' => $settings->getBrowserLanguage(), + 'ChallengeWindowSize' => $settings->getChallengeWindowSize(), + 'ClientIPAddress' => $settings->getClientIPAddress(), + 'TransType' => '01' + ); + + $query += $request->getData(); + + $customer = $request->getCustomer(); + if ($customer instanceof SagepayCustomer) + { + $query += self::_setAuxValue($query, 'CustomerXML', $customer->export()); + } + $query += self::_setAuxValue($query, 'VendorData', $settings->getVendorData()); + $query += self::_setAuxValue($query, 'ReferrerID', $settings->getPartnerId()); + $query += self::_setAuxValue($query, 'Language', $settings->getLanguage()); + + + + // Add check for state for US addresses only. + if ($billingAddress->country == 'US') { + $query['BillingState'] = $billingAddress->state; + } + + //Override with supplied delivery address if we have one . + $query += self::_populateDeliveryDetails($billingAddress, $deliveryAddress); + + if (isset($paneValues['cardType']) && empty($paneValues['cardType'])) + { + $integrationMethod = Constants::SAGEPAY_TOKEN; + } + + // Check if we need to encode cart. + if (!$settings->basketAsXmlDisabled()) + { + $query['BasketXML'] = $basket->exportAsXml(); + } + else + { + $query['Basket'] = $basket->exportAsXml(false); + } + + if (count($settings->getSurcharges()) > 0) + { + $surcharges = new SagepaySurcharge(); + $surcharges->setSurcharges($settings->getSurcharges()); + $query['SurchargeXML'] = $surcharges->export(); + } + + switch ($integrationMethod) + { + case Constants::SAGEPAY_FORM: + // Unset unused values + unset($query['VPSProtocol']); + unset($query['Vendor']); + unset($query['TxType']); + + $env = $settings->getEnv(); + + + $request->setData($query); + $queryStr = SagepayUtil::arrayToQueryString($query); + + $formValues = array(); + $formValues['Vendor'] = $settings->getVendorName(); + $formValues['VPSProtocol'] = $settings->getProtocolVersion(); + $formValues['TxType'] = $txType; + $formValues['Crypt'] = SagepayUtil::encryptAes($queryStr, $settings->getFormEncryptionPassword($env)); + // Encrypt order details using base64 and the secret key from the settings. + return $formValues; + + case Constants::SAGEPAY_SERVER: + $query['NotificationURL'] = $settings->getFullServerNotificationUrl(); + $query['TxType'] = $txType; + $query['Profile'] = $settings->getServerProfile(); + $query['StoreToken'] = 1; + $query += self::_setAuxValue($query, 'AccountType', $settings->getAccountType()); + return $query; + + case Constants::SAGEPAY_DIRECT: + if ($paneValues) { + $query = array_merge($query, self::_getCardDetails($paneValues)); + } +// $query = array_merge($query, self::_getCardDetails($paneValues)); + $query['TxType'] = $txType; +// $query['CardHolder'] = $billingAddress->firstname . ' ' . $billingAddress->lastname; + + // Add 3D Secure flag only if the 3d Secure module is enabled for DIRECT. + $query['Apply3DSecure'] = $settings->getApply3dSecure(); + $query['ThreeDSNotificationURL'] = $settings->getThreeDSNotificationURL(); + $query += self::_setAuxValue($query, 'AccountType', $settings->getAccountType()); + return $query; + + case Constants::SAGEPAY_PAYPAL: + $query['TxType'] = $txType; + $query['CardType'] = 'PAYPAL'; + $query['PayPalCallbackURL'] = $settings->getFullPaypalCallbackUrl() . '?vtx=' . $query['VendorTxCode']; + return $query; + + case Constants::SAGEPAY_TOKEN: + $query['TxType'] = $txType; + $query['Token'] = $paneValues['token']; + $query['CV2'] = $paneValues['cv2']; + $query['AllowGiftAid'] = $paneValues['giftAid'] ? 1 : 0; + $query += self::_setAuxValue($query, 'AccountType', $settings->getAccountType()); + $query['StoreToken'] = 1; + $query['ApplyAVSCV2'] = 2; + return $query; + default : + throw new \SagepayLib\classes\SagepayApiException('Invalid integration type'); + } + } + + /** + * Get the card details + * + * @param array $creditCard + * + * @return array + */ + static private function _getCardDetails($creditCard) + { + $query = array(); + + if (isset($creditCard['startDate']) && !empty($creditCard['startDate'])) + { + $query['StartDate'] = $creditCard['startDate']; + } + + $query['CardType'] = isset($creditCard['cardType']) ? self::_lookupCardType($creditCard['cardType']) : ''; + $query['CardNumber'] = isset($creditCard['cardNumber']) ? $creditCard['cardNumber'] : ''; + $query['ExpiryDate'] = isset($creditCard['expiryDate']) ? $creditCard['expiryDate'] : ''; + $query['CV2'] = isset($creditCard['cv2']) ? $creditCard['cv2'] : ''; + $query['AllowGiftAid'] = (isset($creditCard['giftAid']) && $creditCard['giftAid']) ? 1 : 0; + return $query; + } + + /** + * Populate Delivery Details if exist, otherwise populate with billing details. + * + * @param SagepayCustomerDetails $billingDetails + * @param SagepayCustomerDetails|null $deliveryDetails + * + * @return array + */ + static private function _populateDeliveryDetails(SagepayCustomerDetails $billingDetails, + SagepayCustomerDetails $deliveryDetails = null) + { + $query = array(); + if (is_null($deliveryDetails)) + { + $deliveryDetails = $billingDetails; + } + $query['DeliverySurname'] = $deliveryDetails->lastname; + $query['DeliveryFirstnames'] = $deliveryDetails->firstname; + $query['DeliveryAddress1'] = $deliveryDetails->address1; + $query['DeliveryAddress2'] = $deliveryDetails->address2; + $query['DeliveryPhone'] = $deliveryDetails->phone; + $query['DeliveryCity'] = $deliveryDetails->city; + $query['DeliveryPostCode'] = $deliveryDetails->getPostCode(); + $query['DeliveryCountry'] = $deliveryDetails->country; + + if ($deliveryDetails->country == 'US' && $deliveryDetails->state) + { + $query['DeliveryState'] = $deliveryDetails->state; + } + return $query; + } + + static private function _setAuxValue(array $query, $key, $value) + { + if (!empty($value)) + { + $query[$key] = $value; + } + return $query; + } + + /** + * Seeks and returns the card type + * + * @param string $cardType + * + * @return string + */ + static private function _lookupCardType($cardType) + { + switch ($cardType) + { + case 'mastercard': + return 'MC'; + case 'visaelectron': + return 'UKE'; + default: + return $cardType; + } + } + +} diff --git a/Classes/SagepayCustomer.php b/Classes/SagepayCustomer.php new file mode 100644 index 0000000..fadb6c2 --- /dev/null +++ b/Classes/SagepayCustomer.php @@ -0,0 +1,288 @@ + array( + array('exactLength', array(1)), + ), + 'customerBirth' => array( + array('exactLength', array(10)), + array('regex', array("/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}*$/")), + ), + 'customerWorkPhone' => array( + array('minLength', array(11)), + array('maxLength', array(19)), + array('regex', array("/^[0-9\-a-zA-Z+\s()]*$/")), + ), + 'customerMobilePhone' => array( + array('minLength', array(11)), + array('maxLength', array(19)), + array('regex', array("/^[0-9\-a-zA-Z+\s()]*$/")), + ), + 'previousCust' => array( + array('exactLength', array(1)), + array('regex', array("/^[01]$/")), + ), + 'timeOnFile' => array( + array('maxLength', array(16)), + array('regex', array("/^[0-9]+$/")), + ), + 'customerId' => array( + array('regex', array("/^[A-Za-z0-9]$/")), + ), + + ); + + /** + * Get middle initial of the customer + * + * @return string + */ + public function getCustomerMiddleInitial() + { + return $this->_customerMiddleInitial; + } + + /** + * Set middle initial of the customer + * + * @param string $customerMiddleInitial + */ + public function setCustomerMiddleInitial($customerMiddleInitial) + { + $this->_customerMiddleInitial = $customerMiddleInitial; + } + + /** + * Get date of birth of the customer + * + * @return string + */ + public function getCustomerBirth() + { + return $this->_customerBirth; + } + + /** + * Set date of birth of the customer + * + * @param string $customerBirth + */ + public function setCustomerBirth($customerBirth) + { + $this->_customerBirth = $customerBirth; + } + + /** + * Get work phone number of the customer. + * + * @return string + */ + public function getCustomerWorkPhone() + { + return $this->_customerWorkPhone; + } + + /** + * Set work phone number of the customer. + * + * @param string $customerWorkPhone + */ + public function setCustomerWorkPhone($customerWorkPhone) + { + $this->_customerWorkPhone = $customerWorkPhone; + } + + /** + * Get mobile number of the customer + * + * @return string + */ + public function getCustomerMobilePhone() + { + return $this->_customerMobilePhone; + } + + /** + * Set mobile number of the customer + * + * @param string $customerMobilePhone + */ + public function setCustomerMobilePhone($customerMobilePhone) + { + $this->_customerMobilePhone = $customerMobilePhone; + } + + /** + * Get is a previous customer + * + * @return int + */ + public function getPreviousCust() + { + return $this->_previousCust; + } + + /** + * Set is a previous customer + * + * @param int $previousCust + */ + public function setPreviousCust($previousCust) + { + $this->_previousCust = intval(!!$previousCust); + } + + /** + * Get the number of days since the card was first seen. + * + * @return int + */ + public function getTimeOnFile() + { + return $this->_timeOnFile; + } + + /** + * Set the number of days since the card was first seen. + * + * @param int $timeOnFile + */ + public function setTimeOnFile($timeOnFile) + { + $this->_timeOnFile = intval($timeOnFile); + } + + /** + * Get customer ID + * + * @return string + */ + public function getCustomerId() + { + return $this->_customerId; + } + + /** + * Set customer ID + * + * @param string $customerId + */ + public function setCustomerId($customerId) + { + $this->_customerId = $customerId; + } + + /** + * Export customer details as XML string + * + * @return string XML with customer details + */ + public function export() + { + $dom = new \DOMDocument(); + $dom->loadXML(""); + foreach ($this->_exportFields as $field) + { + $value = NULL; + $getter = 'get' . ucfirst($field); + if (method_exists($this, $getter)) + { + $value = $this->$getter(); + } + + if (empty($value)) + { + continue; + } + $node = $dom->createElement($field, $value); + $dom->documentElement->appendChild($node); + } + return $dom->saveXML($dom->documentElement); + } + +} + diff --git a/Classes/SagepayCustomerDetails.php b/Classes/SagepayCustomerDetails.php new file mode 100644 index 0000000..f472ea4 --- /dev/null +++ b/Classes/SagepayCustomerDetails.php @@ -0,0 +1,254 @@ + array( + array('notEmpty'), + array('maxLength', array(20)), + array('regex', array("/^[a-zA-Z\xC0-\xFF0-9\s\\\\\/&\.\']*$/")), + ), + 'lastname' => array( + array('notEmpty'), + array('maxLength', array(20)), + array('regex', array("/^[a-zA-Z\xC0-\xFF0-9\s\\\\\/&\.\']*$/")), + ), + 'address1' => array( + array('notEmpty'), + array('maxLength', array(100)), + array('regex', array("/^[a-zA-Z\xC0-\xFF0-9\s\+\'\\\\\/&:,\.\-()]*$/")), + ), + 'address2' => array( + array('maxLength', array(100)), + array('regex', array("/^[a-zA-Z\xC0-\xFF0-9\s\+\'\\\\\/&:,\.\-()]*$/")), + ), + 'email' => array( + array('maxLength', array(255)), + array('email'), + ), + 'phone' => array( + array('maxLength', array(20)), + array('regex', array("/^[0-9\-a-zA-Z+\s()]*$/")), + ), + 'city' => array( + array('notEmpty'), + array('maxLength', array(40)), + array('regex', array("/^[a-zA-Z\xC0-\xFF0-9\s\+\'\\\\\/&:,\.\-()]*$/")), + ), + 'postcode' => array( + array('maxLength', array(10)), + array('regex', array("/^[a-zA-Z0-9\s-]*$/")), + ), + 'country' => array( + array('notEmpty'), + array('maxLength', array(2)), + array('regex', array("/^[A-Z]{2}$/")), + ), + 'state' => array( + array('maxLength', array(2)), + array('regex', array("/^([A-Z]{2})*$/")), + ), + ); + + + /** + * Reading data from inaccessible properties. + * + * @param string $name + * @return string + */ + public function __get($name) + { + $privateName = "_" . $name; + if (property_exists($this, $privateName)) + { + return $this->$privateName; + } + return null; + } + + /** + * Writing data to inaccessible properties + * + * @param string $name + * @param string $value + */ + public function __set($name, $value) + { + $privateName = "_" . $name; + if (property_exists($this, $privateName)) + { + $this->$privateName = $value; + } + } + + /** + * Constructor for SagepayCustomerDetails + */ + public function __construct() + { + $this->rules['state'][] = array(array($this, 'validUsa')); + $this->rules['postcode'][] = array(array($this, 'notEmptyZipCodeUK')); + } + + /** + * Get default postcode if the address supplied didn't have one + * + * @param string $default The default value to use when not found or empty + * + * @return string + */ + public function getPostCode($default = '') + { + if (empty($this->_postcode)) + { + $this->_postcode = $default; + } + return $this->_postcode; + } + + /** + * Validates values using validation rules and return the result + * + * @return string[] + */ + public function validate() + { + $errors = array(); + foreach ($this->rules as $key => $rule) + { + $propertyValue = $this->$key; + $validator = new SagepayValidator($propertyValue, $rule); + if (!$validator->isValid()) + { + $errors[$key] = $validator->getErrors(); + } + } + return $errors; + } + + /** + * Validate State Code for US only + * Validate State Code for other country not US + * + * @param string $value + * + * @return boolean + */ + public function validUsa($value) + { + if ($this->_country == 'US') + { + return SagepayValid::notEmpty($value); + } + else + { + return SagepayValid::equals($value, ""); + } + } + + /** + * Validate Zip Code for UK only + * + * @param string $value + * + * @return boolean + */ + public function notEmptyZipCodeUK($value) + { + if ($this->_country == 'GB') + { + return SagepayValid::notEmpty($value); + } + return true; + } + +} diff --git a/Classes/SagepayDirectApi.php b/Classes/SagepayDirectApi.php new file mode 100644 index 0000000..b660c1b --- /dev/null +++ b/Classes/SagepayDirectApi.php @@ -0,0 +1,150 @@ +_vpsDirectUrl = $config->getPurchaseUrl('direct'); + $this->mandatory = array( + 'VPSProtocol', + 'TxType', + 'Vendor', + 'VendorTxCode', + 'Amount', + 'Currency', + 'Description', + 'CardHolder', + 'CardNumber', + 'ExpiryDate', + 'CardType', + 'BillingSurname', + 'BillingFirstnames', + 'BillingAddress1', + 'BillingCity', + 'BillingPostCode', + 'BillingCountry', + 'DeliverySurname', + 'DeliveryFirstnames', + 'DeliveryAddress1', + 'DeliveryCity', + 'DeliveryPostCode', + 'DeliveryCountry', + 'BrowserJavascriptEnabled', + 'BrowserAcceptHeader', + 'BrowserLanguage', + 'BrowserUserAgent', + 'ThreeDSNotificationURL', + 'ChallengeWindowSize', + 'TransType' + ); + } + + /** + * Generate values for payment. + * Ensure that post data is setted to request with SagepayAbstractApi::setData() + * + * @return array The response from Sage Pay + * @throws SagepayApiException + * @see SagepayAbstractApi::createRequest() + */ + public function createRequest() + { + $this->data = SagepayCommon::encryptedOrder($this); + $this->addConfiguredValues(); + $this->checkMandatoryFields(); + + $ttl = $this->config->getRequestTimeout(); + $caCert = $this->config->getCaCertPath(); + return SagepayCommon::requestPost($this->_vpsDirectUrl, $this->data, $ttl, $caCert); + } + + /** + * Set integrationMethod + * + * @param string $integrationMethod + */ + public function setIntegrationMethod($integrationMethod) + { + if (in_array($integrationMethod, array(Constants::SAGEPAY_DIRECT, Constants::SAGEPAY_PAYPAL, Constants::SAGEPAY_TOKEN))) + { + $this->integrationMethod = $integrationMethod; + } + } + + /** + * @see SagepayAbstractApi::getQueryData() + * @return null + */ + public function getQueryData() + { + return null; + } + + /** + * Get vpsDirectUrl + * + * @return string + */ + public function getVpsDirectUrl() + { + return $this->_vpsDirectUrl; + } + + /** + * Set vpsDirectUrl + * + * @uses SagepayValid::url Validate URL field + * @param string $vpsDirectUrl + */ + public function setVpsDirectUrl($vpsDirectUrl) + { + if (SagepayValid::url($vpsDirectUrl)) + { + $this->_vpsDirectUrl = $vpsDirectUrl; + } + } + + protected function checkMandatoryFields() + { + foreach ($this->data as $key => $ele) { + if ( $ele === '') { + unset($this->data[$key]); + } + } + parent::checkMandatoryFields(); // TODO: Change the autogenerated stub + } +} + diff --git a/Classes/SagepayDiscount.php b/Classes/SagepayDiscount.php new file mode 100644 index 0000000..a2f230e --- /dev/null +++ b/Classes/SagepayDiscount.php @@ -0,0 +1,58 @@ +_description; + } + + public function setDescription($description) + { + $this->_description = $description; + } + + public function setFixed($fixed){ + $this->_fixed = $fixed; + } + + public function getFixed(){ + return $this->_fixed; + } + + /** + * Create a DOMNode from property + * + * @param \DOMDocument $basket + * + * @return \DOMNode + */ + public function asDomElement(\DOMDocument $basket) + { + $element = $basket->createElement('discount'); + $fixed = $basket->createElement('fixed', number_format($this->getFixed(), 2, ".", "")); + $description = $basket->createElement('description', substr($this->getDescription(), 0, 100)); + $element->appendChild($fixed); + $element->appendChild($description); + return $element; + } + +} diff --git a/Classes/SagepayFormApi.php b/Classes/SagepayFormApi.php new file mode 100644 index 0000000..3374112 --- /dev/null +++ b/Classes/SagepayFormApi.php @@ -0,0 +1,81 @@ +mandatory = array( + 'VendorTxCode', + 'Amount', + 'Currency', + 'Description', + 'SuccessURL', + 'FailureURL', + 'BillingSurname', + 'BillingFirstnames', + 'BillingAddress1', + 'BillingCity', + 'BillingPostCode', + 'BillingCountry', + 'DeliverySurname', + 'DeliveryFirstnames', + 'DeliveryAddress1', + 'DeliveryCity', + 'DeliveryPostCode', + 'DeliveryCountry', + ); + } + + /** + * Return urlencoded string based on data + * + * @uses SagepayUtil::arrayToQueryString + * @return string + */ + public function getQueryData() + { + // Replace after implemeting right View content + return SagepayUtil::arrayToQueryString($this->data); + } + + /** + * Generate values for payment. + * Ensure that post data is setted to request with SagepayAbstractApi::setData() + * + * @see SagepayAbstractApi::createRequest() + * @uses SagepayCommon::encryptedOrder + * @return array The response from Sage Pay + */ + public function createRequest() + { + $this->addConfiguredValues(); + return SagepayCommon::encryptedOrder($this); + } + +} + diff --git a/Classes/SagepayItem.php b/Classes/SagepayItem.php new file mode 100644 index 0000000..8685b3e --- /dev/null +++ b/Classes/SagepayItem.php @@ -0,0 +1,647 @@ +_description; + } + + /** + * Set description + * + * @param string $description + */ + public function setDescription($description) + { + $this->_description = $description; + } + + /** + * Get unique product identifier code + * + * @return string + */ + public function getProductSku() + { + return $this->_productSku; + } + + /** + * Set unique product identifier code + * + * @param string $productSku + */ + public function setProductSku($productSku) + { + $this->_productSku = $productSku; + } + + /** + * Get product code + * + * @return string + */ + public function getProductCode() + { + return $this->_productCode; + } + + /** + * Set product code + * + * @param string $productCode + */ + public function setProductCode($productCode) + { + $this->_productCode = $productCode; + } + + /** + * Get quantity of the item ordered + * + * @return integer + */ + public function getQuantity() + { + return $this->_quantity; + } + + /** + * Set quantity of the item ordered + * + * @param integer $quantity + */ + public function setQuantity($quantity) + { + $this->_quantity = intval($quantity); + } + + /** + * Get cost of the item before tax + * + * @return float + */ + public function getUnitNetAmount() + { + return $this->_unitNetAmount; + } + + /** + * Set cost of the item before tax + * + * @param float $unitNetAmount + */ + public function setUnitNetAmount($unitNetAmount) + { + $this->_unitNetAmount = floatval($unitNetAmount); + } + + /** + * Get amount of tax on the item + * + * @return float + */ + public function getUnitTaxAmount() + { + return $this->_unitTaxAmount; + } + + /** + * Set amount of tax on the item + * + * @param float $unitTaxAmount + */ + public function setUnitTaxAmount($unitTaxAmount) + { + $this->_unitTaxAmount = floatval($unitTaxAmount); + } + + /** + * Get total cost of the item with tax + * + * @return float + */ + public function getUnitGrossAmount() + { + return $this->_unitNetAmount + $this->_unitTaxAmount; + } + + /** + * Get total cost of the line including quantity and tax + * + * @return float + */ + public function getTotalGrossAmount() + { + return $this->getUnitGrossAmount() * $this->getQuantity(); + } + + /** + * Get first name of the recipient of this item + * + * @return string + */ + public function getRecipientFName() + { + return $this->_recipientFName; + } + + /** + * Set first name of the recipient of this item + * + * @param string $recipientFName + */ + public function setRecipientFName($recipientFName) + { + $this->_recipientFName = $recipientFName; + } + + /** + * Get last name of the recipient of this item + * + * @return string + */ + public function getRecipientLName() + { + return $this->_recipientLName; + } + + /** + * Set last name of the recipient of this item + * + * @param string $recipientLName + */ + public function setRecipientLName($recipientLName) + { + $this->_recipientLName = $recipientLName; + } + + /** + * Get middle initial of the recipient of this item + * + * @return string + */ + public function getRecipientMName() + { + return $this->_recipientMName; + } + + /** + * Set middle initial of the recipient of this item + * + * @param string $recipientMName + */ + public function setRecipientMName($recipientMName) + { + $this->_recipientMName = $recipientMName; + } + + /** + * Get salutation of the recipient of this item + * + * @return string + */ + public function getRecipientSal() + { + return $this->_recipientSal; + } + + /** + * Set salutation of the recipient of this item + * + * @param string $recipientSal + */ + public function setRecipientSal($recipientSal) + { + $this->_recipientSal = $recipientSal; + } + + /** + * Get email of the recipient of this item + * + * @return string + */ + public function getRecipientEmail() + { + return $this->_recipientEmail; + } + + /** + * Set email of the recipient of this item + * + * @param string $recipientEmail + */ + public function setRecipientEmail($recipientEmail) + { + $this->_recipientEmail = $recipientEmail; + } + + /** + * Get phone number of the recipient of this item + * + * @return string + */ + public function getRecipientPhone() + { + return $this->_recipientPhone; + } + + /** + * Set phone number of the recipient of this item + * + * @param string $recipientPhone + */ + public function setRecipientPhone($recipientPhone) + { + $this->_recipientPhone = $recipientPhone; + } + + /** + * Get first address line of the recipient of this item + * + * @return string + */ + public function getRecipientAdd1() + { + return $this->_recipientAdd1; + } + + /** + * Set first address line of the recipient of this item + * + * @param string $recipientAdd1 + */ + public function setRecipientAdd1($recipientAdd1) + { + $this->_recipientAdd1 = $recipientAdd1; + } + + /** + * Get second address line of the recipient of this item + * + * @return string + */ + public function getRecipientAdd2() + { + return $this->_recipientAdd2; + } + + /** + * Set second address line of the recipient of this item + * + * @param string $recipientAdd2 + */ + public function setRecipientAdd2($recipientAdd2) + { + $this->_recipientAdd2 = $recipientAdd2; + } + + /** + * Get city of the recipient of this item + * + * @return string + */ + public function getRecipientCity() + { + return $this->_recipientCity; + } + + /** + * Set city of the recipient of this item + * + * @param string $recipientCity + */ + public function setRecipientCity($recipientCity) + { + $this->_recipientCity = $recipientCity; + } + + /** + * Get code for the state of the recipient of this item + * + * @return string + */ + public function getRecipientState() + { + return $this->_recipientState; + } + + /** + * Set code for the state of the recipient of this item + * + * @param string $recipientState + */ + public function setRecipientState($recipientState) + { + $this->_recipientState = $recipientState; + } + + /** + * Get country code of the recipient of this item + * + * @return string + */ + public function getRecipientCountry() + { + return $this->_recipientCountry; + } + + /** + * Set country code of the recipient of this item + * + * @param string $recipientCountry + */ + public function setRecipientCountry($recipientCountry) + { + $this->_recipientCountry = $recipientCountry; + } + + /** + * Get postcode of the recipient of this item + * + * @return string + */ + public function getRecipientPostCode() + { + return $this->_recipientPostCode; + } + + /** + * Set postcode of the recipient of this item + * + * @param string $recipientPostCode + */ + public function setRecipientPostCode($recipientPostCode) + { + $this->_recipientPostCode = $recipientPostCode; + } + + /** + * Get shipping item number + * + * @return string + */ + public function getItemShipNo() + { + return $this->_itemShipNo; + } + + /** + * Set shipping item number + * + * @param string $itemShipNo + */ + public function setItemShipNo($itemShipNo) + { + $this->_itemShipNo = $itemShipNo; + } + + /** + * Get gift message associated with this item + * + * @return string + */ + public function getItemGiftMsg() + { + return $this->_itemGiftMsg; + } + + /** + * Set gift message associated with this item + * + * @param string $itemGiftMsg + */ + public function setItemGiftMsg($itemGiftMsg) + { + $this->_itemGiftMsg = $itemGiftMsg; + } + + /** + * Create a DOMNode from property + * + * @param \DOMDocument $basket + * + * @return \DOMNode + */ + public function asDomElement(\DOMDocument $basket) + { + $item = $basket->createElement('item'); + $props = get_class_vars(__NAMESPACE__ . "\\" .'SagepayItem'); + foreach ($props as $name => $value) + { + $name = substr($name, 1); + if (substr($name, 0, 9) === 'recipient') + { + continue; + } + $getter = "get" . strtoupper($name); + $value = $this->$getter(); + + $node = null; + if (is_string($value) || is_int($value)) + { + $node = $basket->createElement($name, trim($value)); + } + else if (is_float($value)) + { + $node = $basket->createElement($name, number_format($value, 2, ".", "")); + } + if ($node !== null) + { + $item->appendChild($node); + } + } + return $item; + } + + /** + * Return a array of the item properties + * + * @return array + */ + public function asArray() + { + return array( + 'item' => $this->getDescription(), + 'quantity' => $this->getQuantity(), + 'value' => $this->getUnitNetAmount(), + 'tax' => $this->getUnitTaxAmount(), + 'itemTotal' => $this->getUnitGrossAmount(), + 'lineTotal' => $this->getTotalGrossAmount() + ); + } + +} diff --git a/Classes/SagepayServerApi.php b/Classes/SagepayServerApi.php new file mode 100644 index 0000000..9c59a5f --- /dev/null +++ b/Classes/SagepayServerApi.php @@ -0,0 +1,118 @@ +_vpsServerUrl = $config->getPurchaseUrl('server'); + $this->mandatory = array( + 'VPSProtocol', + 'TxType', + 'Vendor', + 'VendorTxCode', + 'Amount', + 'Currency', + 'Description', + 'NotificationURL', + 'BillingSurname', + 'BillingFirstnames', + 'BillingAddress1', + 'BillingCity', + 'BillingPostCode', + 'BillingCountry', + 'DeliverySurname', + 'DeliveryFirstnames', + 'DeliveryAddress1', + 'DeliveryCity', + 'DeliveryPostCode', + 'DeliveryCountry', + 'StoreToken' + ); + } + + /** + * Generate values for payment. + * Ensure that post data is setted to request with SagepayAbstractApi::setData() + * + * @see SagepayAbstractApi::createRequest() + * @return array The response from Sage Pay + */ + public function createRequest() + { + $this->data = SagepayCommon::encryptedOrder($this); + $this->addConfiguredValues(); + $this->checkMandatoryFields(); + + $ttl = $this->config->getRequestTimeout(); + $caCert = $this->config->getCaCertPath(); + return SagepayCommon::requestPost($this->_vpsServerUrl, $this->data, $ttl, $caCert); + } + + /** + * @see SagepayAbstractApi::getQueryData() + * @return null + */ + public function getQueryData() + { + return null; + } + + /** + * Get vpsServerUrl + * + * @return type + */ + public function getVpsServerUrl() + { + return $this->_vpsServerUrl; + } + + /** + * Set vpsServerUrl + * + * @uses SagepayValid::url Validate URL field + * @param type $vpsServerUrl + */ + public function setVpsServerUrl($vpsServerUrl) + { + if (SagepayValid::url($vpsServerUrl)) + { + $this->_vpsServerUrl = $vpsServerUrl; + } + } + +} + diff --git a/Classes/SagepaySettings.php b/Classes/SagepaySettings.php new file mode 100644 index 0000000..af32a1d --- /dev/null +++ b/Classes/SagepaySettings.php @@ -0,0 +1,1566 @@ +Mandatory.
+ * Specify the correct server environment to connect(test or live).
+ * Default: test + * @var string + */ + private $_env = Constants::SAGEPAY_ENV_TEST; + + /** + * Mandatory.
+ * SagePay Protocol Version used for payment
+ * Default: 3.00 + * @var float + */ + private $_protocolVersion = 3.00; + + /** + * Mandatory. + * Vendor name provided by Sagepay service + * @var string + */ + private $_vendorName = ''; + + /** + * Vendor email + * Set this to the mail address which will receive order confirmations and failures + * @var string + */ + private $_vendorEmail = ''; + + /** + * Use this to pass data you wish to be displayed against the transaction in MySagePay. + * @var string + */ + private $_vendorData = ''; + + /** + * Mandatory. + * Set this to indicate the currency in which you wish to trade. + * Should be ISO 4217 Valid + * @link http://en.wikipedia.org/wiki/ISO_4217 + * @var string + */ + private $_currency = 'GBP'; + + /** + * Mandatory. + * Usually PAYMENT. This can be DEFERRED or AUTHENTICATE + * if your Sage Pay account supports those payment types + * @var string + */ + private $_txType = Constants::SAGEPAY_TXN_PAYMENT; + + /** + * The URL of a vendor's server can be overwritten + * which will send a custom notificationURL and failure/successURL to the Sage Pay gateway + * @var string[] + */ + private $_siteFqdn = array( + 'test' => '', + 'live' => '', + ); + + /** + * If you are a Sage Pay Partner and wish to flag the transactions with your unique partner id, + * it should be set here + * @var string + */ + private $_partnerId = ''; + + /** + * Apply Address Verification Status / Card Verification Value + * 0 = If AVS/CV2 enabled then check them. If rules apply, use rules (default). + * 1 = Force AVS/CV2 checks even if not enabled for the account. If rules apply, use rules. + * 2 = Force NO AVS/CV2 checks even if enabled on account. + * 3 = Force AVS/CV2 checks even if not enabled for the account but DON'T apply any rules. + * @var int + */ + private $_applyAvsCv2 = 0; + + /** + * Apply 3D-Secure + * 0 = If 3D-Secure checks are possible and rules allow, perform the checks and apply the authorisation rules. (default) + * 1 = Force 3D-Secure checks for this transaction if possible and apply rules for authorisation. + * 2 = Do not perform 3D-Secure checks for this transaction and always authorise. + * 3 = Force 3D-Secure checks for this transaction if possible but ALWAYS obtain an auth code, irrespective of rule base. + * @var int + */ + private $_apply3dSecure = 0; + + /** + * For charities registered for Gift Aid, + * set to 1 to display the Gift Aid check + * box on the payment pages, or else 0 + * (Server & Form protocols only) + * @var int + */ + private $_allowGiftAid = 0; + + /** + * Use this to send surcharge xml and override the default values set for your account. + * @var array + */ + private $_surcharges = array(); + + /** + * If you are a dealing with financial transfers then offer the option to collect + * details about the recipient + * @var boolean + */ + private $_collectRecipientDetails = false; + + // FORM Protocol only + + /** + * Set this value to the AES encryption password assigned to you by Sage Pay + * @var array + */ + private $_formPassword = array( + 'test' => '0123456789abcdef', + 'live' => '0123456789abcdef', + ); + + /** + * Set this value for success page redirect for FORM Protocol + * @var string + */ + private $_formSuccessUrl = ''; + + /** + * Set this value for failure page redirect for FORM Protocol + * @var string + */ + private $_formFailureUrl = ''; + + /** + * Send email + * 0 = Do not send either customer or vendor e-mails, + * 1 = Send customer and vendor e-mails if address(es) are provided(DEFAULT). + * 2 = Send Vendor Email but not Customer Email. If you do not supply this field, 1 is assumed and e-mails are sent if addresses are provided. + * @var int + */ + private $_sendEmail = 1; + + /** + * Contents of email message. + * You can specify any custom message to send to your customers in their confirmation e-mail here + * The field can contain HTML if you wish, and be different for each order. + * @var string + */ + private $_emailMessage = 'Thanks for your order'; + + // DIRECT & SERVER Protocol only + + /** + * This value will be used to set the BillingAgreement field in the registration POST + * A default is value of 0 is used if this parameter is not included in this properties file + * @var int + */ + private $_billingAgreement = 0; + + // DIRECT Protocol only + + /** + * Tell the SagePay System which merchant account to use. + * If omitted the system will use E, then M, then C by default. + * E = Use the e-commerce merchant account (default). + * M = Use the mail + * C = Use the continuous authority merchant account (if present). + * @var string + */ + private $_accountType; + + /** + * Any 7 character salt for the local customer password database used by the kit + * @var string + */ + private $_customerPasswordSalt = ''; + + /** + * Set this to false to use colon delimited format for the basket instead of XML + * @var boolean + */ + private $_basketAsXmlDisable = false; + + /** + * Server profile used by default + * @var string + */ + private $_serverProfile = Constants::SAGEPAY_SERVER_PROFILE_NORMAL; + + /** + * Set this value for notification url called by Sagepay System for SERVER Protocol + * @var string + */ + private $_serverNotificationUrl = ''; + + /** + * Set this value for callback url called by PayPal + * @var string + */ + private $_paypalCallbackUrl = ''; + + /** + * List of Purchase URLs used for Registration Transaction + * @var array + */ + private $_purchaseUrls = array( + 'test' => array( + 'form' => Constants::SAGEPAY_FORM_SERVER_TEST, + 'server' => Constants::SAGEPAY_SERVER_SERVER_TEST, + 'direct' => Constants::SAGEPAY_DIRECT_SERVER_TEST, + 'direct3d' => Constants::SAGEPAY_DIRECT_SERVER_3D_SECURE_CALLBACK_TEST, + 'paypal' => Constants::SAGEPAY_PAYPAL_COMPLETION_TEST, + ), + 'live' => array( + 'form' => Constants::SAGEPAY_FORM_SERVER_LIVE, + 'server' => Constants::SAGEPAY_SERVER_SERVER_LIVE, + 'direct' => Constants::SAGEPAY_DIRECT_SERVER_LIVE, + 'direct3d' => Constants::SAGEPAY_DIRECT_SERVER_3D_SECURE_CALLBACK_LIVE, + 'paypal' => Constants::SAGEPAY_PAYPAL_COMPLETION_LIVE, + ) + ); + + /** + * List of Shared URLs used for admin panel actions + * @var array + */ + private $_sharedUrls = array( + 'test' => array( + 'repeat' => Constants::SAGEPAY_SHARED_REPEAT_TRANSACTION_TEST, + 'abort' => Constants::SAGEPAY_SHARED_ABORT_TRANSACTION_TEST, + 'release' => Constants::SAGEPAY_SHARED_RELEASE_TRANSACTION_TEST, + 'refund' => Constants::SAGEPAY_SHARED_REFUND_TRANSACTION_TEST, + 'void' => Constants::SAGEPAY_SHARED_VOID_TRANSACTION_TEST, + 'authorise' => Constants::SAGEPAY_SHARED_AUTHORISE_TRANSACTION_TEST, + 'cancel' => Constants::SAGEPAY_SHARED_CANCEL_TRANSACTION_TEST, + ), + 'live' => array( + 'repeat' => Constants::SAGEPAY_SHARED_REPEAT_TRANSACTION_LIVE, + 'abort' => Constants::SAGEPAY_SHARED_ABORT_TRANSACTION_LIVE, + 'release' => Constants::SAGEPAY_SHARED_RELEASE_TRANSACTION_LIVE, + 'refund' => Constants::SAGEPAY_SHARED_REFUND_TRANSACTION_LIVE, + 'void' => Constants::SAGEPAY_SHARED_VOID_TRANSACTION_LIVE, + 'authorise' => Constants::SAGEPAY_SHARED_AUTHORISE_TRANSACTION_LIVE, + 'cancel' => Constants::SAGEPAY_SHARED_CANCEL_TRANSACTION_LIVE, + ) + ); + + /** + * List of Token URLs used for store/remove token + * @var array + */ + private $_tokenUrls = array( + 'test' => array( + 'register-server' => Constants::SAGEPAY_SERVER_TOKEN_REGISTER_TEST, + 'register-direct' => Constants::SAGEPAY_DIRECT_TOKEN_REGISTER_TEST, + 'remove' => Constants::SAGEPAY_TOKEN_REMOVE_TEST, + ), + 'live' => array( + 'register-server' => Constants::SAGEPAY_SERVER_TOKEN_REGISTER_LIVE, + 'register-direct' => Constants::SAGEPAY_DIRECT_TOKEN_REGISTER_LIVE, + 'remove' => Constants::SAGEPAY_TOKEN_REMOVE_LIVE, + ) + ); + + /** + * If it is true, all logs will be stored in debug.log + * @var boolean + */ + private $_logError = false; + + /** + * The language the customer sees the payment pages in is determined by the code sent here. + * If this is null then the language default of the shoppers browser will be used. + * @var string + */ + private $_language = null; + + /** + * Reference to the website this transaction came from. + * This field is useful if transactions can originate from more than one website. + * @var string + */ + private $_website = null; + + /** + * Timeout for POST cURL requests + * @var int + */ + private $_requestTimeout = 30; + + /** + * The name of a file holding one or more certificates to verify the peer with. + * @var string + */ + private $_caCertPath = ''; + + /** + * Mandatory if enable 3DS2. + * @var string + */ + private $_threeDSNotificationURL = ''; + + private $_browserJavascriptEnabled; + + private $_challengeWindowSize = '05'; + + private $_browserUserAgent; + + private $_browserAcceptHeader; + + private $_browserLanguage; + + private $_transType; + + private $_clientIPAddress; + + private $_browserJavaEnabled; + + private $_browserColorDepth; + + private $_browserScreenHeight; + + private $_browserScreenWidth; + + private $_browserTZ; + + /** + * Initialize the configuration depends on array or if $config is null, + * then load from file. + * + * @param array $config Well-formed associative array + * @param boolean $loadFileConfig Load configurations from file + */ + private function __construct(array $config, $loadFileConfig) + { + if ($loadFileConfig) { + $fileConfig = $this->_loadFileConfig(); + $config = array_merge($fileConfig, $config); + } + $this->_applyConfig($config); + } + + /** + * Restrict clone functionality + */ + private function __clone() + { + } + + /** + * Get instance of settings + * + * @param array $config + * @param boolean $loadFileConfig + * + * @return SagepaySettings + */ + static public function getInstance(array $config = array(), $loadFileConfig = true) + { + if (self::$_instance === null) { + self::$_instance = new SagepaySettings($config, $loadFileConfig); + } + + return self::$_instance; + } + + /** + * Get environment. + * @return string Environment + */ + public function getEnv() + { + return $this->_env; + } + + /** + * Set environment + * + * @param string $_env Environment + */ + public function setEnv($env) + { + if (in_array($env, array(Constants::SAGEPAY_ENV_TEST, Constants::SAGEPAY_ENV_LIVE))) { + $this->_env = $env; + } else { + trigger_error("Invalid Environment value, [test, live] expected, " . $env . " given", E_USER_WARNING); + } + } + + /** + * Get SagePay Protocol Version used for payment + * @return float Protocol version + */ + public function getProtocolVersion() + { + return number_format($this->_protocolVersion, 2); + } + + /** + * Set SagePay Protocol Version used for payment + * + * @param float $_protocolVersion Protocol version + */ + public function setProtocolVersion($protocolVersion) + { + if (is_float($protocolVersion)) { + $this->_protocolVersion = floatval($protocolVersion); + } else { + trigger_error("Invalid Protocol Version value, float expected, " . gettype($protocolVersion) . " given", E_USER_WARNING); + } + } + + /** + * Get vendor name provided by Sagepay service + * @return string Vendor name + */ + public function getVendorName() + { + return $this->_vendorName; + } + + /** + * Set vendor name provided by Sagepay service + * + * @param string $vendorName Vendor name + */ + public function setVendorName($vendorName) + { + $this->_vendorName = $vendorName; + } + + /** + * Get value of vendor data you wish to be displayed against the transaction in MySagePay. + * @return string Vendor data + */ + public function getVendorData() + { + return $this->_vendorData; + } + + /** + * Set value of vendor data you wish to be displayed against the transaction in MySagePay. + * + * @param string $vendorData vendor data + */ + public function setVendorData($vendorData) + { + $this->_vendorData = $vendorData; + } + + /** + * Get currency in which you wish to trade + * @return string Currency + */ + public function getCurrency() + { + return $this->_currency; + } + + /** + * Set currency in which you wish to trade + * + * @param string $currency Currency + */ + public function setCurrency($currency) + { + $this->_currency = $currency; + } + + /** + * Get transaction type + * @return string Transaction type + */ + public function getTxType() + { + return $this->_txType; + } + + /** + * Set transaction type + * + * @param string $txType Transaction type + */ + public function setTxType($txType) + { + $this->_txType = $txType; + } + + /** + * Get list of URL of a vendor's server + * @return array List of siteFqdn + */ + public function getSiteFqdns() + { + return $this->_siteFqdn; + } + + /** + * Set list of URL of a vendor's server + * + * @param array $siteFqdn List of siteFqdn + */ + public function setSiteFqdns(array $siteFqdn) + { + $this->_siteFqdn = $siteFqdn; + } + + /** + * Get unique partner ID + * @return string Partner ID + */ + public function getPartnerId() + { + return $this->_partnerId; + } + + /** + * Set unique partner ID + * + * @param string $partnerId Partner ID + */ + public function setPartnerId($partnerId) + { + $this->_partnerId = $partnerId; + } + + /** + * Get value of Address Verification Status / Card Verification Value option + * @return int Apply AVS/CV2 validation option + */ + public function getApplyAvsCv2() + { + return $this->_applyAvsCv2; + } + + /** + * Set value of Address Verification Status / Card Verification Value option + * + * @param int $applyAvsCv2 Apply AVS/CV2 validation option + */ + public function setApplyAvsCv2($applyAvsCv2) + { + if (in_array($applyAvsCv2, range(0, 3))) { + $this->_applyAvsCv2 = $applyAvsCv2; + } else { + trigger_error("Invalid Apply AVS/CV2 value, [0, 1, 2, 3] expected, " . $applyAvsCv2 . " given", E_USER_WARNING); + } + } + + /** + * Get value of 3D Secure Verification option + * @return int 3D Secure Verification option + */ + public function getApply3dSecure() + { + return $this->_apply3dSecure; + } + + /** + * Set value of 3D Secure Verification option + * + * @param int $apply3dSecure 3D Secure Verification option + */ + public function setApply3dSecure($apply3dSecure) + { + if (in_array($apply3dSecure, range(0, 3))) { + $this->_apply3dSecure = $apply3dSecure; + } else { + trigger_error("Invalid Apply 3D Secure value, [0, 1, 2, 3] expected, " . $apply3dSecure . " given", E_USER_WARNING); + } + } + + /** + * Get value of Allow gift aid option + * @return int Allow gift aid option + */ + public function getAllowGiftAid() + { + return $this->_allowGiftAid; + } + + /** + * Set value of Allow gift aid option + * + * @param int $allowGiftAid Allow gift aid option + */ + public function setAllowGiftAid($allowGiftAid) + { + if (in_array($allowGiftAid, range(0, 1))) { + $this->_allowGiftAid = $allowGiftAid; + } else { + trigger_error("Invalid Allow Gift Aid value, [0, 1] expected, " . $allowGiftAid . " given", E_USER_WARNING); + } + } + + /** + * Get List of Surcharges + * @return array list of Surcharges + */ + public function getSurcharges() + { + return $this->_surcharges; + } + + /** + * Set list of Surcharges + * + * @param array $surcharges list of Surcharges + */ + public function setSurcharges($surcharges) + { + $this->_surcharges = $surcharges; + } + + /** + * Get value of Collect recipient details option + * @return boolean Collect recipient details option + */ + public function getCollectRecipientDetails() + { + return $this->_collectRecipientDetails; + } + + /** + * Set value of Collect recipient details option + * + * @param boolean $collect Collect recipient details option + */ + public function setCollectRecipientDetails($collect) + { + if (is_bool($collect)) { + $this->_collectRecipientDetails = $collect; + } else { + trigger_error("Invalid Collect Recipient Details value, boolean expected, " . gettype($collect) . " given", E_USER_WARNING); + } + } + + /** + * Get list of FORM Protocol encryption password setting + * AES encryption password assigned to you by Sage Pay + * @return array list of FORM Protocol encryption password + */ + public function getFormPassword() + { + return $this->_formPassword; + } + + /** + * Set list of FORM Protocol encryption password setting + * AES encryption password assigned to you by Sage Pay + * + * @param array $formPassword list of FORM Protocol encryption password + */ + public function setFormPassword($formPassword) + { + $this->_formPassword = $formPassword; + } + + /** + * Get success page redirect for FORM Protocol + * @return string FORM Protocol success URL + */ + public function getFormSuccessUrl() + { + return $this->_formSuccessUrl; + } + + + /** + * Set success page redirect for FORM Protocol + * + * @param string $formSuccessUrl FORM Protocol success URL + */ + public function setFormSuccessUrl($formSuccessUrl) + { + $this->_formSuccessUrl = $formSuccessUrl; + } + + /** + * Get failure page redirect for FORM Protocol + * @return string FORM Protocol failure URL + */ + public function getFormFailureUrl() + { + return $this->_formFailureUrl; + } + + /** + * Set failure page redirect for FORM Protocol + * + * @param string $formFailureUrl FORM Protocol failure URL + */ + public function setFormFailureUrl($formFailureUrl) + { + $this->_formFailureUrl = $formFailureUrl; + } + + /** + * Get merchant account type + * @return type + */ + public function getAccountType() + { + return $this->_accountType; + } + + /** + * Set merchant account type + * + * @param string $accountType + */ + public function setAccountType($accountType) + { + if (in_array($accountType, array(Constants::SAGEPAY_ACCOUNT_ECOMMERCE, Constants::SAGEPAY_ACCOUNT_CONTINUOUS, Constants::SAGEPAY_ACCOUNT_MAIL))) { + $this->_accountType = $accountType; + } else { + trigger_error("Invalid Account Type value, [E, C, M] expected, '" . $accountType . "' given", E_USER_WARNING); + } + } + + /** + * Get server profile used by default + * @return type SERVER Protocol profile + */ + public function getServerProfile() + { + return $this->_serverProfile; + } + + /** + * Set server profile used by default + * + * @param string $serverProfile SERVER Protocol profile + */ + public function setServerProfile($serverProfile) + { + if (in_array($serverProfile, array(Constants::SAGEPAY_SERVER_PROFILE_LOW, Constants::SAGEPAY_SERVER_PROFILE_NORMAL))) { + $this->_serverProfile = $serverProfile; + } else { + trigger_error("Invalid Server Profile value, [NORMAL, LOW] expected, '" . $serverProfile . "' given", E_USER_WARNING); + } + } + + /** + * Get value for notification url called by Sagepay System for SERVER Protocol + * @return string SERVER Protocol Notification URL + */ + public function getServerNotificationUrl() + { + return $this->_serverNotificationUrl; + } + + /** + * Set value for notification url called by Sagepay System for SERVER Protocol + * + * @param string $serverNotificationUrl SERVER Protocol Notification URL + */ + public function setserverNotificationUrl($serverNotificationUrl) + { + $this->_serverNotificationUrl = $serverNotificationUrl; + } + + /** + * Get password salt for the local customer password database used by the kit + * @return string Password salt + */ + public function getCustomerPasswordSalt() + { + return $this->_customerPasswordSalt; + } + + /** + * Set password salt for the local customer password database used by the kit + * 7 chars length + * + * @param string $customerPasswordSalt Password salt + */ + public function setCustomerPasswordSalt($customerPasswordSalt) + { + $this->_customerPasswordSalt = substr($customerPasswordSalt, 0, 7); + } + + /** + * Get value of Billing Agreement option + * @return int Billing Agreement option + */ + public function getBillingAgreement() + { + return $this->_billingAgreement; + } + + /** + * Set value of Billing Agreement option + * + * @param int $billingAgreement Billing Agreement option + */ + public function setBillingAgreement($billingAgreement) + { + if (in_array($billingAgreement, array(0, 1), true)) { + $this->_billingAgreement = $billingAgreement; + } else { + trigger_error("Invalid Billing Agreement value, [0, 1] expected, " . $billingAgreement . " given", E_USER_WARNING); + } + } + + /** + * Get value of Send e-mail option + * @return int Send e-mail option + */ + public function getSendEmail() + { + return $this->_sendEmail; + } + + /** + * Set value of Send e-mail option + * + * @param int $sendEmail Send e-mail option + */ + public function setSendEmail($sendEmail) + { + $this->_sendEmail = $sendEmail; + } + + /** + * Get e-mail message + * @return string E-mail message + */ + public function getEmailMessage() + { + return $this->_emailMessage; + } + + /** + * Set e-mail message + * + * @param string $emailMessage E-mail message + */ + public function setEmailMessage($emailMessage) + { + $this->_emailMessage = strip_tags($emailMessage); + } + + /** + * Get value of vendor email + * @return string Vendor email + */ + public function getVendorEmail() + { + return $this->_vendorEmail; + } + + /** + * Set value of vendor email + * + * @param string $vendorEmail Vendor email + */ + public function setVendorEmail($vendorEmail) + { + if (SagepayValid::email($vendorEmail)) { + $this->_vendorEmail = $vendorEmail; + } else { + trigger_error("Invalid Vendor Email value, email format expected, '" . $vendorEmail . "' given", E_USER_WARNING); + } + } + + /** + * Get value of Basket as XML seeting + * @return boolean Basket as XML + */ + public function basketAsXmlDisabled() + { + return $this->_basketAsXmlDisable; + } + + /** + * Set value of Basket as XML seeting + * + * @param boolean $basketAsXmlDisable Basket as XML + */ + public function setBasketAsXmlDisable($basketAsXmlDisable) + { + if (is_bool($basketAsXmlDisable)) { + $this->_basketAsXmlDisable = $basketAsXmlDisable; + } else { + trigger_error("Invalid Basket as XML value, boolean expected, " . gettype($basketAsXmlDisable) . " given", E_USER_WARNING); + } + } + + /** + * Get value of PayPal Callback URL + * @return string PayPal Callback URL + */ + public function getPaypalCallbackUrl() + { + return $this->_paypalCallbackUrl; + } + + /** + * Set value of PayPal Callback URL + * + * @param string $paypalCallbackUrl PayPal Callback URL + */ + public function setPaypalCallbackUrl($paypalCallbackUrl) + { + $this->_paypalCallbackUrl = $paypalCallbackUrl; + } + + /** + * Get list of Registration Service + * @return array List of Registration Service + */ + public function getPurchaseUrls() + { + return $this->_purchaseUrls; + } + + /** + * Set list of Registration Services + * + * @param array $purchaseUrls List of Registration Service + */ + public function setPurchaseUrls($purchaseUrls) + { + $this->_purchaseUrls = $this->_mergeEnvironmentUrls($this->_purchaseUrls, $purchaseUrls); + } + + /** + * Get list of Shared Services + * @return array List of Shared Services + */ + public function getSharedUrls() + { + return $this->_sharedUrls; + } + + /** + * Set list of Shared Services + * + * @param array $sharedUrls List of Shared Services + */ + public function setSharedUrls($sharedUrls) + { + $this->_sharedUrls = $this->_mergeEnvironmentUrls($this->_sharedUrls, $sharedUrls); + } + + /** + * Get list of Token Services + * @return array List of Token Services + */ + public function getTokenUrls() + { + return $this->_tokenUrls; + } + + /** + * Set list of Token Services + * + * @param array $tokenUrls List of Token Services + */ + public function setTokenUrls($tokenUrls) + { + $this->_tokenUrls = $this->_mergeEnvironmentUrls($this->_tokenUrls, $tokenUrls); + } + + /** + * Get Log Error option + * @return boolean + */ + public function getLogError() + { + return $this->_logError; + } + + /** + * Set Log Error option + * + * @param boolean $logError + */ + public function setLogError($logError) + { + if (is_bool($logError)) { + $this->_logError = $logError; + } else { + trigger_error("Invalid Log Error, boolean expected, " . gettype($logError) . " given", E_USER_WARNING); + } + } + + /** + * Get language value ISO 639-1 valid + * @return type + */ + public function getLanguage() + { + return $this->_language; + } + + /** + * Get language value ISO 639-1 valid + * @link http://en.wikipedia.org/wiki/ISO_639 + * + * @param type $language + */ + public function setLanguage($language) + { + $this->_language = substr($language, 0, 2); + } + + /** + * Get reference to the website this transaction came from. + * @return type + */ + public function getWebsite() + { + return $this->_website; + } + + /** + * Set reference to the website this transaction came from. + * + * @param type $website + */ + public function setWebsite($website) + { + if (!empty($website) && SagepayValid::url($website)) { + $this->_website = $website; + } else { + //trigger_error("Invalid Website URL value, email format expected, '" . $website . "' given", E_USER_WARNING); + } + } + + /** + * Get timeout for POST cURL requests + * @return int + */ + public function getRequestTimeout() + { + return $this->_requestTimeout; + } + + /** + * Set timeout for POST cURL requests + * + * @param int $ttl + */ + public function setRequestTimeout($ttl) + { + if (is_int($ttl)) { + $this->_requestTimeout = $ttl; + } else { + trigger_error("Invalid request timeout value, integer expected, '" . $ttl . "' given", E_USER_WARNING); + } + } + + /** + * Get CACert file path + * @return string + */ + public function getCaCertPath() + { + return $this->_caCertPath; + } + + /** + * Set CACert file path + * + * @param string $caCertPath + */ + public function setCaCertPath($caCertPath) + { + $this->_caCertPath = $caCertPath; + } + + /** + * Get value of siteFqdn for specific environment + * + * @param string $env Specific environment + * + * @return string SiteFqdn URL + */ + public function getSiteFqdn($env = '') + { + $env = $this->_validEnvironment($env); + + return $this->_siteFqdn[$env]; + } + + /** + * Set value of siteFqdn for specific environment + * + * @param string $siteFqdn SiteFqdn URL + * @param string $env Specific environment + */ + public function setSiteFqdn($siteFqdn, $env = '') + { + $env = $this->_validEnvironment($env); + $this->_siteFqdn[$env] = $siteFqdn; + } + + /** + * Set Encryption password for specific environment + * + * @param string $password Encryption password + * @param string $env Environment name, by default is using current environment value + */ + public function setFormEncryptionPassword($password, $env = '') + { + $env = $this->_validEnvironment($env); + $this->_formPassword[$env] = $password; + } + + + /** + * Get full URL for Form Successes + * + * @param string $env Specific environment + * + * @return string + */ + public function getFullFormSuccessUrl($env = '') + { + $base = $this->getSiteFqdn($env); + + return $base . $this->_formSuccessUrl; + } + + /** + * Get full URL for Form Failures + * + * @param string $env Specific environment + * + * @return string + */ + public function getFullFormFailureUrl($env = '') + { + $base = $this->getSiteFqdn($env); + + return $base . $this->_formFailureUrl; + } + + /** + * Get full URL for Server Notifications + * + * @param string $env Specific environment + * + * @return string + */ + public function getFullServerNotificationUrl($env = '') + { + $base = $this->getSiteFqdn($env); + + return $base . $this->_serverNotificationUrl; + } + + /** + * Get Encryption password for specific environment + * + * @param string $env Environment name, by default is using current environment value + * + * @return string Encryption password + */ + public function getFormEncryptionPassword($env = '') + { + $env = $this->_validEnvironment($env); + + return $this->_formPassword[$env]; + } + + /** + * Get value of specific Registration Service + * + * @param string $method Method alias + * @param string $env Environment name, by default is using current environment value + * + * @return string Registration Service URL + */ + public function getPurchaseUrl($method, $env = '') + { + $env = $this->_validEnvironment($env); + if (isset($this->_purchaseUrls[$env][$method])) { + return $this->_purchaseUrls[$env][$method]; + } + + return ''; + } + + /** + * Set value of specific method of Registration Services + * + * @param string $purchaseUrl Registration Service URL + * @param string $method Method alias + * @param string $env Environment name, by default is using current environment value + */ + public function setPurchaseUrl($purchaseUrl, $method, $env = '') + { + $env = $this->_validEnvironment($env); + if (!empty($method) && !empty($purchaseUrl)) { + $this->_purchaseUrls[$env][$method] = $purchaseUrl; + } + } + + /** + * Get value of specific method of Shared Services + * + * @param string $method Method alias + * @param string $env Environment name, by default is using current environment value + * + * @return string Shared Service URL + */ + public function getSharedUrl($method, $env = '') + { + $env = $this->_validEnvironment($env); + if (isset($this->_sharedUrls[$env][$method])) { + return $this->_sharedUrls[$env][$method]; + } + + return ''; + } + + /** + * Set value of specific method of Shared Services + * + * @param string $sharedUrl Shared Service URL + * @param string $method Method alias + * @param string $env Environment name, by default is using current environment value + */ + public function setSharedUrl($sharedUrl, $method, $env = '') + { + $env = $this->_validEnvironment($env); + if (!empty($method) && !empty($sharedUrl)) { + $this->_sharedUrls[$env][$method] = $sharedUrl; + } + } + + /** + * Get url for specific token service and environment + * + * @param string $method Method name + * @param string $env Environment name, by default is using current environment value + * + * @return string Token Service URL + */ + public function getTokenUrl($method, $env = '') + { + + $env = $this->_validEnvironment($env); + if (isset($this->_tokenUrls[$env][$method])) { + return $this->_tokenUrls[$env][$method]; + } + + return ''; + } + + /** + * Set url for specific token service and environment + * + * @param string $tokenUrl Token Service URL + * @param string $method Method name + * @param string $env Environment name, by default is using current environment value + */ + public function setTokenUrl($tokenUrl, $method, $env = '') + { + $env = $this->_validEnvironment($env); + if (!empty($method) && !empty($tokenUrl)) { + $this->_tokenUrls[$env][$method] = $tokenUrl; + } + } + + public function setThreeDSNotificationURL($url) + { + $this->_threeDSNotificationURL = $url; + } + + public function getThreeDSNotificationURL($env = '') + { + $base = $this->getSiteFqdn($env); + + return $base . $this->_threeDSNotificationURL; + } + + /** + * @return int + */ + public function getBrowserJavascriptEnabled() + { + return $this->_browserJavascriptEnabled; + } + + /** + * @param int $browerJSEnable + */ + public function setBrowserJavascriptEnabled($browserJSEnable) + { + $this->_browserJavascriptEnabled = $browserJSEnable; + } + + /** + * @return string + */ + public function getChallengeWindowSize() + { + return $this->_challengeWindowSize; + } + + /** + * @param string $challengeWindowSize + */ + public function setChallengeWindowSize($challengeWindowSize = '01') + { + $this->_challengeWindowSize = $challengeWindowSize; + } + + /** + * @return mixed + */ + public function getBrowserUserAgent() + { + return $this->_browserUserAgent; + } + + /** + * @param mixed $browserUserAgent + */ + public function setBrowserUserAgent($browserUserAgent) + { + $this->_browserUserAgent = $browserUserAgent; + } + + /** + * @return mixed + */ + public function getBrowserAcceptHeader() + { + return $this->_browserAcceptHeader; + } + + /** + * @param mixed $browserAcceptHeader + */ + public function setBrowserAcceptHeader($browserAcceptHeader) + { + $this->_browserAcceptHeader = $browserAcceptHeader; + } + + /** + * @return mixed + */ + public function getBrowserLanguage() + { + return $this->_browserLanguage; + } + + /** + * @param mixed $browserLanguage + */ + public function setBrowserLanguage($browserLanguage) + { + $this->_browserLanguage = $browserLanguage; + } + + /** + * @return mixed + */ + public function getTransType() + { + return $this->_transType; + } + + /** + * @param mixed $transType + */ + public function setTransType($transType) + { + $this->_transType = $transType; + } + + /** + * @return mixed + */ + public function getClientIPAddress() + { + return $this->_clientIPAddress; + } + + /** + * @param mixed $clientIPAddress + */ + public function setClientIPAddress($clientIPAddress) + { + $this->_clientIPAddress = $clientIPAddress; + } + + /** + * @return mixed + */ + public function getBrowserJavaEnabled() + { + return $this->_browserJavaEnabled; + } + + /** + * @param mixed $browserJavaEnabled + */ + public function setBrowserJavaEnabled($browserJavaEnabled) + { + $this->_browserJavaEnabled = $browserJavaEnabled; + } + + /** + * @return mixed + */ + public function getBrowserColorDepth() + { + return $this->_browserColorDepth; + } + + /** + * @param mixed $browserColorDepth + */ + public function setBrowserColorDepth($browserColorDepth) + { + $this->_browserColorDepth = $browserColorDepth; + } + + /** + * @return mixed + */ + public function getBrowserScreenHeight() + { + return $this->_browserScreenHeight; + } + + /** + * @param mixed $browserScreenHeight + */ + public function setBrowserScreenHeight($browserScreenHeight) + { + $this->_browserScreenHeight = $browserScreenHeight; + } + + /** + * @return mixed + */ + public function getBrowserScreenWidth() + { + return $this->_browserScreenWidth; + } + + /** + * @param mixed $browserScreenWidth + */ + public function setBrowserScreenWidth($browserScreenWidth) + { + $this->_browserScreenWidth = $browserScreenWidth; + } + + /** + * @return mixed + */ + public function getBrowserTZ() + { + return $this->_browserTZ; + } + + /** + * @param mixed $browserTZ + */ + public function setBrowserTZ($browserTZ) + { + $this->_browserTZ = $browserTZ; + } + + public function getFullPaypalCallbackUrl($env = '') + { + $base = $this->getSiteFqdn($env); + + return $base . $this->_paypalCallbackUrl; + } + + /** + * Load file configuration and return the $config array + * @return array Configuration + */ + private function _loadFileConfig() + { + return include Constants::SAGEPAY_SDK_PATH . '/config.php'; + } + + /** + * Insert values of array config into current instance SagepayConfig + * + * @param array $config + */ + private function _applyConfig(array $config) + { + foreach ($config as $key => $val) { + $prop = 'set' . ucfirst($key); + if (method_exists($this, $prop)) { + $this->$prop($val); + } + } + } + + /** + * Validate value for environment + * Set up current env if it is missing or is not valid environment + * + * @param string $env Environment + * + * @return string Environment + */ + private function _validEnvironment($env) + { + if (!in_array($env, array(Constants::SAGEPAY_ENV_TEST, Constants::SAGEPAY_ENV_LIVE))) { + return $this->_env; + } + + return $env; + } + + /** + * Recursive merge of URLs + * + * @param array $oldUrls Old URLs values + * @param array $newUrls New URLs values + * + * @return array + */ + private function _mergeEnvironmentUrls(array $oldUrls, array $newUrls) + { + foreach ($oldUrls as $env => $methods) { + foreach (array_keys($methods) as $method) { + if (isset($newUrls[$env][$method]) && !empty($newUrls[$env][$method])) { + $oldUrls[$env][$method] = $newUrls[$env][$method]; + } + } + } + + return $oldUrls; + } + +} diff --git a/Classes/SagepaySurcharge.php b/Classes/SagepaySurcharge.php new file mode 100644 index 0000000..a174552 --- /dev/null +++ b/Classes/SagepaySurcharge.php @@ -0,0 +1,130 @@ +_surcharges; + } + + /** + * Set surcharges + * + * @param array $surcharges + */ + public function setSurcharges($surcharges) + { + $this->_surcharges = $surcharges; + } + + /** + * Add a surcharge to list + * + * @param array $surcharge + */ + private function _addSurcharge($surcharge) + { + $this->_surcharges[] = $surcharge; + } + + /** + * List of fields that should be exported to surcharges XML + * + * @var array + */ + private $_exportFields = array( + 'paymentType', + 'percentage', + 'fixed', + ); + + /** + * Add surcharge by details + * @uses SagepayUtil::cardTypes List of cards + * + * @param string $paymentType + * @param float $percentage + * @param float $fixed + * + * @return boolean + */ + public function addSurchargeDetails($paymentType, $percentage = null, $fixed = null) + { + if (!in_array(strtolower($paymentType), SagepayUtil::cardTypes())) + { + return false; + } + + $surcharge = array('paymentType' => $paymentType); + if (!empty($percentage)) + { + $surcharge['percentage'] = $percentage; + $this->_addSurcharge($surcharge); + return true; + } + + if (!empty($fixed)) + { + $surcharge['fixed'] = $fixed; + $this->_addSurcharge($surcharge); + return true; + } + + return false; + } + + /** + * Export surcharges details as XML string + * + * @return string XML with surcharges details + */ + public function export() + { + $dom = new \DOMDocument(); + $dom->loadXML(""); + + foreach ($this->_surcharges as $surcharge) + { + $surchargeEl = $dom->createElement('surcharge'); + $exportFieldsCount = 0; + foreach ($this->_exportFields as $field) + { + if (isset($surcharge[$field]) && $exportFieldsCount < 2) + { + $exportFieldsCount++; + $node = $dom->createElement($field, $surcharge[$field]); + $surchargeEl->appendChild($node); + } + } + $dom->documentElement->appendChild($surchargeEl); + } + + return $dom->saveXML($dom->documentElement); + } + +} diff --git a/Classes/SagepayToken.php b/Classes/SagepayToken.php new file mode 100644 index 0000000..e6d8273 --- /dev/null +++ b/Classes/SagepayToken.php @@ -0,0 +1,180 @@ +_config = $config; + $this->_registerUrl = $config->getTokenUrl('register-direct'); + $this->_removeUrl = $config->getTokenUrl('remove'); + } + + /** + * Get URL for card registration [RFC1738 valid] + * + * @return string + */ + public function getRegisterUrl() + { + return $this->_registerUrl; + } + + /** + * Set URL for card registration [RFC1738 valid] + * + * @uses SagepayValid::url Validate URL field + * @param string $registerUrl + */ + public function setRegisterUrl($registerUrl) + { + if (SagepayValid::url($registerUrl)) + { + $this->_registerUrl = $registerUrl; + } + } + + /** + * Get URL for card remove [RFC1738 valid] + * + * @return string + */ + public function getRemoveUrl() + { + return $this->_removeUrl; + } + + /** + * Set URL for card remove [RFC1738 valid] + * + * @uses SagepayValid::url Validate URL field + * @param string $removeUrl + */ + public function setRemoveUrl($removeUrl) + { + if (SagepayValid::url($removeUrl)) + { + $this->_removeUrl = $removeUrl; + } + } + + /** + * Prepare data for register request + * + * @param array $cardDetails + * @return array + */ + private function _prepareRegisterData(array $cardDetails) + { + return array( + 'VPSProtocol' => $this->_config->getProtocolVersion(), + 'TxType' => self::TOKEN, + 'Vendor' => $this->_config->getVendorName(), + 'Currency' => $this->_config->getCurrency(), + 'CardHolder' => isset($cardDetails['cardHolder']) ? $cardDetails['cardHolder'] : NULL, + 'CardNumber' => isset($cardDetails['cardNumber']) ? $cardDetails['cardNumber'] : NULL, + 'ExpiryDate' => isset($cardDetails['expiryDate']) ? $cardDetails['expiryDate'] : NULL, + 'CV2' => isset($cardDetails['cv2']) ? $cardDetails['cv2'] : NULL, + 'CardType' => isset($cardDetails['cardType']) ? $cardDetails['cardType'] : NULL, + ); + } + + /** + * Prepare data for remove request + * + * @param string $token + * @return array + */ + private function _prepareRemoveData($token) + { + return array( + 'VPSProtocol' => $this->_config->getProtocolVersion(), + 'TxType' => self::REMOVETOKEN, + 'Vendor' => $this->_config->getVendorName(), + 'Token' => $token, + ); + } + + /** + * Register card to Sagepay Service + * + * @param string[] $cardDetails Associative array with card details + * + * @return string|null + */ + public function register(array $cardDetails) + { + $data = $this->_prepareRegisterData($cardDetails); + $ttl = $this->_config->getRequestTimeout(); + $caCert = $this->_config->getCaCertPath(); + $response = SagepayCommon::requestPost($this->_registerUrl, $data, $ttl, $caCert); + return isset($response['Token']) ? $response['Token'] : NULL; + } + + /** + * Remove token from Sagepay Service + * + * @param string $token Token GUID provided by Sagepay service on card registration + * + * @return boolean + */ + public function remove($token) + { + $data = $this->_prepareRemoveData($token); + $ttl = $this->_config->getRequestTimeout(); + $caCert = $this->_config->getCaCertPath(); + $response = SagepayCommon::requestPost($this->_removeUrl, $data, $ttl, $caCert); + return ($response['Status'] == Constants::SAGEPAY_REMOTE_STATUS_OK); + } + +} diff --git a/Classes/SagepayUtil.php b/Classes/SagepayUtil.php new file mode 100644 index 0000000..9990224 --- /dev/null +++ b/Classes/SagepayUtil.php @@ -0,0 +1,350 @@ + 'Visa', + 'visaelectron' => 'Visa Electron', + 'mastercard' => 'Mastercard', + 'amex' => 'American Express', + 'delta' => 'Delta', + 'dc' => 'Diners Club', + 'jcb' => 'JCB', + 'laser' => 'Laser', + 'maestro' => 'Maestro', + ); + + /** + * The card types that SagePay supports. + * + * @return array Array of card codes. + */ + static public function cardTypes() + { + return array_keys(self::$cardNames); + } + + /** + * Populate the card names in to a usable array. + * + * @param array $availableCards Available card codes. + * + * @return array Array of card codes and names. + */ + static public function availableCards(array $availableCards) + { + $cardArr = array(); + + // Filter input card types + foreach ($availableCards as $code) + { + $code = strtolower($code); + if ((array_key_exists($code, self::$cardNames))) + { + $cardArr[$code] = self::$cardNames[$code]; + } + } + + return $cardArr; + } + + /** + * PHP's mcrypt does not have built in PKCS5 Padding, so we use this. + * + * @param string $input The input string. + * + * @return string The string with padding. + */ + static protected function addPKCS5Padding($input) + { + $blockSize = 16; + $padd = ""; + + // Pad input to an even block size boundary. + $length = $blockSize - (strlen($input) % $blockSize); + for ($i = 1; $i <= $length; $i++) + { + $padd .= chr($length); + } + + return $input . $padd; + } + + /** + * Remove PKCS5 Padding from a string. + * + * @param string $input The decrypted string. + * + * @return string String without the padding. + * @throws SagepayApiException + */ + static protected function removePKCS5Padding($input) + { + $blockSize = 16; + $padChar = ord($input[strlen($input) - 1]); + + /* Check for PadChar is less then Block size */ +// if ($padChar > $blockSize) +// { +// throw new \SagepayLib\classes\SagepayApiException('Invalid encryption string'); +// } + /* Check by padding by character mask */ +// if (strspn($input, chr($padChar), strlen($input) - $padChar) != $padChar) +// { +// throw new \SagepayLib\classes\SagepayApiException('Invalid encryption string'); +// } + + $unpadded = substr($input, 0, (-1) * $padChar); + /* Chech result for printable characters */ + if (preg_match('/[[:^print:]]/', $unpadded)) + { + throw new \SagepayLib\classes\SagepayApiException('Invalid encryption string'); + } + return $unpadded; + } + + /** + * Encrypt a string ready to send to SagePay using encryption key. + * + * @param string $string The unencrypyted string. + * @param string $key The encryption key. + * + * @return string The encrypted string. + */ + static public function encryptAesOld($string, $key) + { + // AES encryption, CBC blocking with PKCS5 padding then HEX encoding. + // Add PKCS5 padding to the text to be encypted. + $string = self::addPKCS5Padding($string); + + // Perform encryption with PHP's MCRYPT module. + //$crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $key); + $crypt = openssl_encrypt($string, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $key); + + // Perform hex encoding and return. + return "@" . strtoupper(bin2hex($crypt)); + } + + static public function encryptAes($string, $key) + { + $cryptor = new \phpseclib\Crypt\AES(\phpseclib\Crypt\Base::MODE_CBC); + $cryptor->setKey($key); + $cryptor->setIV($key); + $binCipherText = $cryptor->encrypt($string); + $hexaText = bin2hex($binCipherText); + $uppercaseHexaText = strtoupper($hexaText); + + return "@$uppercaseHexaText"; + } + + /** + * Decode a returned string from SagePay. + * + * @param string $strIn The encrypted String. + * @param string $password The encyption password used to encrypt the string. + * + * @return string The unecrypted string. + * @throws SagepayApiException + */ + static public function decryptAesOld($strIn, $password) + { + // HEX decoding then AES decryption, CBC blocking with PKCS5 padding. + // Use initialization vector (IV) set from $str_encryption_password. + $strInitVector = $password; + + // Remove the first char which is @ to flag this is AES encrypted and HEX decoding. + $hex = substr($strIn, 1); + + // Throw exception if string is malformed + if (!preg_match('/^[0-9a-fA-F]+$/', $hex)) + { + throw new \SagepayLib\classes\SagepayApiException('Invalid encryption string'); + } + $strIn = pack('H*', $hex); + + // Perform decryption with PHP's MCRYPT module. + //$string2 = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, $strIn, MCRYPT_MODE_CBC, $strInitVector); + $string = openssl_decrypt($strIn, 'AES-128-CBC', $password, OPENSSL_RAW_DATA, $strInitVector); + return self::removePKCS5Padding($string); + } + + static public function decryptAes($strIn, $password) + { + $cryptor = new \phpseclib\Crypt\AES(\phpseclib\Crypt\Base::MODE_CBC); + $cryptor->setKey($password); + $cryptor->setIV($password); + $hex = substr($strIn, 1); + // Throw exception if string is malformed + if (!preg_match('/^[0-9a-fA-F]+$/', $hex)) { + throw new \SagepayLib\classes\SagepayApiException('Invalid encryption string'); + } + $strIn = pack('H*', $hex); + return $cryptor->decrypt($strIn); + } + + /** + * Convert a data array to a query string ready to post. + * + * @param array $data The data array. + * @param string $delimeter Delimiter used in query string + * @param boolean $urlencoded If true encode the final query string + * + * @return string The array as a string. + */ + static public function arrayToQueryString(array $data, $delimiter = '&', $urlencoded = false) + { + $queryString = ''; + $delimiterLength = strlen($delimiter); + + // Parse each value pairs and concate to query string + foreach ($data as $name => $value) + { + // Apply urlencode if it is required + if ($urlencoded) + { + $value = urlencode($value); + } + $queryString .= $name . '=' . $value . $delimiter; + } + + // remove the last delimiter + return substr($queryString, 0, -1 * $delimiterLength); + } + + static public function arrayToQueryStringRemovingSensitiveData(array $data,array $nonSensitiveDataKey, $delimiter = '&', $urlencoded = false) + { + $queryString = ''; + $delimiterLength = strlen($delimiter); + + // Parse each value pairs and concate to query string + foreach ($data as $name => $value) + { + if (!in_array($name, $nonSensitiveDataKey)){ + $value= self::MASK_FOR_HIDDEN_FIELDS; + } + else if ($urlencoded){ + $value = urlencode($value); + } + // Apply urlencode if it is required + + $queryString .= $name . '=' . $value . $delimiter; + } + + // remove the last delimiter + return substr($queryString, 0, -1 * $delimiterLength); + } + /** + * Convert string to data array. + * + * @param string $data Query string + * @param string $delimeter Delimiter used in query string + * + * @return array + */ + static public function queryStringToArray($data, $delimeter = "&") + { + // Explode query by delimiter + $pairs = explode($delimeter, $data); + $queryArray = array(); + + // Explode pairs by "=" + foreach ($pairs as $pair) + { + $keyValue = explode('=', $pair); + + // Use first value as key + $key = array_shift($keyValue); + + // Implode others as value for $key + $queryArray[$key] = implode('=', $keyValue); + } + return $queryArray; + } + + static public function queryStringToArrayRemovingSensitiveData($data, $delimeter = "&", $nonSensitiveDataKey) + { + // Explode query by delimiter + $pairs = explode($delimeter, $data); + $queryArray = array(); + + // Explode pairs by "=" + foreach ($pairs as $pair) + { + $keyValue = explode('=', $pair); + // Use first value as key + $key = array_shift($keyValue); + if (in_array($key, $nonSensitiveDataKey)){ + $keyValue = explode('=', $pair); + } + else{ + $keyValue = array(self::MASK_FOR_HIDDEN_FIELDS); + } + // Implode others as value for $key + $queryArray[$key] = implode('=', $keyValue); + + } + return $queryArray; + } + /** + * Logging the debugging information to "debug.log" + * + * @param string $message + * @return boolean + */ + static public function log($message) + { + $settings = SagepaySettings::getInstance(); + if ($settings->getLogError()) + { + $filename = Constants::SAGEPAY_SDK_PATH . '/debug.log'; + $line = '[' . date('Y-m-d H:i:s') . '] :: ' . $message; + try + { + $file = fopen($filename, 'a+'); + fwrite($file, $line . PHP_EOL); + fclose($file); + } catch (\Exception $ex) + { + return false; + } + } + return true; + } + + /** + * Extract last 4 digits from card number; + * + * @param string $cardNr + * + * @return string + */ + static public function getLast4Digits($cardNr) + { + // Apply RegExp to extract last 4 digits + $matches = array(); + if (preg_match('/\d{4}$/', $cardNr, $matches)) + { + return $matches[0]; + } + return ''; + } + +} + diff --git a/Classes/SagepayValid.php b/Classes/SagepayValid.php new file mode 100644 index 0000000..0733580 --- /dev/null +++ b/Classes/SagepayValid.php @@ -0,0 +1,225 @@ + 0; + } + + /** + * Checks a field against a regular expression. + * + * @param string $input value + * @param string $regexp regular expression to match (including delimiters) + * + * @return boolean + */ + public static function regex($input, $regexp) + { + return (bool) preg_match($regexp, (string) $input); + } + + /** + * Checks that a field is greater or equal with minimum required. + * + * @param string $input value + * @param integer $length minimum length required + * + * @return boolean + */ + public static function minLength($input, $length) + { + return strlen($input) >= $length; + } + + /** + * Checks that a field is less or equal with maximum required + * + * @param string $input value + * @param integer $length maximum length required + * + * @return boolean + */ + public static function maxLength($input, $length) + { + return strlen($input) <= $length; + } + + /** + * Checks that a number is within a range. + * + * @param string $input number to check + * @param integer $minValue minimum value + * @param integer $maxValue maximum value + * + * @return boolean + */ + public static function range($input, $minValue, $maxValue) + { + return ($input >= $minValue && $input <= $maxValue); + } + + /** + * Checks that a field have exact length. + * + * @param string $input value + * @param integer|array $length exact length required, or array of valid lengths + * + * @return boolean + */ + public static function exactLength($input, $length) + { + if (is_array($length)) + { + foreach ($length as $specificLength) + { + if (strlen($input) === $specificLength) + { + return TRUE; + } + } + return FALSE; + } + + return strlen($input) === $length; + } + + /** + * Checks that a field is same as required. + * + * @param string $input original value + * @param string $expected expected value + * + * @return boolean + */ + public static function equals($input, $expected) + { + return ($input === $expected); + } + + /** + * Check an email address for valid format. + * + * @param string $input email address + * + * @return boolean + */ + public static function email($input) + { + if ($input === '') + { + return true; + } + return (bool) filter_var($input, FILTER_VALIDATE_EMAIL); + } + + /** + * Check an URL for valid format. + * + * @param string $input URL + * + * @return boolean + */ + static public function url($input) + { + return (bool) filter_var($input, FILTER_VALIDATE_URL); + } + + /** + * Validates a credit card number with luhn checksum + * + * @uses SagepayValid::luhn + * @param integer $input credit card number + * + * @return boolean + */ + public static function creditCard($input) + { + $input = preg_replace('/\D+/', '', $input); + return self::luhn($input); + } + + /** + * Validate a number against the Luhn checksum + * + * @link http://en.wikipedia.org/wiki/Luhn_algorithm + * @param string $input number to check + * + * @return boolean + */ + public static function luhn($input) + { + if (!ctype_digit($input)) + { + return FALSE; + } + + $checksum = ''; + + foreach (str_split(strrev($input)) as $i => $d) + { + $checksum .= $i % 2 !== 0 ? $d * 2 : $d; + } + + return array_sum(str_split($checksum)) % 10 === 0; + } + + /** + * Checks whether a string consists of digits only. + * + * @param string $input input string + * + * @return boolean + */ + public static function digit($input) + { + return (is_int($input) && $input >= 0) || ctype_digit($input); + } + + /** + * Checks whether a string is a valid number. + * + * @param string $input input string + * + * @return boolean + */ + public static function numeric($input) + { + return (bool) preg_match('/^-?+(?=.*[0-9])[0-9]*+[,\.]?+[0-9]*+$/D', $input); + } + + /** + * Checks if a string is a proper decimal format. + * + * @param string $input number to check + * @param integer $places number of decimal places + * + * @return boolean + */ + public static function decimal($input, $places = 2) + { + return (bool) preg_match('/^[+-]?[0-9]+[,\.][0-9]{' . ((int) $places) . '}$/D', $input); + } + +} diff --git a/Classes/SagepayValidator.php b/Classes/SagepayValidator.php new file mode 100644 index 0000000..740419e --- /dev/null +++ b/Classes/SagepayValidator.php @@ -0,0 +1,94 @@ +_errors[] = $validMethod[1]; + $this->_isValid = false; + break; + } + } + } + + /** + * Get validation result + * + * @return boolean + */ + public function isValid() + { + return $this->_isValid; + } + + /** + * Get validation errors as array + * + * @return string[] + */ + public function getErrors() + { + return $this->_errors; + } + +} + diff --git a/README.md b/README.md index 5fb049d..df0ec59 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -# sagepay-lib -Sagepay Library for Magenest extensions +This component is designed to provide SagePay library for Magenest's Magento 2 Sage Payment Gateway extensions diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a3f52b2 --- /dev/null +++ b/composer.json @@ -0,0 +1,17 @@ +{ + "name": "magenest/sagepay-lib", + "description": "SagePay library for Magenest extensions", + "type": "magento2-library", + "version": "1.0.0", + "license": [ + "OSL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magenest\\SagepayLib\\": "" + } + } +} diff --git a/registration.php b/registration.php new file mode 100644 index 0000000..69bc530 --- /dev/null +++ b/registration.php @@ -0,0 +1,8 @@ +