Skip to Content
 Русский Русский    English English   

 

Разработка ПО для приема платежей в системе CyberPlat и интеграция с АСР Fastcom

Разработка ПО для приема платежей в системе CyberPlat и интеграция с АСР Fastcom

Необходимость в разработке шлюза между платежной системой "Киберплат" и АСР Fastcom возникла в далеком 2008 году. На тот момент уже было реализовано ПО для взаимодействия с системами FreeCash и Assist. Полученный при реализации платежных шлюзов опыт позволил выбрать архитектуру, снижающую нагрузку на сервер биллинга Fastcom, ядром которого является СУБД Oracle, и повысить безопасность инфраструктуры приема платежей. Все платежные шлюзы были размещены на отдельном сервере под управлением ОС FreeBSD, в качестве web-сервера был выбран Apache 2.2 + suEXEC. Для обеспечения подключений к СУБД Oracle должна быть установлена библиотека OCI8. Платежный шлюз реализовала на PHP с использованием модуля расширения php_oci8.

ОАО «Киберплат» осуществляет организацию приема платежей в режиме on-line. Схема приема платежей:

Схема приема платежей в системе CyberPlat

Изначальная реализация шлюза предусматривала возможность проведения платежей за услуги доступа в интернет и кабельное телевидение, однако в 2011 году добавились требования по приему авансовых платежей новых абонентов без проверки номера договора (договор в процессе оформления) и поддержкой новой системы нумерации договоров с сохранением старой нумерации.

Итак, в соответствии с требованиями технического задания провайдера необходимо было обеспечить поддержку следующих типов платежей:

Тип 0. Название: «Авансовый платеж новых абонентов».

Данные для предварительной проверки: в поле additional передается ФИО плательщика, номер договора не обязателен, минимальная сумма платежа - 10 рублей, максимальная сумма платежа - 10000 рублей.

Тип 1. Название: «Интернет».

Данные для предварительной проверки: допустимый набор символов - 0123456789, минимальное число символов - 8, максимальное количество символов - 11, префиксов символов нет, весь диапазон разрешен, минимальная сумма платежа - 10 рублей, максимальная сумма платежа - 10000 рублей.

Тип 2. Название: «Кабельное телевидение».

Данные для предварительной проверки: допустимый набор символов - 0123456789, минимальное число символов - 8, максимальное количество символов - 11, префиксов символов нет, весь диапазон разрешен, минимальная сумма платежа - 10 рублей, максимальная сумма платежа - 10000 рублей.

По каждому типу платежа должна быть реализована возможность поиска абонента, проверка состояния платежа и занесение платежа в базу данных Fastcom.

Справка по операторам, обслуживаемым в системе CyberPlat, содержит подробную информацию о параметрах платежей каждого оператора в соответствии с протоколом взаимодействия CyberPlat.

В 2008 году аутентификация платежной системы могла осуществляться по выбору: базовая HTTP аутентификация, с использованием клиентского SSL сертификата X.509, однако впоследствии аутентификация с использованием сертификата стала обязательным условием подключения к системе Киберплат.

Скрипт для приема платежей в системе Киберплат - cyberplat.php:

<?php
/*
##################################################################
Скрипт cyberplat.php - пример приёма платежей для взаимодействия с
платежной системой «CyberPlat». 
##################################################################
*/


PutEnv("ORACLE_SID=fst");
PutEnv("NLS_LANG=AMERICAN_AMERICA.CL8MSWIN1251");
PutEnv("ORACLE_HOME=/usr/local/oracle8-client/");
PutEnv("TNS_ADMIN=/www/oracle/");
$DATE="";

//Реквизиты платежной системы Cyberplat в биллинге Фастком
$auth_username=""; 
$auth_password=""; 

/////////////////////////////////////////////////////////////////////
/* 
function Answer - ответы на запросы, возвращаются в виде XML-сообщений. 
*/

function Answer($status, $message){
	GLOBAL $PAYMENT_ID;
	GLOBAL $DATE;
	GLOBAL $ACTION;
	$replacement = "$1-$2-$3T$4:$5:$6";
	$pay_d= preg_replace('/^(20\d{2})-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01]) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/', $replacement, $DATE);

	if ($status == 0 and ($ACTION == 'payment' or $ACTION == 'cancel' or $ACTION == 'status')) 
	{
       		$string =<<<END
<?xml version="1.0" encoding="windows-1251"?>

<response>
<code>$status</code>
<authcode>$PAYMENT_ID</authcode>
<date>$pay_d</date>
<message>$message</message>
</response>
END;
	}elseif(($ACTION == 'check' and $status == 0) or ($status != 0)){
		$string =<<<END
<?xml version="1.0" encoding="windows-1251"?>

<response>
<code>$status</code>
<message>$message</message>
</response>
END;
	}
	print "$string";
	exit();
}
/////////////////////////////////////////////////////////////////////
/* 
function Check_contract - проверка возможности проведения платежа по номеру абонента и сумме платежа 
*/

