Revision 564 (by ahitrov, 2016/04/12 21:46:19) Auto-refresh order status and history

package payments::Provider::PayTure;

use strict;
use warnings 'all';

use base 'payments::Provider::Base';
use Contenido::Globals;
use payments::Keeper;
use Digest::MD5;
use Data::Dumper;
use URI;
use URI::QueryParam;
use XML::Fast;

our $init_url = '';

sub new {
    my ($proto, %params) = @_;
    my $class = ref($proto) || $proto;
    my $self = {};
    my $prefix = $class =~ /\:\:(\w+)$/ ? lc($1) : undef;
    return      unless $prefix;

    $self->{payment_system} = $prefix;
    $self->{app_id} =           $state->{payments}->{$prefix."_app_id"};
    $self->{secret} =           $state->{payments}->{$prefix."_app_secret"};
    $self->{currency} =         $state->{payments}->{$prefix."_currency_code"};
    $self->{test_mode} =        $state->{payments}->{$prefix."_test_mode"};

    my $host = $self->{test_mode} ? 'sandbox' : 'secure';
    $self->{api} = {
	init	=> "https://$host.payture.com/apim/Init",
	pay	=> "https://$host.payture.com/apim/Pay",
	status	=> "https://$host.payture.com/apim/PayStatus",
    };
    $self->{result} = {};

    bless $self, $class;

    return $self;
}

sub init {
    my $self = shift;
    my (%opts) = @_;

    warn "PayTure Init: ".Dumper(\%opts)		if $DEBUG;

    ### Session Type: Pay or Block
    my $session_type = $opts{session_type} || 'Pay';

    my $order_id = $opts{order_id};
    unless ( $order_id ) {
	$self->{result}{error} = 'Не указан order_id';
	return $self;
    }

    my $uid = $opts{uid};
    unless ( $uid ) {
	$self->{result}{error} = 'Не указан user id';
	return $self;
    }

    ### Сумма в копейках. Если дробное - преобразуем
    my $total;
    my $sum = $opts{sum};
    if ( !$sum || $sum !~ /^[\d\,\.]+$/ ) {
	$self->{result}{error} = 'Не указана или неправильно указана сумма транзакции';
	return $self;
    }
    $sum =~ s/\,/\./;
    $total = sprintf("%.02f", $sum);
    $sum = int($sum * 100);

    my $operation = $keeper->get_documents(
		class	=> 'payments::Operation',
		status	=> $state->{payments}->{payture_test_mode},
		order_id	=> $order_id,
		order_by	=> 'ctime',
		return_mode	=> 'array_ref',
	);
    if ( ref $operation eq 'ARRAY' && @$operation ) {
	my $last = $operation->[-1];
	if ( $last->name eq 'suspend' || $last->name eq 'cancel' || $last->name eq 'close' ) {
		$self->{result}{error} = 'Заказ закрыт, отменен или заморожен. Оплата по нему невозможна';
		return $self;
	} else {
		$operation = $last;
	}
    } else {
	$operation = payments::Operation->new( $keeper );
	$operation->status( $state->{payments}->{payture_test_mode} );
	$operation->name( 'create' );
	$operation->order_id( $order_id );
	$operation->uid( $uid );
	$operation->sum( $sum );
	$operation->store;
    }

    my $ip = $opts{ip};
    unless ( $ip && $ip =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/ ) {
	$self->{result}{error} = 'Неверный IP-адрес';
	return $self;
    }
    warn "IP: $ip\n"	if $DEBUG;

    ### Пример: http://yoursite.com/result?orderid={orderid}&result={success}
    my $url = $opts{url};

    my $template = $opts{teplate};
    my $lang = $opts{lang};
    my $params = $opts{params};

    my ($transaction) = $keeper->get_documents(
			class	=> 'payments::Transaction',
			status	=> $state->{payments}->{payture_test_mode},
			limit	=> 1,
			order_id	=> $order_id,
			provider	=> $self->{payment_system},
		);
    if ( ref $transaction ) {
	### Init already exists
	$self->{result}{success} = 1;
	$self->{result}{session_id} = $transaction->session_id;
	$self->{result}{transaction} = $transaction;
    } else {
	my $ua = LWP::UserAgent->new;
	if ( $DEBUG ) {
		my $is_https = $ua->is_protocol_supported( 'https' );
		warn "PayTure Init: protocol 'https' is $is_https\n";
	}
	$ua->agent('Mozilla/5.0');
	my $req = URI->new( $self->{api}{init} );
	my @data = ( "SessionType=$session_type", "OrderId=$order_id", "Amount=$sum", "IP=$ip");
	push @data, "Url=$url"			if $url;
	push @data, "TemplateTag=$template"		if $template;
	push @data, "Url=$url"			if $url;
	push @data, "Language=$lang"		if $lang;
	push @data, "Total=$total"		unless exists $params->{Total};
	if ( ref $params eq 'HASH' ) {
		while ( my ($param, $val) = each %$params ) {
			push @data, "$param=$val";
		}
	}
	my $data_unescaped = join ';', @data;
	warn Dumper "PayTure Init data: $data_unescaped\n"		if $DEBUG;
	my $data_str = URI::Escape::uri_escape( $data_unescaped );
	warn "PayTure Init query: ".Dumper($req)			if $DEBUG;

	my $result = $ua->post( $req, Content => { Key => $self->id, Data => $data_str } );
	my $return_data = {};
	if ( $result->code == 200 ) {
		warn "PayTure Init result: [".$result->content."]\n"	if $DEBUG;
		my $content = xml2hash $result->content;
		warn Dumper $content					if $DEBUG;
		if ( ref $content && exists $content->{'Init'} && exists $content->{'Init'}{'-Success'} ) {
			if ( $content->{'Init'}{'-Success'} eq 'True' ) {
				my $now = Contenido::DateTime->new;
				my $transaction = payments::Transaction->new( $keeper );
				$transaction->dtime( $now->ymd('-').' '.$now->hms );
				$transaction->provider( $self->{payment_system} );
				$transaction->session_id( $content->{'Init'}{'-SessionId'} );
				$transaction->status( $state->{payments}->{payture_test_mode} );
				$transaction->order_id( $order_id );
				$transaction->operation_id( $operation->id );
				$transaction->currency_code( 'RUR' );
				$transaction->sum( $sum );
				$transaction->name( 'Init' );
				$transaction->store;

				$self->{result}{success} = 1;
				$self->{result}{session_id} = $content->{'Init'}{'-SessionId'};
				$self->{result}{transaction} = $transaction;
			} else {
				$self->{result}{error} = $content->{'Init'}{'-ErrCode'};
			}
		} else {
			$self->{result}{error} = 'PayTure Init failed';
			$self->{result}{responce} = $content;
			warn $self->{result}{error}."\n";
			warn "[$content]\n";
		}
	} else {
		$self->{result}{error} = 'PayTure Init failed';
		$self->{result}{responce} = $result->status_line;
		warn $self->{result}{error}.": ".$result->status_line."\n";
		warn Dumper $result;
	}
    }
    return $self;
}

