PageRenderTime 47ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/s5-members/application/default/plugins/payment/sagepay-form.php

https://bitbucket.org/awylie199/s5t
PHP | 290 lines | 165 code | 37 blank | 88 comment | 16 complexity | 3aaa14b57ce17c4c190639bf3d505eb0 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0, LGPL-3.0, MIT, BSD-3-Clause
  1. <?php
  2. /**
  3. * @table paysystems
  4. * @id sagepay-form
  5. * @title Sagepay Form
  6. * @visible_link http://www.sagepay.com/
  7. * @logo_url sagepay.png
  8. * @recurring none
  9. */
  10. class Am_Paysystem_SagepayForm extends Am_Paysystem_Abstract {
  11. const PLUGIN_STATUS = self::STATUS_BETA;
  12. const PLUGIN_REVISION = '5.1.5';
  13. protected $defaultTitle = 'Sagepay Form';
  14. protected $defaultDescription = 'Pay by credit card';
  15. const TEST_URL = "https://test.sagepay.com/gateway/service/vspform-register.vsp";
  16. const LIVE_URL = "https://live.sagepay.com/gateway/service/vspform-register.vsp";
  17. public function supportsCancelPage()
  18. {
  19. return true;
  20. }
  21. public function _initSetupForm(Am_Form_Setup $form)
  22. {
  23. $form->addText('login')->setLabel('Your SagePay login');
  24. $form->addPassword('pass')->setLabel('Your SagePay password');
  25. $form->addAdvCheckbox('testing')->setLabel("Test Mode Enabled");
  26. }
  27. public function getSupportedCurrencies()
  28. {
  29. return array('AUD', 'CAD', 'CHF', 'DKK', 'EUR', 'GBP',
  30. 'HKD', 'IDR', 'JPY', 'LUF', 'NOK', 'NZD', 'SEK', 'SGD', 'TRL', 'USD');
  31. }
  32. public function _process(Invoice $invoice, Am_Mvc_Request $request, Am_Paysystem_Result $result)
  33. {
  34. $u = $invoice->getUser();
  35. $a = new Am_Paysystem_Action_Form($this->getConfig('testing') ? self::TEST_URL : self::LIVE_URL);
  36. $a->VPSProtocol = '3.00';
  37. $a->TxType = 'PAYMENT';
  38. $a->Vendor = $this->getConfig('login');
  39. $vars = array(
  40. 'VendorTxCode='.$invoice->public_id,
  41. 'Amount='.$invoice->first_total,
  42. 'Currency='.$invoice->currency,
  43. 'Description='.$invoice->getLineDescription(),
  44. 'SuccessURL='.$this->getPluginUrl('thanks'),
  45. 'FailureURL='.$this->getCancelUrl(),
  46. 'CustomerEmail='.$u->email,
  47. 'VendorEmail='.$this->getDi()->config->get('admin_email'),
  48. 'CustomerName='.$u->name_f . ' ' . $u->name_l,
  49. );
  50. // New mandatory fields for 3.00 protocol
  51. // All mandatory fields must contain a value, apart from the BillingPostcode/DeliveryPostCode.
  52. $surname = ($u->name_l != '') ? $u->name_l : 'Surname';
  53. $firstname = ($u->name_f != '') ? $u->name_f : 'Firstname';
  54. $address = ($u->street != '') ? $u->street : 'Address';
  55. $city = ($u->city != '') ? $u->city : 'City';
  56. $country = ($u->country != '') ? $u->country : 'US';
  57. $state = ($u->state != '') ? $u->state : 'AL';
  58. $zip = ($u->zip != '') ? $u->zip : '12345';
  59. $vars[] = 'BillingSurname='.$surname;
  60. $vars[] = 'BillingFirstnames='.$firstname;
  61. $vars[] = 'BillingAddress1='.$address;
  62. $vars[] = 'BillingCity='.$city;
  63. $vars[] = 'BillingPostCode='.$zip;
  64. $vars[] = 'BillingCountry='.$country;
  65. $vars[] = 'DeliverySurname='.$surname;
  66. $vars[] = 'DeliveryFirstnames='.$firstname;
  67. $vars[] = 'DeliveryAddress1='.$address;
  68. $vars[] = 'DeliveryCity='.$city;
  69. $vars[] = 'DeliveryPostCode='.$zip;
  70. $vars[] = 'DeliveryCountry='.$country;
  71. if ($country == 'US') {
  72. //becomes mandatory when the BillingCountry/DeliveryCountry is set to US
  73. $vars[] = 'BillingState='.$state;
  74. $vars[] = 'DeliveryState='.$state;
  75. }
  76. /*
  77. * Important – if your business is classed as Financial Institution (Merchant code – 6012)
  78. * there are 4 additional fields that will need to be included with the transaction post from your system.
  79. * FIRecipientAcctNumber
  80. * FIRecipientSurname
  81. * FIRecipientPostcode
  82. * FIRecipientDoB
  83. */
  84. //$a->Crypt = base64_encode($this->sagepay_simple_xor(implode('&',$vars), $this->getConfig('pass')));
  85. $a->Crypt = self::encryptAes(implode('&',$vars), $this->getConfig('pass'));
  86. $a->filterEmpty();
  87. $result->setAction($a);
  88. }
  89. // public function sagepay_simple_xor($InString, $Key) {
  90. // // Initialise key array
  91. // $KeyList = array();
  92. // // Initialise out variable
  93. // $output = "";
  94. //
  95. // // Convert $Key into array of ASCII values
  96. // for($i = 0; $i < strlen($Key); $i++){
  97. // $KeyList[$i] = ord(substr($Key, $i, 1));
  98. // }
  99. //
  100. // // Step through string a character at a time
  101. // for($i = 0; $i < strlen($InString); $i++) {
  102. // // Get ASCII code from string, get ASCII code from key (loop through with MOD), XOR the two, get the character from the result
  103. // // % is MOD (modulus), ^ is XOR
  104. // $output.= chr(ord(substr($InString, $i, 1)) ^ ($KeyList[$i % strlen($Key)]));
  105. // }
  106. // // Return the result
  107. // return $output;
  108. // }
  109. public function createTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
  110. {
  111. }
  112. public function createThanksTransaction(Am_Mvc_Request $request, Am_Mvc_Response $response, array $invokeArgs)
  113. {
  114. return new Am_Paysystem_Transaction_SagePayForm_Thanks($this, $request, $response, $invokeArgs);
  115. }
  116. public function getRecurringType()
  117. {
  118. return self::REPORTS_NOT_RECURRING;
  119. }
  120. /**
  121. * PHP's mcrypt does not have built in PKCS5 Padding, so we use this.
  122. *
  123. * @param string $input The input string.
  124. *
  125. * @return string The string with padding.
  126. */
  127. static protected function addPKCS5Padding($input)
  128. {
  129. $blockSize = 16;
  130. $padd = "";
  131. // Pad input to an even block size boundary.
  132. $length = $blockSize - (strlen($input) % $blockSize);
  133. for ($i = 1; $i <= $length; $i++)
  134. {
  135. $padd .= chr($length);
  136. }
  137. return $input . $padd;
  138. }
  139. /**
  140. * Remove PKCS5 Padding from a string.
  141. *
  142. * @param string $input The decrypted string.
  143. *
  144. * @return string String without the padding.
  145. * @throws Am_Exception_Paysystem
  146. */
  147. static protected function removePKCS5Padding($input)
  148. {
  149. $blockSize = 16;
  150. $padChar = ord($input[strlen($input) - 1]);
  151. /* Check for PadChar is less then Block size */
  152. if ($padChar > $blockSize)
  153. {
  154. throw new Am_Exception_Paysystem('Invalid encryption string');
  155. }
  156. /* Check by padding by character mask */
  157. if (strspn($input, chr($padChar), strlen($input) - $padChar) != $padChar)
  158. {
  159. throw new Am_Exception_Paysystem('Invalid encryption string');
  160. }
  161. $unpadded = substr($input, 0, (-1) * $padChar);
  162. /* Chech result for printable characters */
  163. if (preg_match('/[[:^print:]]/', $unpadded))
  164. {
  165. throw new Am_Exception_Paysystem('Invalid encryption string');
  166. }
  167. return $unpadded;
  168. }
  169. /**
  170. * Encrypt a string ready to send to SagePay using encryption key.
  171. *
  172. * @param string $string The unencrypyted string.
  173. * @param string $key The encryption key.
  174. *
  175. * @return string The encrypted string.
  176. */
  177. static public function encryptAes($string, $key)
  178. {
  179. // AES encryption, CBC blocking with PKCS5 padding then HEX encoding.
  180. // Add PKCS5 padding to the text to be encypted.
  181. $string = self::addPKCS5Padding($string);
  182. // Perform encryption with PHP's MCRYPT module.
  183. $crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $key);
  184. // Perform hex encoding and return.
  185. return "@" . strtoupper(bin2hex($crypt));
  186. }
  187. /**
  188. * Decode a returned string from SagePay.
  189. *
  190. * @param string $strIn The encrypted String.
  191. * @param string $password The encyption password used to encrypt the string.
  192. *
  193. * @return string The unecrypted string.
  194. * @throws Am_Exception_Paysystem
  195. */
  196. static public function decryptAes($strIn, $password)
  197. {
  198. // HEX decoding then AES decryption, CBC blocking with PKCS5 padding.
  199. // Use initialization vector (IV) set from $str_encryption_password.
  200. $strInitVector = $password;
  201. // Remove the first char which is @ to flag this is AES encrypted and HEX decoding.
  202. $hex = substr($strIn, 1);
  203. // Throw exception if string is malformed
  204. if (!preg_match('/^[0-9a-fA-F]+$/', $hex))
  205. {
  206. throw new Am_Exception_Paysystem('Invalid encryption string');
  207. }
  208. $strIn = pack('H*', $hex);
  209. // Perform decryption with PHP's MCRYPT module.
  210. $string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, $strIn, MCRYPT_MODE_CBC, $strInitVector);
  211. return self::removePKCS5Padding($string);
  212. }
  213. }
  214. class Am_Paysystem_Transaction_SagePayForm_Thanks extends Am_Paysystem_Transaction_Incoming
  215. {
  216. public function __construct(Am_Paysystem_Abstract $plugin, Am_Mvc_Request $request, Am_Mvc_Response $response, $invokeArgs)
  217. {
  218. parent::__construct($plugin, $request, $response, $invokeArgs);
  219. // $s = base64_decode(str_replace(" ", "+", $request->get("Crypt",$request->get("crypt"))));
  220. // $s = $plugin->sagepay_simple_xor($s, $plugin->getConfig('pass'));
  221. $s = Am_Paysystem_SagepayForm::decryptAes($request->get("Crypt", $request->get("crypt")), $plugin->getConfig('pass'));
  222. parse_str($s, $this->vars);
  223. }
  224. public function getAmount()
  225. {
  226. return moneyRound($this->vars['Amount']);
  227. }
  228. public function getUniqId()
  229. {
  230. return $this->vars["VPSTxId"];
  231. }
  232. public function findInvoiceId()
  233. {
  234. return $this->vars["VendorTxCode"];
  235. }
  236. public function validateSource()
  237. {
  238. return true;
  239. }
  240. public function validateStatus()
  241. {
  242. return $this->vars['Status'] == 'OK';
  243. }
  244. public function validateTerms()
  245. {
  246. return true;
  247. }
  248. function getInvoice()
  249. {
  250. return $this->loadInvoice($this->findInvoiceId());
  251. }
  252. }