function Check_contract($cid, $summ_pay, $type, $con){ 
	GLOBAL $PAY_MAXSUM;
	GLOBAL $PAY_CONTRACT; 
	GLOBAL $CLIENT_ID;
	$PAY_CONTRACT = "";
	$CLIENT_ID = "";

	if (($summ_pay < 10) or ($summ_pay > $PAY_MAXSUM)){
		Answer("3","");
	}
		
	$query = "SELECT cc.* FROM ct_t_contract cc where contract_no='$cid'";
	$stid = oci_parse($con, $query);
	if (!$stid){
		Answer("-3","");
	}	

	oci_define_by_name($stid, "ID", $f_id);
	oci_define_by_name($stid, "CLIENT_ID", $cl_id);
	$r = oci_execute($stid);
	if (!$r) {
		Answer("-3","");
	}
	if (oci_fetch($stid)) {
		$PAY_CONTRACT=$f_id;
		$CLIENT_ID=$cl_id;
		oci_free_statement($stid);
		return 1;
	} else {
		oci_free_statement($stid);
		Answer("2","");
	}	
}
/////////////////////////////////////////////////////////////////////
/* 
function Check_pay - проверка наличия платежа в биллинге 
*/

function Check_pay($p_id, $con){ 
	GLOBAL $PAYMENT_ID;
	GLOBAL $DATE;
	$PAYMENT_ID="";
	$query = "SELECT ID, TO_CHAR(PAY_DATE,'YYYY-MM-DD HH24:MI:SS') PAY_DATE FROM pm_t_payment pm WHERE pm.payment_no = '$p_id'";
	$stid = oci_parse($con, $query);
	if (!$stid){
		Answer("-3","");
	}
	oci_define_by_name($stid, "ID", $PAYMENT_ID);
	oci_define_by_name($stid, "PAY_DATE", $pay_date);
	$r = oci_execute($stid);
	if (!$r) {
		Answer("-3","");
	}
	if (oci_fetch($stid)) {
		oci_free_statement($stid);
		$DATE=$pay_date;
		return 1;
	}else{
		oci_free_statement($stid);
		return 0;
	}
}

/////////////////////////////////////////////////////////////////////
/* 
function GetValue - поиск данных объекта договора в биллинге Фастком 
*/

function GetValue($objid, $objtype, $propname, $con){
	$query = "SELECT v.value
      		FROM ct_t_value v, ct_t_property p
     		where v.objprop_id = p.id
       		and p.code = '$propname'
       		and p.objecttype_code = '$objtype' 
       		and v.controbj_id = '$objid'"; 

	$stid = oci_parse($con, $query);
	if (!$stid){
		Answer("-3","");
	}
	oci_define_by_name($stid, "VALUE", $val);
	$r = oci_execute($stid);
	if (!$r) {
		Answer("-3","");
	}
	oci_fetch($stid);
	oci_free_statement($stid); 
	return $val;
}
/////////////////////////////////////////////////////////////////////
/* 
function Аuthenticate - аутентификация платежной системы Cyberplat в биллинге 
и поиск значений, необходимых для занесения платежа в базу данных 
*/