sub pay {
    my $self = shift;
    my (%opts) = @_;

    unless ( exists $self->{result} ) {
	$self->init( %opts ); 
    }
    if ( $self->{result}{success} ) {
	return $self->{api}{pay}.'?SessionId='.$self->{result}{session_id};
    }
    return undef;
}

sub status {
    my $self = shift;
    my (%opts) = @_;

    warn "PayTure Status: ".Dumper(\%opts)                if $DEBUG;

    my $order_id = $opts{order_id};
    unless ( $order_id ) {
	$self->{result} = { error => 'Не указан order_id' };
	return $self;
    }
    
    my ($transaction) = exists $self->{result}{transaction} ? ($self->{result}{transaction}) : $keeper->get_documents(
			class	=> 'payments::Transaction',
			status	=> $state->{payments}->{payture_test_mode},
			limit	=> 1,
			order_id	=> $order_id,
			provider	=> $self->{payment_system},
		);
    if ( ref $transaction ) {
	my $ua = LWP::UserAgent->new;
	$ua->agent('Mozilla/5.0');
	my $req = URI->new( $self->{api}{status} );
	my $result = $ua->post( $req, Content => { Key => $self->id, OrderId => $order_id } );
	my $return_data = {};
	if ( $result->code == 200 ) {
		warn "PayTure Status result: [".$result->content."]\n"	if $DEBUG;
		my $content = xml2hash $result->content;
		warn Dumper $content					if $DEBUG;
		if ( ref $content && exists $content->{'PayStatus'} && exists $content->{'PayStatus'}{'-Success'} ) {
			if ( $content->{'PayStatus'}{'-Success'} eq 'True' ) {
				$self->{result} = {
					success	=> 1,
					status	=> $content->{'PayStatus'}{'-State'},
					amount	=> $content->{'PayStatus'}{'-Amount'},
					transaction	=> $transaction,
				};
			} else {
				$self->{result} = {
					error	=> $content->{'PayStatus'}{'-ErrCode'},
					transaction	=> $transaction,
				};
			}
		} else {
			$self->{result}{error} = 'PayTure Status failed';
			$self->{result}{responce} = $content;
			warn $self->{result}{error}."\n";
			warn "[$content]\n";
		}
	} else {
		$self->{result}{error} = 'PayTure Status failed';
		$self->{result}{responce} = $result->status_line;
		warn $self->{result}{error}.": ".$result->status_line."\n";
		warn Dumper $result;
	}
    } else {
	$self->{result}{error} = "Не найдена транзакция для order_id=$order_id";
	return $self;
    }

    return $self;
}

1;