PayfortIntegration.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. <?php
  2. /**
  3. * @copyright Copyright PayFort 2012-2016
  4. *
  5. */
  6. class PayfortIntegration
  7. {
  8. public $gatewayHost = 'https://checkout.payfort.com/';
  9. public $gatewaySandboxHost = 'https://sbcheckout.payfort.com/';
  10. public $language = 'en';
  11. /**
  12. * @var string your Merchant Identifier account (mid)
  13. */
  14. public $merchantIdentifier = 'qRVCYhzy';
  15. /**
  16. * @var string your access code
  17. */
  18. public $accessCode = 'IvPf2BwOqHwvZ6PrE8xU';
  19. /**
  20. * @var string SHA Request passphrase
  21. */
  22. public $SHARequestPhrase = 'SGSHAIN';
  23. /**
  24. * @var string SHA Response passphrase
  25. */
  26. public $SHAResponsePhrase = 'SGSHAOUT';
  27. /**
  28. * @var string SHA Type (Hash Algorith)
  29. * expected Values ("sha1", "sha256", "sha512")
  30. */
  31. public $SHAType = 'sha256';
  32. /**
  33. * @var string command
  34. * expected Values ("AUTHORIZATION", "PURCHASE")
  35. */
  36. public $command = 'AUTHORIZATION';
  37. /**
  38. * @var decimal order amount
  39. */
  40. public $amount = 100;
  41. /**
  42. * @var string order currency
  43. */
  44. public $currency = 'USD';
  45. /**
  46. * @var string item name
  47. */
  48. public $itemName = 'Apple iPhone 6s Plus';
  49. /**
  50. * @var string you can change it to your email
  51. */
  52. public $customerEmail = 'test@test.com';
  53. /**
  54. * @var boolean for live account change it to false
  55. */
  56. public $sandboxMode = false;
  57. /**
  58. * @var string project root folder
  59. * change it if the project is not on root folder.
  60. */
  61. public $projectUrlPath = '/sg/payment/sdk/payfort';
  62. public function __construct()
  63. {
  64. }
  65. public function processRequest($paymentMethod)
  66. {
  67. if ($paymentMethod == 'cc_merchantpage' || $paymentMethod == 'cc_merchantpage2' || $paymentMethod == 'installments_merchantpage') {
  68. $merchantPageData = $this->getMerchantPageData($paymentMethod);
  69. $postData = $merchantPageData['params'];
  70. $gatewayUrl = $merchantPageData['url'];
  71. }
  72. else{
  73. $data = $this->getRedirectionData($paymentMethod);
  74. $postData = $data['params'];
  75. $gatewayUrl = $data['url'];
  76. }
  77. $form = $this->getPaymentForm($gatewayUrl, $postData);
  78. echo json_encode(array('form' => $form, 'url' => $gatewayUrl, 'params' => $postData, 'paymentMethod' => $paymentMethod));
  79. exit;
  80. }
  81. public function getRedirectionData($paymentMethod) {
  82. $merchantReference = $this->generateMerchantReference();
  83. if ($this->sandboxMode) {
  84. $gatewayUrl = $this->gatewaySandboxHost . 'FortAPI/paymentPage';
  85. }
  86. else {
  87. $gatewayUrl = $this->gatewayHost . 'FortAPI/paymentPage';
  88. }
  89. if ($paymentMethod == 'sadad') {
  90. $this->currency = 'SAR';
  91. }
  92. $postData = array(
  93. 'amount' => $this->convertFortAmount($this->amount, $this->currency),
  94. 'currency' => strtoupper($this->currency),
  95. 'merchant_identifier' => $this->merchantIdentifier,
  96. 'access_code' => $this->accessCode,
  97. 'merchant_reference' => $merchantReference,
  98. 'customer_email' => 'test@payfort.com',
  99. //'customer_name' => trim($order_info['b_firstname'].' '.$order_info['b_lastname']),
  100. 'command' => $this->command,
  101. 'language' => $this->language,
  102. 'return_url' => $this->getUrl('route.php?r=processResponse'),
  103. );
  104. if ($paymentMethod == 'sadad') {
  105. $postData['payment_option'] = 'SADAD';
  106. }
  107. elseif ($paymentMethod == 'naps') {
  108. $postData['payment_option'] = 'NAPS';
  109. $postData['order_description'] = $this->itemName;
  110. }
  111. elseif ($paymentMethod == 'installments') {
  112. $postData['installments'] = 'STANDALONE';
  113. $postData['command'] = 'PURCHASE';
  114. }
  115. $postData['signature'] = $this->calculateSignature($postData, 'request');
  116. $debugMsg = "Fort Redirect Request Parameters \n".print_r($postData, 1);
  117. $this->log($debugMsg);
  118. return array('url' => $gatewayUrl, 'params' => $postData);
  119. }
  120. public function getMerchantPageData($paymentMethod)
  121. {
  122. $merchantReference = $this->generateMerchantReference();
  123. $returnUrl = $this->getUrl('route.php?r=merchantPageReturn');
  124. if(isset($_GET['3ds']) && $_GET['3ds'] == 'no') {
  125. $returnUrl = $this->getUrl('route.php?r=merchantPageReturn&3ds=no');
  126. }
  127. $iframeParams = array(
  128. 'merchant_identifier' => $this->merchantIdentifier,
  129. 'access_code' => $this->accessCode,
  130. 'merchant_reference' => $merchantReference,
  131. 'service_command' => 'TOKENIZATION',
  132. 'language' => $this->language,
  133. 'return_url' => $returnUrl,
  134. );
  135. if($paymentMethod == 'installments_merchantpage'){
  136. $iframeParams['currency'] = strtoupper($this->currency);
  137. $iframeParams['installments'] = 'STANDALONE';
  138. $iframeParams['amount'] = $this->convertFortAmount($this->amount, $this->currency);
  139. }
  140. $iframeParams['signature'] = $this->calculateSignature($iframeParams, 'request');
  141. if ($this->sandboxMode) {
  142. $gatewayUrl = $this->gatewaySandboxHost . 'FortAPI/paymentPage';
  143. }
  144. else {
  145. $gatewayUrl = $this->gatewayHost . 'FortAPI/paymentPage';
  146. }
  147. $debugMsg = "Fort Merchant Page Request Parameters \n".print_r($iframeParams, 1);
  148. $this->log($debugMsg);
  149. return array('url' => $gatewayUrl, 'params' => $iframeParams);
  150. }
  151. public function getPaymentForm($gatewayUrl, $postData)
  152. {
  153. $form = '<form style="display:none" name="payfort_payment_form" id="payfort_payment_form" method="post" action="' . $gatewayUrl . '">';
  154. foreach ($postData as $k => $v) {
  155. $form .= '<input type="hidden" name="' . $k . '" value="' . $v . '">';
  156. }
  157. $form .= '<input type="submit" id="submit">';
  158. return $form;
  159. }
  160. public function processResponse()
  161. {
  162. $fortParams = array_merge($_GET, $_POST);
  163. $debugMsg = "Fort Redirect Response Parameters \n".print_r($fortParams, 1);
  164. $this->log($debugMsg);
  165. $reason = '';
  166. $response_code = '';
  167. $success = true;
  168. if(empty($fortParams)) {
  169. $success = false;
  170. $reason = "Invalid Response Parameters";
  171. $debugMsg = $reason;
  172. $this->log($debugMsg);
  173. }
  174. else{
  175. //validate payfort response
  176. $params = $fortParams;
  177. $responseSignature = $fortParams['signature'];
  178. $merchantReference = $params['merchant_reference'];
  179. unset($params['r']);
  180. unset($params['signature']);
  181. unset($params['integration_type']);
  182. $calculatedSignature = $this->calculateSignature($params, 'response');
  183. $success = true;
  184. $reason = '';
  185. if ($responseSignature != $calculatedSignature) {
  186. $success = false;
  187. $reason = 'Invalid signature.';
  188. $debugMsg = sprintf('Invalid Signature. Calculated Signature: %1s, Response Signature: %2s', $responseSignature, $calculatedSignature);
  189. $this->log($debugMsg);
  190. }
  191. else {
  192. $response_code = $params['response_code'];
  193. $response_message = $params['response_message'];
  194. $status = $params['status'];
  195. if (substr($response_code, 2) != '000') {
  196. $success = false;
  197. $reason = $response_message;
  198. $debugMsg = $reason;
  199. $this->log($debugMsg);
  200. }
  201. }
  202. }
  203. if(!$success) {
  204. $p = $params;
  205. $p['error_msg'] = $reason;
  206. $return_url = $this->getUrl('error.php?'.http_build_query($p));
  207. }
  208. else{
  209. $return_url = $this->getUrl('success.php?'.http_build_query($params));
  210. }
  211. echo "<html><body onLoad=\"javascript: window.top.location.href='" . $return_url . "'\"></body></html>";
  212. exit;
  213. }
  214. public function processMerchantPageResponse()
  215. {
  216. $fortParams = array_merge($_GET, $_POST);
  217. $debugMsg = "Fort Merchant Page Response Parameters \n".print_r($fortParams, 1);
  218. $this->log($debugMsg);
  219. $reason = '';
  220. $response_code = '';
  221. $success = true;
  222. if(empty($fortParams)) {
  223. $success = false;
  224. $reason = "Invalid Response Parameters";
  225. $debugMsg = $reason;
  226. $this->log($debugMsg);
  227. }
  228. else{
  229. //validate payfort response
  230. $params = $fortParams;
  231. $responseSignature = $fortParams['signature'];
  232. unset($params['r']);
  233. unset($params['signature']);
  234. unset($params['integration_type']);
  235. unset($params['3ds']);
  236. $merchantReference = $params['merchant_reference'];
  237. $calculatedSignature = $this->calculateSignature($params, 'response');
  238. $success = true;
  239. $reason = '';
  240. if ($responseSignature != $calculatedSignature) {
  241. $success = false;
  242. $reason = 'Invalid signature.';
  243. $debugMsg = sprintf('Invalid Signature. Calculated Signature: %1s, Response Signature: %2s', $responseSignature, $calculatedSignature);
  244. $this->log($debugMsg);
  245. }
  246. else {
  247. $response_code = $params['response_code'];
  248. $response_message = $params['response_message'];
  249. $status = $params['status'];
  250. if (substr($response_code, 2) != '000') {
  251. $success = false;
  252. $reason = $response_message;
  253. $debugMsg = $reason;
  254. $this->log($debugMsg);
  255. }
  256. else {
  257. $success = true;
  258. $host2HostParams = $this->merchantPageNotifyFort($fortParams);
  259. $debugMsg = "Fort Merchant Page Host2Hots Response Parameters \n".print_r($fortParams, 1);
  260. $this->log($debugMsg);
  261. if (!$host2HostParams) {
  262. $success = false;
  263. $reason = 'Invalid response parameters.';
  264. $debugMsg = $reason;
  265. $this->log($debugMsg);
  266. }
  267. else {
  268. $params = $host2HostParams;
  269. $responseSignature = $host2HostParams['signature'];
  270. $merchantReference = $params['merchant_reference'];
  271. unset($params['r']);
  272. unset($params['signature']);
  273. unset($params['integration_type']);
  274. $calculatedSignature = $this->calculateSignature($params, 'response');
  275. if ($responseSignature != $calculatedSignature) {
  276. $success = false;
  277. $reason = 'Invalid signature.';
  278. $debugMsg = sprintf('Invalid Signature. Calculated Signature: %1s, Response Signature: %2s', $responseSignature, $calculatedSignature);
  279. $this->log($debugMsg);
  280. }
  281. else {
  282. $response_code = $params['response_code'];
  283. if ($response_code == '20064' && isset($params['3ds_url'])) {
  284. $success = true;
  285. $debugMsg = 'Redirect to 3DS URL : '.$params['3ds_url'];
  286. $this->log($debugMsg);
  287. echo "<html><body onLoad=\"javascript: window.top.location.href='" . $params['3ds_url'] . "'\"></body></html>";
  288. exit;
  289. //header('location:'.$params['3ds_url']);
  290. }
  291. else {
  292. if (substr($response_code, 2) != '000') {
  293. $success = false;
  294. $reason = $host2HostParams['response_message'];
  295. $debugMsg = $reason;
  296. $this->log($debugMsg);
  297. }
  298. }
  299. }
  300. }
  301. }
  302. }
  303. if(!$success) {
  304. $p = $params;
  305. $p['error_msg'] = $reason;
  306. $return_url = $this->getUrl('error.php?'.http_build_query($p));
  307. }
  308. else{
  309. $return_url = $this->getUrl('success.php?'.http_build_query($params));
  310. }
  311. echo "<html><body onLoad=\"javascript: window.top.location.href='" . $return_url . "'\"></body></html>";
  312. exit;
  313. }
  314. }
  315. public function merchantPageNotifyFort($fortParams)
  316. {
  317. //send host to host
  318. if ($this->sandboxMode) {
  319. $gatewayUrl = $this->gatewaySandboxHost . 'FortAPI/paymentPage';
  320. }
  321. else {
  322. $gatewayUrl = $this->gatewayHost . 'FortAPI/paymentPage';
  323. }
  324. $postData = array(
  325. 'merchant_reference' => $fortParams['merchant_reference'],
  326. 'access_code' => $this->accessCode,
  327. 'command' => $this->command,
  328. 'merchant_identifier' => $this->merchantIdentifier,
  329. 'customer_ip' => $_SERVER['REMOTE_ADDR'],
  330. 'amount' => $this->convertFortAmount($this->amount, $this->currency),
  331. 'currency' => strtoupper($this->currency),
  332. 'customer_email' => $this->customerEmail,
  333. 'customer_name' => 'John Doe',
  334. 'token_name' => $fortParams['token_name'],
  335. 'language' => $this->language,
  336. 'return_url' => $this->getUrl('route.php?r=processResponse'),
  337. );
  338. if(!empty($merchantPageData['paymentMethod']) && $merchantPageData['paymentMethod'] == 'installments_merchantpage'){
  339. $postData['installments'] = 'YES';
  340. $postData['plan_code'] = $fortParams['plan_code'];
  341. $postData['issuer_code'] = $fortParams['issuer_code'];
  342. $postData['command'] = 'PURCHASE';
  343. }
  344. if(isset($fortParams['3ds']) && $fortParams['3ds'] == 'no') {
  345. $postData['check_3ds'] = 'NO';
  346. }
  347. //calculate request signature
  348. $signature = $this->calculateSignature($postData, 'request');
  349. $postData['signature'] = $signature;
  350. $debugMsg = "Fort Host2Host Request Parameters \n".print_r($postData, 1);
  351. $this->log($debugMsg);
  352. if ($this->sandboxMode) {
  353. $gatewayUrl = 'https://sbpaymentservices.payfort.com/FortAPI/paymentApi';
  354. }
  355. else {
  356. $gatewayUrl = 'https://paymentservices.payfort.com/FortAPI/paymentApi';
  357. }
  358. $array_result = $this->callApi($postData, $gatewayUrl);
  359. $debugMsg = "Fort Host2Host Response Parameters \n".print_r($array_result, 1);
  360. $this->log($debugMsg);
  361. return $array_result;
  362. }
  363. /**
  364. * Send host to host request to the Fort
  365. * @param array $postData
  366. * @param string $gatewayUrl
  367. * @return mixed
  368. */
  369. public function callApi($postData, $gatewayUrl)
  370. {
  371. //open connection
  372. $ch = curl_init();
  373. //set the url, number of POST vars, POST data
  374. $useragent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0";
  375. curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
  376. curl_setopt($ch, CURLOPT_HTTPHEADER, array(
  377. 'Content-Type: application/json;charset=UTF-8',
  378. //'Accept: application/json, application/*+json',
  379. //'Connection:keep-alive'
  380. ));
  381. curl_setopt($ch, CURLOPT_URL, $gatewayUrl);
  382. curl_setopt($ch, CURLOPT_POST, 1);
  383. curl_setopt($ch, CURLOPT_FAILONERROR, 1);
  384. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  385. curl_setopt($ch, CURLOPT_ENCODING, "compress, gzip");
  386. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  387. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  388. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // allow redirects
  389. //curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return into a variable
  390. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); // The number of seconds to wait while trying to connect
  391. //curl_setopt($ch, CURLOPT_TIMEOUT, Yii::app()->params['apiCallTimeout']); // timeout in seconds
  392. curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData));
  393. $response = curl_exec($ch);
  394. //$response_data = array();
  395. //parse_str($response, $response_data);
  396. curl_close($ch);
  397. $array_result = json_decode($response, true);
  398. if (!$response || empty($array_result)) {
  399. return false;
  400. }
  401. return $array_result;
  402. }
  403. /**
  404. * calculate fort signature
  405. * @param array $arrData
  406. * @param string $signType request or response
  407. * @return string fort signature
  408. */
  409. public function calculateSignature($arrData, $signType = 'request')
  410. {
  411. $shaString = '';
  412. ksort($arrData);
  413. foreach ($arrData as $k => $v) {
  414. $shaString .= "$k=$v";
  415. }
  416. if ($signType == 'request') {
  417. $shaString = $this->SHARequestPhrase . $shaString . $this->SHARequestPhrase;
  418. }
  419. else {
  420. $shaString = $this->SHAResponsePhrase . $shaString . $this->SHAResponsePhrase;
  421. }
  422. $signature = hash($this->SHAType, $shaString);
  423. return $signature;
  424. }
  425. /**
  426. * Convert Amount with dicemal points
  427. * @param decimal $amount
  428. * @param string $currencyCode
  429. * @return decimal
  430. */
  431. public function convertFortAmount($amount, $currencyCode)
  432. {
  433. $new_amount = 0;
  434. $total = $amount;
  435. $decimalPoints = $this->getCurrencyDecimalPoints($currencyCode);
  436. $new_amount = round($total, $decimalPoints) * (pow(10, $decimalPoints));
  437. return $new_amount;
  438. }
  439. public function castAmountFromFort($amount, $currencyCode)
  440. {
  441. $decimalPoints = $this->getCurrencyDecimalPoints($currencyCode);
  442. //return $amount / (pow(10, $decimalPoints));
  443. $new_amount = round($amount, $decimalPoints) / (pow(10, $decimalPoints));
  444. return $new_amount;
  445. }
  446. /**
  447. *
  448. * @param string $currency
  449. * @param integer
  450. */
  451. public function getCurrencyDecimalPoints($currency)
  452. {
  453. $decimalPoint = 2;
  454. $arrCurrencies = array(
  455. 'JOD' => 3,
  456. 'KWD' => 3,
  457. 'OMR' => 3,
  458. 'TND' => 3,
  459. 'BHD' => 3,
  460. 'LYD' => 3,
  461. 'IQD' => 3,
  462. );
  463. if (isset($arrCurrencies[$currency])) {
  464. $decimalPoint = $arrCurrencies[$currency];
  465. }
  466. return $decimalPoint;
  467. }
  468. public function getUrl($path)
  469. {
  470. $url = 'http://' . $_SERVER['HTTP_HOST'] . $this->projectUrlPath .'/'. $path;
  471. return $url;
  472. }
  473. public function generateMerchantReference()
  474. {
  475. return rand(0, getrandmax());
  476. }
  477. /**
  478. * Log the error on the disk
  479. */
  480. public function log($messages) {
  481. $messages = "========================================================\n\n".$messages."\n\n";
  482. $file = __DIR__.'/trace.log';
  483. if (filesize($file) > 907200) {
  484. $fp = fopen($file, "r+");
  485. ftruncate($fp, 0);
  486. fclose($fp);
  487. }
  488. $myfile = fopen($file, "a+");
  489. fwrite($myfile, $messages);
  490. fclose($myfile);
  491. }
  492. /**
  493. *
  494. * @param type $po payment option
  495. * @return string payment option name
  496. */
  497. function getPaymentOptionName($po) {
  498. switch($po) {
  499. case 'creditcard' : return 'Credit Cards';
  500. case 'cc_merchantpage' : return 'Credit Cards (Merchant Page)';
  501. case 'installments_merchantpage' : return 'Installments (Merchant Page)';
  502. case 'installments' : return 'Installments';
  503. case 'sadad' : return 'SADAD';
  504. case 'naps' : return 'NAPS';
  505. default : return '';
  506. }
  507. }
  508. }
  509. ?>