function Аuthenticate($con,$username,$password){
	GLOBAL $CONTRACT_NO; //Идентификатор платежной системы Cyberplat в биллинге
	GLOBAL $ENTERPRISE_ID; 
	GLOBAL $PAY_MAXSUM; //Максимальная сумма платежа в биллинге
	GLOBAL $PAY_FORM;
	GLOBAL $CODE;
	GLOBAL $CURRENCY_CODE; 
	$CONTRACT_NO="";
	$ENTERPRISE_ID="";
	$PAY_FORM="";
	$CODE="";
	$CURRENCY_CODE="";

	$query = "SELECT v.controbj_id  
    			from ct_t_value v, ct_t_property p
   			WHERE 
			v.value = '$username' and
     			 v.objprop_id = p.id
     			and p.code = 'USERNAME' 
     			and p.objecttype_code = 'PAY_RECEIVE'";
	$stid = oci_parse($con, $query);
	if (!$stid){
		Answer("-3","");
	}
	oci_define_by_name($stid, "CONTROBJ_ID", $c_id);
	$r = oci_execute($stid);
	if (!$r) {
		Answer("-3","");
	}
	if (!oci_fetch($stid)) {
		Answer("-3","");
	}
	oci_free_statement($stid);
	if (GetValue($c_id,'PAY_RECEIVE','PASSWORD',$con) <> $password){
		Answer("10","Unauthorized");		
	} else {
		$query_ct = "SELECT ct.*
    				FROM CT_T_CONTRACT ct, CT_T_OBJECT ob
   				WHERE ct.id = ob.contract_id
     				AND ob.id = '$c_id'";
		$stid_ct = oci_parse($con, $query_ct);
		if (!$stid_ct){
				Answer("-3","");
		}
		oci_define_by_name($stid_ct, "CONTRACT_NO", $c_no);
		oci_define_by_name($stid_ct, "ENTERPRISE_ID", $c_enterprise);
		$r_ct = oci_execute($stid_ct);
		if (!$r_ct) {
				Answer("-3","");
		}
		while (oci_fetch($stid_ct)) {
				$CONTRACT_NO=$c_no;
				$ENTERPRISE_ID=$c_enterprise;
		}
	}
	oci_free_statement($stid_ct);

	$PAY_MAXSUM=GetValue($c_id,'PAY_RECEIVE','MAX_SUMMA',$con);
	if(empty($PAY_MAXSUM)){
		$PAY_MAXSUM=10000;
	}
		
	$paydoccode=GetValue($c_id,'PAY_RECEIVE','PAY_DOCUMENT',$con);
		
	$query_paydoc = "SELECT * FROM RF_T_PAYDOCTYPE WHERE code='$paydoccode'";
	$stid_paydoc = oci_parse($con, $query_paydoc);
	if (!$stid_paydoc){
		Answer("-3","");
	}
	oci_define_by_name($stid_paydoc, "PAY_FORM", $pay_form);
	oci_define_by_name($stid_paydoc, "CODE", $code);
	oci_define_by_name($stid_paydoc, "CURRENCY_CODE", $currency_code);
	$r_paydoc = oci_execute($stid_paydoc);
	if (!$r_paydoc) {
		Answer("-3","");
	}
	while (oci_fetch($stid_paydoc)) {
		$PAY_FORM=$pay_form;
		$CODE=$code;
		$CURRENCY_CODE=$currency_code;
	}
	oci_free_statement($stid_paydoc);
	return 1;	
}
/////////////////////////////////////////////////////////////////////
/* 
function Payment - проведение платежа в базе данных Fastcom.

$p_no - идентификатор платежа в базе данных АСР "Фастком".
$pay_summ - сумма платежа.
$p_add - номер договора абонента или ФИО для авансовых платежей.
$con - идентификатор соединения к базе данных oracle.
$type - тип платежа (0 - авансовый платеж новых абонентов, 1 - платеж за интернет, 
  2 - платеж за кабельное телевидение).
*/

function Payment($p_no, $pay_summ, $p_add, $con,$type) { 
	GLOBAL $CODE;
	GLOBAL $CLIENT_ID;
	GLOBAL $PAY_CONTRACT;
	GLOBAL $ENTERPRISE_ID;
	GLOBAL $CURRENCY_CODE;
	GLOBAL $PAY_FORM;
	GLOBAL $PAYMENT_ID; //id вставляемой записи в базу данных Фастком
	GLOBAL $DATE;
	$PAYMENT_ID="";

	$query_s = "select PM_Q_PAYMENT.NEXTVAL from dual";
	$stid = oci_parse($con, $query_s);
	if (!$stid){
		Answer("-3","");
	}
	oci_define_by_name($stid, "NEXTVAL", $PAYMENT_ID);
	$ex_s = oci_execute($stid);
	if (!$ex_s) {
		Answer("-3","");
	}
	if (!oci_fetch($stid)) {
		Answer("-3","");
	}
	oci_free_statement($stid);

	$query_i = oci_parse($con, "INSERT INTO PM_T_PAYMENT (ID,
                             PAYMENT_NO,
                             PAY_DATE,
                             PAYFORM,
                             REST,
                             SUMMA,
                             YEARMONTH,
                             PAYTYPE_CODE,
                             CLIENT_ID,
                             CONTRACT_ID,
                             IS_PROCEED,
                             FIRST_PROCEED_SUMMA,
                             OWNER_ID,
                             WHO_INPUT,
                             REST4ADV,
                             IS_NEED_ACCEPT,
                             CURRENCY_CODE,
                             COMPUTE_RATE_ODDS,
                             DIRECTION,
                             NOTE)
     VALUES ($PAYMENT_ID,
        '$p_no',
        to_date('$DATE', 'YYYY-MM-DD HH24:MI:SS'), 
        '$PAY_FORM',
        $pay_summ,
        $pay_summ,
        TRUNC(to_date('$DATE', 'YYYY-MM-DD HH24:MI:SS'),'MONTH'),
        '$CODE',
       	'$CLIENT_ID',
        '$PAY_CONTRACT',
        'N',
        $pay_summ,
       	$ENTERPRISE_ID,
        'USER',
        $pay_summ,
        'Y',
        '$CURRENCY_CODE',
        'N',
       	'In',
        '$p_add')");

        if (!$query_i){
                Answer("-3","");
        }	
        $ex_i=oci_execute($query_i);
        if (!$ex_i){
                Answer("-3","");
        }else {
		if($type<>0){
			$query_proc = "begin bh_p_operations.pay($PAYMENT_ID); end;";  //подтверждение платежа (расход)
			$stid_proc = oci_parse($con, $query_proc);
			$ex_proc = oci_execute($stid_proc);
			oci_free_statement($stid_proc);
		}
		Answer("0","");
        }
}

