├── .gitignore ├── composer.json └── WayForPay.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wayforpay/php", 3 | "description": "Wayforpay PHP API", 4 | "license": "MIT", 5 | "require": {} 6 | } 7 | -------------------------------------------------------------------------------- /WayForPay.php: -------------------------------------------------------------------------------- 1 | _merchant_account = $merchant_account; 50 | $this->_merchant_password = $merchant_password; 51 | $this->_charset = $charset; 52 | } 53 | 54 | /** 55 | * MODE_SETTLE 56 | * 57 | * @param $fields 58 | * @return mixed 59 | */ 60 | public function settle($fields) 61 | { 62 | $this->_prepare(self::MODE_SETTLE, $fields); 63 | return $this->_query(); 64 | } 65 | 66 | /** 67 | * MODE_CHARGE 68 | * 69 | * @param $fields 70 | * @return mixed 71 | */ 72 | public function charge($fields) 73 | { 74 | $this->_prepare(self::MODE_CHARGE, $fields); 75 | return $this->_query(); 76 | } 77 | 78 | /** 79 | * MODE_REFUND 80 | * 81 | * @param $fields 82 | * @return mixed 83 | */ 84 | public function refund($fields) 85 | { 86 | $this->_prepare(self::MODE_REFUND, $fields); 87 | return $this->_query(); 88 | } 89 | 90 | /** 91 | * MODE_CHECK_STATUS 92 | * 93 | * @param $fields 94 | * @return mixed 95 | */ 96 | public function checkStatus($fields) 97 | { 98 | $this->_prepare(self::MODE_CHECK_STATUS, $fields); 99 | return $this->_query(); 100 | } 101 | 102 | /** 103 | * MODE_P2P_CREDIT 104 | * 105 | * @param $fields 106 | * @return mixed 107 | */ 108 | public function account2card($fields) 109 | { 110 | $this->_prepare(self::MODE_P2P_CREDIT, $fields); 111 | return $this->_query(); 112 | } 113 | 114 | /** 115 | * MODE_P2P_CREDIT 116 | * 117 | * @param $fields 118 | * @return mixed 119 | */ 120 | public function createInvoice($fields) 121 | { 122 | $this->_prepare(self::MODE_CREATE_INVOICE, $fields); 123 | return $this->_query(); 124 | } 125 | 126 | /** 127 | * MODE_P2P_CREDIT 128 | * 129 | * @param $fields 130 | * @return mixed 131 | */ 132 | public function account2phone($fields) 133 | { 134 | $this->_prepare(self::MODE_P2_PHONE, $fields); 135 | return $this->_query(); 136 | } 137 | 138 | /** 139 | * TRANSACTION_LIST 140 | * 141 | * @param $fields 142 | * @return mixed 143 | */ 144 | public function transactionList($fields) 145 | { 146 | $this->_prepare(self::MODE_TRANSACTION_LIST, $fields); 147 | return $this->_query(); 148 | } 149 | 150 | /** 151 | * MODE_PURCHASE 152 | * Generate html form 153 | * 154 | * @param $fields 155 | * @return string 156 | */ 157 | public function buildForm($fields) 158 | { 159 | $this->_prepare(self::MODE_PURCHASE, $fields); 160 | 161 | $form = sprintf('
', self::PURCHASE_URL); 162 | 163 | foreach ($this->_params as $key => $value) { 164 | if (is_array($value)) { 165 | foreach ($value as $field) { 166 | $form .= sprintf('', $key . '[]', htmlspecialchars($field)); 167 | } 168 | } else { 169 | $form .= sprintf('', $key, htmlspecialchars($value)); 170 | } 171 | } 172 | 173 | $form .= '
'; 174 | 175 | return $form; 176 | } 177 | 178 | /** 179 | * MODE_PURCHASE 180 | * If GET redirect is used to redirect to purchase form, i.e. 181 | * https://secure.wayforpay.com/pay/get?merchantAccount=test_merch_n1&merchantDomainName=domain.ua&merchantSignature=c6d08855677ec6beca68e292b2c3c6ae&orderReference=RG3656-1430373125&orderDate=1430373125&amount=0.16¤cy=UAH&productName=Saturn%20BUE%201.2&productPrice=0.16&productCount=1&language=RU 182 | * 183 | * @param $fields 184 | * @return string 185 | */ 186 | public function generatePurchaseUrl($fields) { 187 | $this->_prepare(self::MODE_PURCHASE, $fields); 188 | return self::PURCHASE_URL.'/get?'.http_build_query($this->_params); 189 | } 190 | 191 | /** 192 | * Return signature hash 193 | * 194 | * @param $action 195 | * @param $fields 196 | * @return mixed 197 | */ 198 | public function createSignature($action, $fields) 199 | { 200 | $this->_prepare($action, $fields); 201 | 202 | return $this->_buildSignature(); 203 | } 204 | 205 | /** 206 | * @param $action 207 | * @param array $params 208 | * @throws InvalidArgumentException 209 | */ 210 | private function _prepare($action, array $params) 211 | { 212 | $this->_action = $action; 213 | 214 | if(empty($params)){ 215 | throw new InvalidArgumentException('Arguments must be not empty'); 216 | } 217 | 218 | $this->_params = $params; 219 | $this->_params['transactionType'] = $this->_action; 220 | $this->_params['merchantAccount'] = $this->_merchant_account; 221 | $this->_params['merchantSignature'] = $this->_buildSignature(); 222 | 223 | if ($this->_action !== self::MODE_PURCHASE) $this->_params['apiVersion'] = self::API_VERSION; 224 | 225 | $this->_checkFields(); 226 | 227 | } 228 | 229 | /** 230 | * Check required fields 231 | * 232 | * @param $fields 233 | * @return bool 234 | * @throws InvalidArgumentException 235 | */ 236 | private function _checkFields() 237 | { 238 | $required = $this->_getRequiredFields(); 239 | $error = array(); 240 | 241 | foreach ($required as $item) { 242 | if (array_key_exists($item, $this->_params)) { 243 | if (empty($this->_params[$item])) { 244 | $error[] = $item; 245 | } 246 | } else { 247 | $error[] = $item; 248 | } 249 | } 250 | 251 | if (!empty($error)) { 252 | throw new InvalidArgumentException('Missed required field(s): ' . implode(', ', $error) . '.'); 253 | } 254 | 255 | return true; 256 | } 257 | 258 | /** 259 | * Generate signature hash 260 | * 261 | * @param $fields 262 | * @return string 263 | * @throws InvalidArgumentException 264 | */ 265 | private function _buildSignature() 266 | { 267 | $signFields = $this->_getFieldsNameForSignature(); 268 | $data = array(); 269 | $error = array(); 270 | 271 | foreach ($signFields as $item) { 272 | if (array_key_exists($item, $this->_params)) { 273 | $value = $this->_params[$item]; 274 | if (is_array($value)) { 275 | $data[] = implode(self::FIELDS_DELIMITER, $value); 276 | } else { 277 | $data[] = (string) $value; 278 | } 279 | } else { 280 | $error[] = $item; 281 | } 282 | } 283 | 284 | if ( $this->_charset != self::DEFAULT_CHARSET) { 285 | foreach($data as $key => $value) { 286 | $data[$key] = iconv($this->_charset, self::DEFAULT_CHARSET, $data[$key]); 287 | } 288 | } 289 | 290 | if (!empty($error)) { 291 | throw new InvalidArgumentException('Missed signature field(s): ' . implode(', ', $error) . '.'); 292 | } 293 | 294 | return hash_hmac('md5', implode(self::FIELDS_DELIMITER, $data), $this->_merchant_password); 295 | } 296 | 297 | /** 298 | * Request method 299 | * @return mixed 300 | */ 301 | private function _query() 302 | { 303 | $fields = json_encode($this->_params); 304 | 305 | $ch = curl_init(); 306 | curl_setopt($ch, CURLOPT_URL, self::API_URL); 307 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 308 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json;charset=utf-8')); 309 | curl_setopt($ch, CURLOPT_POST, 1); 310 | curl_setopt($ch, CURLOPT_POSTFIELDS,$fields); 311 | curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); 312 | $response = curl_exec($ch); 313 | curl_close($ch); 314 | 315 | return json_decode($response, true); 316 | } 317 | 318 | 319 | /** 320 | * Signature fields 321 | * 322 | * @return array 323 | * @throws InvalidArgumentException 324 | */ 325 | private function _getFieldsNameForSignature() 326 | { 327 | $purchaseFieldsAlias = array( 328 | 'merchantAccount', 329 | 'merchantDomainName', 330 | 'orderReference', 331 | 'orderDate', 332 | 'amount', 333 | 'currency', 334 | 'productName', 335 | 'productCount', 336 | 'productPrice' 337 | ); 338 | 339 | switch ($this->_action) { 340 | case 'PURCHASE': 341 | return $purchaseFieldsAlias; 342 | break; 343 | case 'REFUND': 344 | return array( 345 | 'merchantAccount', 346 | 'orderReference', 347 | 'amount', 348 | 'currency' 349 | ); 350 | case 'CHECK_STATUS': 351 | return array( 352 | 'merchantAccount', 353 | 'orderReference' 354 | ); 355 | break; 356 | case 'CHARGE': 357 | return $purchaseFieldsAlias; 358 | break; 359 | case 'SETTLE': 360 | return array( 361 | 'merchantAccount', 362 | 'orderReference', 363 | 'amount', 364 | 'currency' 365 | ); 366 | break; 367 | case self::MODE_P2P_CREDIT: 368 | return array( 369 | 'merchantAccount', 370 | 'orderReference', 371 | 'amount', 372 | 'currency', 373 | 'cardBeneficiary', 374 | 'rec2Token', 375 | ); 376 | break; 377 | case self::MODE_CREATE_INVOICE: 378 | return $purchaseFieldsAlias; 379 | break; 380 | case self::MODE_P2_PHONE: 381 | return array( 382 | 'merchantAccount', 383 | 'orderReference', 384 | 'amount', 385 | 'currency', 386 | 'phone', 387 | ); 388 | break; 389 | case self::MODE_TRANSACTION_LIST: 390 | return array( 391 | 'merchantAccount', 392 | 'dateBegin', 393 | 'dateEnd', 394 | ); 395 | break; 396 | default: 397 | throw new InvalidArgumentException('Unknown transaction type: '.$this->_action); 398 | } 399 | } 400 | 401 | /** 402 | * Required fields 403 | * 404 | * @return array 405 | */ 406 | private function _getRequiredFields() 407 | { 408 | switch ($this->_action) { 409 | case 'PURCHASE': 410 | return array( 411 | 'merchantAccount', 412 | 'merchantDomainName', 413 | 'merchantTransactionSecureType', 414 | 'orderReference', 415 | 'orderDate', 416 | 'amount', 417 | 'currency', 418 | 'productName', 419 | 'productCount', 420 | 'productPrice' 421 | ); 422 | case 'SETTLE': 423 | return array( 424 | 'transactionType', 425 | 'merchantAccount', 426 | 'orderReference', 427 | 'amount', 428 | 'currency', 429 | 'apiVersion' 430 | ); 431 | case 'CHARGE': 432 | $required = array( 433 | 'transactionType', 434 | 'merchantAccount', 435 | 'merchantDomainName', 436 | 'orderReference', 437 | 'apiVersion', 438 | 'orderDate', 439 | 'amount', 440 | 'currency', 441 | 'productName', 442 | 'productCount', 443 | 'productPrice', 444 | 'clientFirstName', 445 | 'clientLastName', 446 | 'clientEmail', 447 | 'clientPhone', 448 | 'clientCountry', 449 | 'clientIpAddress' 450 | ); 451 | 452 | $additional = !empty($this->_params['recToken']) ? 453 | array('recToken') : 454 | array('card', 'expMonth', 'expYear', 'cardCvv', 'cardHolder'); 455 | 456 | return array_merge($required, $additional); 457 | case 'REFUND': 458 | return array( 459 | 'transactionType', 460 | 'merchantAccount', 461 | 'orderReference', 462 | 'amount', 463 | 'currency', 464 | 'comment', 465 | 'apiVersion' 466 | ); 467 | case 'CHECK_STATUS': 468 | return array( 469 | 'transactionType', 470 | 'merchantAccount', 471 | 'orderReference', 472 | 'apiVersion' 473 | ); 474 | case self::MODE_P2P_CREDIT: 475 | return array( 476 | 'transactionType', 477 | 'merchantAccount', 478 | 'orderReference', 479 | 'amount', 480 | 'currency', 481 | 'cardBeneficiary', 482 | 'merchantSignature', 483 | ); 484 | case self::MODE_CREATE_INVOICE: 485 | return array( 486 | 'transactionType', 487 | 'merchantAccount', 488 | 'merchantDomainName', 489 | 'orderReference', 490 | 'amount', 491 | 'currency', 492 | 'productName', 493 | 'productCount', 494 | 'productPrice', 495 | ); 496 | case self::MODE_P2_PHONE: 497 | return array( 498 | 'merchantAccount', 499 | 'orderReference', 500 | 'orderDate', 501 | 'currency', 502 | 'amount', 503 | 'phone', 504 | ); 505 | break; 506 | case self::MODE_TRANSACTION_LIST: 507 | return array( 508 | 'merchantAccount', 509 | 'dateBegin', 510 | 'dateEnd', 511 | ); 512 | break; 513 | default: 514 | throw new InvalidArgumentException('Unknown transaction type'); 515 | } 516 | } 517 | 518 | /** 519 | * @param array $fields Widget(https://wiki.wayforpay.com/pages/viewpage.action?pageId=852091) 520 | * @param null $callbackFunction JavaScript callback function called on widget response 521 | * @return string 522 | */ 523 | public function buildWidgetButton(array $fields, $callbackFunction = null) 524 | { 525 | $this->_prepare(self::MODE_PURCHASE, $fields); 526 | 527 | $button = ' 528 | 546 | '; 547 | 548 | return $button; 549 | } 550 | } 551 | 552 | --------------------------------------------------------------------------------