Pingback не был успешным. Причина: тело ответа не соответствует
ожидаемая схема: ОКПодпись базовой строки
UID = валюта = тип = 0ref = 369e67e903ca0b2261cd342575b8979eПодпись = MD5 (Подпись базовой строки)
2aa9f1c847d1492b18cd017cdf78290b
это model.donate.php
<?php
in_file();
class Mdonate{
protected $registry, $db, $config;
private $vars = array();
protected $hash_item = '';
protected $paypal_ipn_url = 'https://www.paypal.com/cgi-bin/webscr';
protected $paypal_ipn_url_ssl = 'www.paypal.com';
protected $req = 'cmd=_notify-validate';
protected $post = array();
protected $paypal_response;
public $order_details = array();
protected $pw_ip_white_list = array('174.36.92.186', '66.220.10.3', '174.36.92.186', '174.36.96.66', '174.36.92.187', '174.36.92.192', '174.37.14.28');
protected $pw_reason_list = array(0 => 'Invalid Reason',
1 => 'Chargeback',
2 => 'Credit Card fraud',
3 => 'Order fraud',
4 => 'Bad data entry',
5 => 'Fake / proxy user',
6 => 'Rejected by advertiser',
7 => 'Duplicate conversions',
8 => 'Goodwill credit taken back',
9 => 'Cancelled order',
10 => 'Partially reversed transaction');
public function __construct(){
$this->registry = registry::getInstance();
$this->db = $this->registry->db;
$this->config = $this->registry->config;
}
public function __set($key, $val){
$this->vars[$key] = $val;
}
public function __get($name){
return $this->vars[$name];
}
public function __isset($name){
return isset($this->vars[$name]);
}
public function get_paypal_packages(){
return $this->db->query('SELECT id, package, reward, price, currency FROM dmncms_donate_paypal_packages WHERE status = 1 ORDER BY orders ASC')->fetch_all();
}
public function check_package($id){
$count = $this->db->snumrows('SELECT COUNT(id) as count FROM dmncms_donate_paypal_packages WHERE id = '.$this->db->escape($id).' AND status = 1');
return ($count == 1);
}
public function insert_paypal_order($reward, $price, $currency){
$this->hash_item = md5($_SESSION['name'].$price.$currency.uniqid(microtime(),1));
$stmt = $this->db->prepare('INSERT INTO dmncms_donate_paypal_orders (amount, currency, credits, account, hash) VALUES(:amount, :currency, :credits, :account, :hash)');
return $stmt->execute(array(':amount' => $price, ':currency' => $currency, ':credits' => $reward, ':account' => $_SESSION['name'], ':hash' => $this->hash_item));
}
public function get_paypal_data(){
return array('email' => $this->config->load_xml_config('donate|pp_email'), 'item' => $this->hash_item, 'user' => $_SESSION['name']);
}
public function gen_post_fields($data){
$data_array = explode('&', $data);
foreach($data_array as $value){
$value = explode ('=', $value);
if(count($value) == 2)
$this->post[$value[0]] = urldecode($value[1]);
}
foreach($this->post as $key => $value) {
$this->req .= "&".$key."=".urlencode($value);
}
}
public function post_back_paypal_fsock(){
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Host: ".$this->paypal_ipn_url_ssl."\r\n";
$header .= "Content-Length: " . strlen($this->req) . "\r\n";
$header .= "Connection: close\r\n\r\n";
$fp = fsockopen('ssl://'.$this->paypal_ipn_url_ssl, 443, $errno, $errstr, 30);
if(!$fp){
$this->writelog('PayPal sent fsockopen error no. '.$errno.': '.$errstr.'','Paypal');
return false;
}
else{
fputs($fp, $header.$this->req);
while(!feof($fp)){
$this->paypal_response = fgets($fp, 1024);
}
fclose($fp);
}
return true;
}
public function post_back_paypal_curl(){
$request = curl_init();
curl_setopt_array($request, array(CURLOPT_URL => $this->paypal_ipn_url,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $this->req,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HTTPHEADER => array('Connection: Close'),
CURLOPT_SSL_VERIFYPEER => TRUE,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_FORBID_REUSE => TRUE,
CURLOPT_CAINFO => APP_PATH.DS.'data'.DS.'cacert.pem'));
$this->paypal_response = curl_exec($request);
if(curl_errno($request)){
$this->writelog(curl_error($request), 'Paypal');
return false;
}
curl_close($request);
return true;
}
public function validate_paypal_payment(){
if(stripos($this->paypal_response, "VERIFIED") !== false){
if(!$this->check_email()){
return false;
}
if(!$this->check_order_number()){
return false;
}
switch($this->vars['payment_status']){
case 'Completed':
if($this->vars['tax'] > 0){
$this->vars['mc_gross'] -= $this->vars['tax'];
}
if($this->vars['mc_gross'] == $this->order_details['amount']){
if($this->vars['mc_currency'] == $this->order_details['currency']){
if($this->check_completed_transaction()){
return false;
}
if($this->check_pending_transaction()){
if($this->update_transaction_status()){
return true;
}
}
else{
if($this->insert_transaction_status()){
return true;
}
}
}
}
break;
case 'Pending':
if($this->vars['tax'] > 0){
$this->vars['mc_gross'] -= $this->vars['tax'];
}
if(!$this->check_completed_transaction() && !$this->check_pending_transaction()){
$this->insert_transaction_status();
}
break;
case 'Reversed': case 'Refunded':
$this->decrease_credits($this->order_details['account'], $this->order_details['credits']);
$this->update_transaction_status();
if($this->config->load_xml_config('donate|pp_punish_player') == 1){
$this->block_user($this->order_details['account']);
}
break;
}
}
if(stripos($this->paypal_response, "INVALID") !== false){
$this->writelog('PayPal sent [status: INVALID] [transaction id: '.$this->vars['txn_id'], 'Paypal');
}
}
private function check_email(){
if(strtolower($this->vars['receiver_email']) != strtolower($this->config->load_xml_config('donate|pp_email'))){
$this->writelog('PayPal sent invalid reciever email: '.$this->vars['receiver_email'].'', 'Paypal');
return false;
}
return true;
}
private function check_order_number(){
$count = $this->db->snumrows('SELECT COUNT(id) AS count FROM dmncms_donate_paypal_orders where hash = '.$this->db->escape($this->vars['item_number']));
if($count == 1){
$this->order_details = $this->db->query('SELECT amount, currency, account, credits FROM dmncms_donate_paypal_orders where hash = '.$this->db->escape($this->vars['item_number']))->fetch();
return true;
}
else{
$this->writelog('PayPal sent invalid order [transaction id: '.$this->vars['txn_id'].']', 'Paypal');
return false;
}
}
private function check_completed_transaction(){
$count = $this->db->snumrows('SELECT COUNT(id) AS count FROM dmncms_donate_paypal_transactions where transaction_id = '.$this->db->escape($this->vars['txn_id']).' and status = \'Completed\'');
if($count > 0){
return true;
}
return false;
}
private function check_pending_transaction(){
$count = $this->db->snumrows('SELECT COUNT(id) AS count FROM dmncms_donate_paypal_transactions where transaction_id = '.$this->db->escape($this->vars['txn_id']).' and status = \'Pending\'');
if($count > 0){
return true;
}
return false;
}
private function update_transaction_status(){
$stmt = $this->db->prepare('UPDATE dmncms_donate_paypal_transactions SET status = :status WHERE transaction_id = :trans_id');
return $stmt->execute(array(':status' => $this->vars['payment_status'], ':trans_id' => $this->vars['txn_id']));
}
private function insert_transaction_status(){
$stmt = $this->db1->prepare('INSERT INTO dmncms_donate_paypal_transactions (transaction_id, amount, currency, acc, credits, order_date, status, payer_email) VALUES (:trans_id, :gross, :currency, :account, :credits, :time, :payment_status, :payer_email)');
return $stmt->execute(array(':trans_id' => $this->vars['txn_id'], ':gross' => $this->vars['mc_gross'], ':currency' => $this->vars['mc_currency'], ':account' => $this->order_details['account'], ':credits' => $this->order_details['credits'], ':time' => time(), ':payment_status' => $this->vars['payment_status'], ':payer_email' => $this->vars['payer_email']));
}
public function reward_user($acc, $credits){
$stmt = $this->db->prepare('UPDATE bg_user SET cash = cash + :credits WHERE bg_user = :account');
$stmt->execute(array(':account' => $acc, ':credits' => str_replace('-', '', $credits)));
}
private function decrease_credits($acc, $credits){
$stmt = $this->db1->prepare('UPDATE bg_user SET cash = cash - :credits WHERE bg_user = :account');
$stmt->execute(array(':credits' => str_replace('-', '', $credits), ':account' => $acc));
}
private function block_user($acc){
return;
}
public function validate_ip_list(){
return (in_array($_SERVER['REMOTE_ADDR'], $this->pw_ip_white_list));
}
public function validate_pw_signature(){
return (md5('uid='.$this->vars['uid'].'currency='.$this->vars['currency'].'type='.$this->vars['type'].'ref='.$this->vars['ref'].$this->config->load_xml_config('donate|pw_secretkey')) == $this->vars['sig']);
}
public function validate_pw_payment(){
if(!$this->check_reference()){
if($this->log_pw_transaction()){
return true;
}
}
else{
if($this->vars['type'] == 2){
$this->change_pw_transaction_status();
if($this->vars['reason'] == 2 || $this->vars['reason'] == 3){
$this->block_user($this->vars['uid']);
}
$this->decrease_credits($this->vars['uid'], $this->vars['currency']);
}
}
}
private function check_reference(){
$count = $this->db->snumrows('SELECT COUNT(uid) AS count FROM dmncms_donate_paymentwall WHERE uid = '.$this->db->escape($this->vars['uid']).' AND ref = '.$this->db->escape($this->vars['ref']).'');
if($count > 0){
return true;
}
return false;
}
private function log_pw_transaction(){
$prepare = $this->db->prepare('INSERT INTO dmncms_donate_paymentwall (uid, currency, type, ref, reason, order_date) VALUES (:uid, :currency, :type, :ref, :reason, :time)');
return $prepare->execute(array(':uid' => $this->vars['uid'], ':currency' => $this->vars['currency'], ':type' => $this->vars['type'], ':ref' => $this->vars['ref'], ':reason' => 'Complete', ':time' => time()));
}
private function change_pw_transaction_status(){
$stmt = $this->db->prepare('UPDATE dmncms_donate_paymentwall SET currency = :currency, reason = :reason, order_date = :order_date WHERE uid =:uid AND ref = :ref');
$stmt->execute(array(':currency' => $this->vars['currency'], ':reason' => $this->pw_reason_list[$this->vars['reason']], ':order_date' => time(), ':uid' => $this->vars['uid'], ':ref' => $this->vars['ref']));
}
public function writelog($logentry, $lgname) {
$log = '['.$_SERVER['REMOTE_ADDR'].'] ['.(isset($_SESSION['name']) ? $_SESSION['name'] : 'Unknown').'] '.$logentry.'';
$log_name = APP_PATH.DS.'logs'.DS.$lgname.'_'.date("m-d-y").'.txt';
$logfile = @fopen($log_name, "a+");
if($logfile){
fwrite($logfile, "[".date ("h:iA")."] $log\r\n");
fclose($logfile);
}
}
}
Это view.paymentwall.php / http://domain.com/donate/paymentwall — я использую его для адресации пингбэков
<?php
if(load::get('errors') != false){
foreach(load::get('errors') as $errors){
echo '<div class="notification-box notification-box-error">'.$errors.'</div>';
}
}
if(load::get('pw') == false || load::get('pw') == 0){
echo '<div class="notification-box notification-box-error">This donation method is disabled.</div>';
}
else{
echo '<div style="/* border: 1px dotted black; *//* -webkit-border-radius: 5px; */-moz-border-radius: 5px;/* border-radius: 5px; */margin-top: 10px; padding: 10px; height: auto; background: rgba(55, 52, 55, 1); box-shadow: 0 0 4px rgba(0,0,0,.6), 0 1px 1px rgba(0,0,0,.5), inset 0 0 0 1px rgba(255,255,255,.015), inset 0 1px 0 rgba(255,255,255,.05); -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; /* margin-left: -38px; */ z-index: 1;">
<div style="padding: 2px; text-align: center;"><iframe src="http://wallapi.com/api/ps/?key='.load::get('pw_apikey').'&uid='.$_SESSION['name'].'&widget='.load::get('pw_widget').'" width="'.load::get('pw_w_width').'" height="'.load::get('pw_w_height').'" frameborder="0"></iframe></div>
</div>';
}
?>
Для расчета подписи необходимо использовать соответствующий алгоритм и параметры в зависимости от версии, которую вы хотите использовать:
https://www.paymentwall.com/en/documentation/Signature-Calculation/2313
Если версия 2 или 3, ваши параметры должны быть отсортированы в алфавитном порядке.
Для pingback вы должны возвращать только строку «OK» с вашего сервера в случае успешного pingback / отрицательного pingback.
Когда Paymentwall отправляет Pingback, он ожидает, что ваш сервер ответит HTTP-кодом статуса 200 и тело ответа будет содержать только OK https://www.paymentwall.com/en/documentation/Virtual-Currency-API/711#http_pingback_return_value
Похоже, что в настоящее время ваш скрипт возвращает HTML-код страницы платежа в качестве ответа на Pingback Paymentwall, поэтому проблема заключается в том, что тело ответа содержит не только OK.
Я рекомендую разделить страницу оплаты и скрипт обработки pingback и переместить скрипт обработки pingback в нечто вроде domain / paymentwall-pingback
На отдельном примечании, чтобы проверить pingbacks Paymentwal проще, пожалуйста, не стесняйтесь использовать Paymentwall PHP Библиотека.
С помощью PHP-библиотеки Paymentwall проверка подписи pingback, источника pingback и параметров может быть выполнена всего несколькими строками:
require_once('/path/to/paymentwall-php/lib/paymentwall.php');
Paymentwall_Config::getInstance()->set(array(
'api_type' => Paymentwall_Config::API_VC, //OR API_GOODS or API_CART
'public_key' => 'YOUR_PUBLIC_KEY',
'private_key' => 'YOUR_PRIVATE_KEY'
));
$pingback = new Paymentwall_Pingback($_GET, $_SERVER['REMOTE_ADDR']);
if ($pingback->validate()) {
//product delivery logic
}