IPay88.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. <?php
  2. namespace frontendApi\modules\v1\components;
  3. use common\helpers\LoggerTool;
  4. use Exception;
  5. use Yii;
  6. class IPay88 {
  7. /**
  8. * Normal iPay88 payment method
  9. */
  10. const TRANSACTION_TYPE_PAYMENT = 'payment';
  11. /**
  12. * Normal iPay88 recurring payment subscription
  13. */
  14. const TRANSACTION_TYPE_RECURRING_SUBSCRIPTION = 'recurring_subscription';
  15. /**
  16. * Normal iPay88 recurring payment termination
  17. */
  18. const TRANSACTION_TYPE_RECURRING_TERMINATION = 'recurring_termination';
  19. /**
  20. * Merchant code assigned by iPay88
  21. */
  22. public $merchantCode;
  23. /**
  24. * Merchant Key assigned by iPay88
  25. */
  26. public $merchantKey;
  27. /**
  28. * Currency Code max length 5
  29. */
  30. public $currencyCode;
  31. /**
  32. * Merchant code assigned by iPay88
  33. */
  34. public $responseUrl;
  35. /*
  36. * Response Url or Return Url after payment
  37. */
  38. public $paymentUrl;
  39. /*
  40. * Backend Url or Notify Url after payment (Send response by iPay88 server)
  41. */
  42. public $backendUrl;
  43. /*
  44. * Requery from iPay88 server regarding bill details
  45. */
  46. public $requeryUrl;
  47. /*
  48. * ipay88 Recurring Payment Url
  49. */
  50. public $recurringUrlSubscription;
  51. /*
  52. * ipay88 Recurring Payment Termination Url
  53. */
  54. public $recurringUrlTermination;
  55. /*
  56. * Details to be sent to IPay88 for payment request.
  57. */
  58. private $paymentRequest = array(
  59. 'MerchantCode', // Merchant code assigned by iPay88. (length 20)
  60. 'PaymentId', // (Optional) (int)
  61. 'RefNo', // Unique merchant transaction number / Order ID (Retry for same RefNo only valid for 30 mins). (length 20)
  62. 'Amount', // Payment amount with two decimals.
  63. 'Currency', // (length 5)
  64. 'ProdDesc', // Product description. (length 100)
  65. 'UserName', // Customer name. (length 100)
  66. 'UserEmail', // Customer email. (length 100)
  67. 'UserContact', // Customer contact. (length 20)
  68. 'Remark', // (Optional) Merchant remarks. (length 100)
  69. 'Lang', // (Optional) Encoding type:- ISO-8859-1 (English), UTF-8 (Unicode), GB2312 (Chinese Simplified), GD18030 (Chinese Simplified), BIG5 (Chinese Traditional)
  70. 'Signature',
  71. 'ResponseURL',
  72. 'BackendURL',
  73. );
  74. /*
  75. * Details to be sent to iPay88 for recurring subscription payment request.
  76. */
  77. private $recurringSubscriptionRequest = array(
  78. 'MerchantCode', // Merchant code assigned by iPay88. (length 20)
  79. 'RefNo', // Unique merchant transaction number / Order ID. (length 20)
  80. 'FirstPaymentDate', // (ddmmyyyy)
  81. 'Currency', // MYR only. (length 5)
  82. 'Amount', // Payment amount with two decimals.
  83. 'NumberOfPayments', // (int)
  84. 'Frequency', // Frequency type; 1 - Monthly, 2 - Quarterly, 3 - Half-Yearly, 4 - Yearly. (int)
  85. 'Desc', // Product description. (length 100)
  86. 'CC_Name', // Name printed on credit card. (length 100)
  87. 'CC_PAN', // 16-digit credit card number (Visa/Mastercard). (length 16)
  88. 'CC_CVC', // 3-digit verification code behind credit card. (length 3)
  89. 'CC_ExpiryDate', // Credit card expiry date. (mmyyyy)
  90. 'CC_Country', // Credit card issuing country. (length 100)
  91. 'CC_Bank', // Credit card issuing bank. (length 100)
  92. 'CC_Ic', // Credit card holder IC / Passport number. (length 50)
  93. 'CC_Email', // Credit card holder email address. (length 255)
  94. 'CC_Phone', // Credit card phone number. (length 100)
  95. 'CC_Remark', // (Optional) Remarks. (varchar 100)
  96. 'P_Name', // Subscriber name as printed in IC / Passport. (length 100)
  97. 'P_Email', // Subscriber email address. (length 255)
  98. 'P_Phone', // Subscriber phone number. (length 100)
  99. 'P_Addrl1', // Subscriber address line 1. (length 100)
  100. 'P_Addrl2', // (Optional) Subscriber address line 2. (length 100)
  101. 'P_City', // Subscriber city. (length 100)
  102. 'P_State', // Subscriber state. (length 100)
  103. 'P_Zip', // Subscriber zip code. (length 100)
  104. 'P_Country', // Subscriber country. (varchar 100)
  105. 'BackendURL', // Payment backend response page. (length 255)
  106. 'Signature', // SHA1 signature. (length 100)
  107. );
  108. /*
  109. * Get required payment fields
  110. */
  111. public function getPaymentFields($reqParams = null, $paymentType) {
  112. $retnParams = array();
  113. try {
  114. if (isset($reqParams) && (count($reqParams) > 0)) {
  115. if (isset($paymentType) && $paymentType != "") {
  116. $paymentType = strtolower(trim($paymentType));
  117. switch ($paymentType) {
  118. case 'payment':
  119. $retnParams = $this->__getPaymentField($reqParams, $paymentType);
  120. break;
  121. case 'recurring_subscription':
  122. $retnParams = $this->__getRecurringSubscriptionField($reqParams, $paymentType);
  123. break;
  124. case 'recurring_termination':
  125. $retnParams = $this->__getRecurringTerminationField($reqParams, $paymentType);
  126. break;
  127. }
  128. } else {
  129. throw new Exception("Ipay: Payment method missing");
  130. }
  131. } else {
  132. throw new Exception("Ipay: Required Parameters missing");
  133. }
  134. } catch (Exception $e) {
  135. LoggerTool::error(['iPay88-getPaymentFields', $e->getLine(), $e->getMessage()]);
  136. }
  137. return $retnParams;
  138. }
  139. /*
  140. * Code for hex2bin
  141. */
  142. public function _hex2bin($hexSource) {
  143. $bin = '';
  144. for ($i = 0; $i < strlen($hexSource); $i = $i + 2) {
  145. $bin .= chr(hexdec(substr($hexSource, $i, 2)));
  146. }
  147. return $bin;
  148. }
  149. /*
  150. * Get payment fields for normal payment fields
  151. */
  152. public function __getPaymentField($reqParams, $paymentType) {
  153. $retnParams = array();
  154. foreach ($this->paymentRequest as $pymtKey) {
  155. if (isset($reqParams[$pymtKey])) {
  156. $retnParams[$pymtKey] = $reqParams[$pymtKey];
  157. } else {
  158. switch ($pymtKey) {
  159. case 'MerchantCode':
  160. $retnParams[$pymtKey] = $this->merchantCode;
  161. break;
  162. case 'Currency':
  163. $retnParams[$pymtKey] = $this->currencyCode;
  164. break;
  165. case 'Lang':
  166. $retnParams[$pymtKey] = 'UTF-8'; //(Optional) Encoding type:- ISO-8859-1 (English), UTF-8 (Unicode), GB2312 (Chinese Simplified), GD18030 (Chinese Simplified), BIG5 (Chinese Traditional)
  167. break;
  168. case 'Signature':
  169. $retnParams[$pymtKey] = $this->__createSignature($retnParams, $paymentType); // SHA1 signature.
  170. break;
  171. case 'ResponseURL':
  172. $retnParams[$pymtKey] = $this->responseUrl; // (Optional) Payment response page.
  173. break;
  174. case 'BackendURL':
  175. $retnParams[$pymtKey] = $this->backendUrl; // (Optional) BackendURL but should security purpose
  176. break;
  177. }
  178. }
  179. }
  180. return $retnParams;
  181. }
  182. /*
  183. * Get payment fields for recurring payment
  184. */
  185. public function __getRecurringSubscriptionField($reqParams, $paymentType) {
  186. $retnParams = array();
  187. foreach ($this->recurringSubscriptionRequest as $pymtKey) {
  188. if (isset($reqParams[$pymtKey])) {
  189. $retnParams[$pymtKey] = $reqParams[$pymtKey];
  190. } else {
  191. switch ($pymtKey) {
  192. case 'MerchantCode':
  193. $retnParams[$pymtKey] = $this->merchantCode;
  194. break;
  195. case 'Currency':
  196. $retnParams[$pymtKey] = $this->currencyCode;
  197. break;
  198. case 'Lang':
  199. $retnParams[$pymtKey] = 'UTF-8'; //(Optional) Encoding type:- ISO-8859-1 (English), UTF-8 (Unicode), GB2312 (Chinese Simplified), GD18030 (Chinese Simplified), BIG5 (Chinese Traditional)
  200. break;
  201. case 'Signature':
  202. $retnParams[$pymtKey] = $this->__createSignature($retnParams, $paymentType); // SHA1 signature.
  203. break;
  204. case 'ResponseURL':
  205. $retnParams[$pymtKey] = $this->responseUrl; // (Optional) Payment response page.
  206. break;
  207. case 'BackendURL':
  208. $retnParams[$pymtKey] = $this->backendUrl; // (Optional) BackendURL but should security purpose
  209. break;
  210. }
  211. }
  212. }
  213. return $retnParams;
  214. }
  215. /*
  216. * Get payment fields for recurring payment termination
  217. */
  218. public function __getRecurringTerminationField($reqParams, $paymentType) {
  219. $retnParams = array();
  220. foreach ($this->recurringSubscriptionRequest as $pymtKey) {
  221. if (isset($reqParams[$pymtKey])) {
  222. $retnParams[$pymtKey] = $reqParams[$pymtKey];
  223. } else {
  224. switch ($pymtKey) {
  225. case 'MerchantCode':
  226. $retnParams[$pymtKey] = $this->merchantCode;
  227. break;
  228. }
  229. }
  230. }
  231. return $retnParams;
  232. }
  233. /*
  234. * Create signature for payment
  235. */
  236. public function __createSignature($signatureParams, $paymentType) {
  237. $signature = '';
  238. if (isset($signatureParams)) {
  239. $_signatureParams = array();
  240. if ($paymentType == self::TRANSACTION_TYPE_PAYMENT) {
  241. $_signatureParams = array('MerchantCode', 'RefNo', 'Amount', 'Currency');
  242. } else if ($paymentType == self::TRANSACTION_TYPE_RECURRING_SUBSCRIPTION) {
  243. $_signatureParams = array('MerchantCode', 'RefNo', 'FirstPaymentDate', 'Currency', 'Amount', 'NumberOfPayments', 'Frequency', 'CC_PAN');
  244. } else if ($paymentType == self::TRANSACTION_TYPE_RECURRING_TERMINATION) {
  245. $_signatureParams = array('MerchantCode', 'RefNo');
  246. }
  247. foreach ($_signatureParams as $val) {
  248. if (!isset($signatureParams[$val])) {
  249. throw new Exception("Ipay: Missing required parameters for signature.");
  250. return false;
  251. }
  252. }
  253. }
  254. // Make sure the order is correct.
  255. if ($paymentType == self::TRANSACTION_TYPE_PAYMENT) {
  256. $signature .= $this->merchantKey;
  257. $signature .= $signatureParams['MerchantCode'];
  258. //$signature .= $signatureParams['PaymentId'];
  259. $signature .= $signatureParams['RefNo'];
  260. $signature .= preg_replace("/[^\d]+/", "", $signatureParams['Amount']);
  261. $signature .= $signatureParams['Currency'];
  262. } else if ($paymentType == self::TRANSACTION_TYPE_RECURRING_SUBSCRIPTION) {
  263. $signature .= $signatureParams['MerchantCode'];
  264. $signature .= $this->merchantKey;
  265. $signature .= $signatureParams['RefNo'];
  266. $signature .= $signatureParams['FirstPaymentDate'];
  267. $signature .= $signatureParams['Currency'];
  268. $signature .= $signatureParams['Amount'];
  269. $signature .= $signatureParams['NumberOfPayments'];
  270. $signature .= $signatureParams['Frequency'];
  271. $signature .= $signatureParams['CC_PAN'];
  272. } else if ($paymentType == self::TRANSACTION_TYPE_RECURRING_TERMINATION) {
  273. $signature .= $signatureParams['MerchantCode'];
  274. $signature .= $this->merchantKey;
  275. $signature .= $signatureParams['RefNo'];
  276. }
  277. // Hash the signature.
  278. //return $signature = base64_encode($this->_hex2bin(sha1($signature)));
  279. return $signature = hash('sha256', $signature);
  280. }
  281. /*
  282. * Get url for respective payment redirection url
  283. */
  284. public function getTransactionUrl($paymentType) {
  285. if ($paymentType == self::TRANSACTION_TYPE_PAYMENT) {
  286. return $this->paymentUrl;
  287. } else if ($paymentType == self::TRANSACTION_TYPE_RECURRING_SUBSCRIPTION) {
  288. return $this->recurringUrlSubscription;
  289. } else if ($paymentType == self::TRANSACTION_TYPE_RECURRING_TERMINATION) {
  290. return $this->recurringUrlTermination;
  291. }
  292. }
  293. /*
  294. * iPay88 payment signature validation
  295. */
  296. public function checkiPay88Signature($reqParams) {
  297. $status = 'fail';
  298. try {
  299. if (isset($reqParams) && count($reqParams) > 0) {
  300. $orginalKey = $this->merchantKey . $this->merchantCode;
  301. if (isset($reqParams['RefNo'])) {
  302. $orginalKey .=$reqParams['RefNo'];
  303. }
  304. if (isset($reqParams['Amount'])) {
  305. $orginalKey .=preg_replace("/[^\d]+/", "", $reqParams['Amount']);
  306. }
  307. $orginalKey .= $this->currencyCode;
  308. if (isset($reqParams['Status'])) {
  309. $orginalKey .=$reqParams['Status'];
  310. }
  311. $orginalKeyGen = base64_encode($this->_hex2bin(sha1($orginalKey)));
  312. $returnKey = $this->merchantKey;
  313. if (isset($reqParams['MerchantCode'])) {
  314. $returnKey .=$reqParams['MerchantCode'];
  315. }
  316. if (isset($reqParams['RefNo'])) {
  317. $returnKey .=$reqParams['RefNo'];
  318. }
  319. if (isset($reqParams['Amount'])) {
  320. $returnKey .=preg_replace("/[^\d]+/", "", $reqParams['Amount']);
  321. }
  322. if (isset($reqParams['Currency'])) {
  323. $returnKey .=$reqParams['Currency'];
  324. }
  325. if (isset($reqParams['Status'])) {
  326. $returnKey .=$reqParams['Status'];
  327. }
  328. $returnKeyGen = base64_encode($this->_hex2bin(sha1($returnKey)));
  329. if ($orginalKeyGen === $returnKeyGen) {
  330. $status = 'success';
  331. }
  332. } else {
  333. throw new Exception("Ipay::checkiPay88Signature: Params missing");
  334. }
  335. } catch (exception $e) {
  336. LoggerTool::error(['iPay88-checkiPay88Signature', $e->getLine(), $e->getMessage()]);
  337. }
  338. return $status;
  339. }
  340. /*
  341. * Curl hit to get bill deyails
  342. */
  343. public function requeryPayment($rawPostData) {
  344. try {
  345. $result = '';
  346. if (is_callable('curl_init')) {
  347. if (isset($rawPostData) && $rawPostData != "") {
  348. $ch = curl_init();
  349. $url = $this->requeryUrl . '?' . $rawPostData;
  350. curl_setopt($ch, CURLOPT_URL, $url);
  351. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  352. $result = curl_exec($ch);
  353. curl_close($ch);
  354. } else {
  355. throw new Exception("Ipay::requeryPayment: No request string");
  356. }
  357. } else {
  358. throw new Exception("Ipay::requeryPayment: Curl not enabled");
  359. }
  360. } catch (exception $e) {
  361. LoggerTool::error(['iPay88-requeryPayment', $e->getLine(), $e->getMessage()]);
  362. }
  363. return $result;
  364. }
  365. }