/////////////////////////////////////////////////////////////////////
/* 
function Secure_Number - проверка на принадлежность числам 
*/

function Secure_Number($str){ 
	if (preg_match("/^[0-9]+$/", $str) and (strlen($str) <= 11)){
		return 1;
	} else return 0;
}

/////////////////////////////////////////////////////////////////////
/*
function Secure_Num_Text - проверка наличия текста и чисел 
*/

function Secure_Num_Text($str){
	if (preg_match("/^[a-zA-Z0-9]+$/", $str) and (strlen($str) <= 20)){
		return 1;
	} else return 0;
}
/////////////////////////////////////////////////////////////////////
/*
function Secure_Amount - проверка суммы платежа 
*/

function Secure_Amount($str){ 
	if (preg_match("/^[\d]+(\.\d{1,2})*$/", $str)){
		return 1;
	} else return 0;
}
///////////////////////////////////////////////////////////////////// 
/* 
function Secure_Number_Pay - проверка номера платежа 
*/

function Secure_Number_Pay($str){
	if (preg_match("/^[0-9]+$/", $str)){
		return 1;
	} else return 0;
}
/////////////////////////////////////////////////////////////////////
/* 
function Secure_Additional - проверка ФИО в поле additional 
*/

function Secure_Additional($str){ 
	if (preg_match("/^[0-9\-\sА-Яа-я]+$/", $str) and (strlen($str) <= 60)){
		return 1;
	} else return 0;
}
/////////////////////////////////////////////////////////////////////

$ACTION = $_GET['action']; //Тип запроса: payment, status, check, cancel
$con=oci_connect("username", "password", "fst"); //Соединение с oracle
if (!$con) {
       Answer("-3","");
}

/* Базовая аутентификация Apache

$auth_username="";
$auth_password="";
if(!isset($_SERVER['PHP_AUTH_USER'])){
        Header("WWW-Authenticate: Basic realm=\"Payment gate\"");
        Header("HTTP/1.0 401 Unauthorized");
        exit();
} elseif (!isset($_SERVER['PHP_AUTH_PW'])) {
        Header("WWW-Authenticate: Basic realm=\"Payment gate\"");
        Header("HTTP/1.0 401 Unauthorized");
        exit();
} else {
        $auth_username=$_SERVER['PHP_AUTH_USER'];
        $auth_password =$_SERVER['PHP_AUTH_PW'];
}

if(!Secure_Num_Text($auth_username)){
	Answer("10","Unauthorized");
}

if(!Secure_Num_Text($auth_password)){
        Answer("10","Unauthorized");
}

*/

Аuthenticate($con,$auth_username,$auth_password);

if ($ACTION == "payment"){ //Проведение платежа, занесение в базу данных АСР "Фастком"
	$type = $_GET['type']; //Тип платежа
	$date_payment =urldecode($_GET['date']); //Дата и время платежа
	if (preg_match('/^(20\d{2})-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])T([0-9]{2}):([0-9]{2}):([0-9]{2})$/', $date_payment))
	{
		$replacement = "$1-$2-$3 $4:$5:$6";
		$DATE= preg_replace('/^(20\d{2})-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])T([0-9]{2}):([0-9]{2}):([0-9]{2})$/', $replacement, $date_payment);
	}else { Answer('5','');}

	$pay_id = $_GET['receipt']; //Номер платежа
	if(!Secure_Number_Pay($pay_id)){
		Answer("4","");
	}

	$pay_amount = $_GET['amount']; //Сумма платежа
	if(!Secure_Amount($pay_amount)){
		Answer("3","");
	}
	$payment_no="cyberplat".$pay_id."@".$CONTRACT_NO; //Идентификатор платежа в базе данных АСР "Фастком"

	if (($type == 1) or ($type == 2)){//платеж за интернет или кабельное телевидение
		$account = $_GET['number']; //Номер абонента
		if(!Secure_Number($account)){
			Answer("2","");
		}

		if (Check_contract($account,$pay_amount,$type,$con)){
			if (!Check_pay($payment_no, $con)){//проверка наличия в базе данных платежа
				Payment($payment_no, $pay_amount, $account, $con,$type);//занесение платежа в базу данных Фастком 
			}else {
				Answer("0","");//платеж успешно проведен
			} 
		}
	}elseif($type == 0){ //авансовый платеж новых абонентов
		$additional = nl2br(htmlspecialchars(urldecode($_GET['additional'])));   //FIO
		if(!Secure_Additional($additional)){
			Answer("-1","");
		}
		setlocale (LC_CTYPE, array ('ru_RU.CP1251', 'rus_RUS.1251'));
		$additional_u=strtoupper($additional);
		$PAY_CONTRACT = "";
		$CLIENT_ID = "";
		if (!Check_pay($payment_no, $con)){//проверка наличия в базе данных платежа
		Payment($payment_no, $pay_amount, $additional_u, $con,$type);//занесение платежа в базу данных Фастком 
		} else {
			Answer("0","");//платеж успешно проведен
		}
	}else  Answer("-2",""); //Неверное значение типа платежа
} elseif ($ACTION == "cancel"){ //Удаление платежа. Не реализовано.
	$pay_id = $_GET['receipt']; //Номер платежа	
	if(!Secure_Number_Pay($pay_id)){
		Answer("4","");
	}
	Answer("9","");

} elseif ($ACTION == "status"){ //Проверка статуса платежа (поиск платежа)
	$pay_id = $_GET['receipt']; //Номер платежа
	if(!Secure_Number_Pay($pay_id)){
		Answer("4","");
	}
	$payment_no="cyberplat".$pay_id."@".$CONTRACT_NO;
	if (Check_pay($payment_no, $con)){ //Проверка наличия в базе данных платежа
		Answer("0","");
	} Answer("6","");
} elseif ($ACTION == "check") { //Проверка номера абонента и суммы платежа
	$type = $_GET['type']; //Тип платежа
	$pay_amount = $_GET['amount']; //Сумма платежа
	if(!Secure_Amount($pay_amount)){
		Answer("3","");
	}
        
	if (($type == 1) or ($type == 2)){ //Платеж за интернет или кабельное телевидение
		$account = $_GET['number']; //Номер абонента
		if(!Secure_Number($account)){
			Answer("2","");
		}

		if (Check_contract($account,$pay_amount,$type,$con)){
			Answer("0","");
		}
	}elseif($type == 0){ //Авансовый платеж новых абонентов. Так как абонент при проверке еще не занесен в базу данных, то результат проверки - всегда успех
		$additional = nl2br(htmlspecialchars(urldecode($_GET['additional'])));   //ФИО нового абонента, у которого договор в процессе оформления 
		if(!Secure_Additional($additional)){
			Answer("2","");
		}
		Answer("0","");
	}else Answer("-2",""); //Неверное значение типа платежа
} else { //Неверный тип запроса 
	Answer("1","");
}

$committed = oci_commit($con);
oci_close($con);  
?>

Список расшифровок ответов для некоторых кодов возврата:

-3   -   внутренняя ошибка провайдера услуг;

-2   -    неверное значение типа платежа (type);

-1   -  неверный формат дополнительного параметра (additional);

0   -  нет ошибки, операция прошла успешно;

1   -  неизвестный тип запроса (action);

2   -  абонент не найден;

3   -  неверная сумма платежа;

4   -  неверное значение номера платежа;

5   -  неверное значение даты;

6   -  платеж не найден;

9   -  платеж не может быть отменен;

10   -  прочие ошибки (в поле message содержится описание ошибки).

 

Ваша оценка: Нет Средняя: 4.6 (26 голосов)

Нажимая кнопку «Сохранить», я подтверждаю свою дееспособность, согласие на получение информации от NetK, согласие на обработку персональных данных в соответствии с Политикой конфиденциальности и Пользовательским соглашением.