Line # Revision Author
1 525 ahitrov package payments::Provider::PayTure;
2
3 use strict;
4 use warnings 'all';
5
6 use base 'payments::Provider::Base';
7 use Contenido::Globals;
8 use payments::Keeper;
9 use Digest::MD5;
10 use Data::Dumper;
11 use URI;
12 use URI::QueryParam;
13 554 ahitrov use XML::Fast;
14 525 ahitrov
15 our $init_url = '';
16
17 sub new {
18 my ($proto, %params) = @_;
19 my $class = ref($proto) || $proto;
20 my $self = {};
21 my $prefix = $class =~ /\:\:(\w+)$/ ? lc($1) : undef;
22 return unless $prefix;
23
24 $self->{payment_system} = $prefix;
25 $self->{app_id} = $state->{payments}->{$prefix."_app_id"};
26 $self->{secret} = $state->{payments}->{$prefix."_app_secret"};
27 $self->{currency} = $state->{payments}->{$prefix."_currency_code"};
28 $self->{test_mode} = $state->{payments}->{$prefix."_test_mode"};
29
30 556 ahitrov my $host = $self->{test_mode} ? 'sandbox' : 'secure';
31 $self->{api} = {
32 init => "https://$host.payture.com/apim/Init",
33 pay => "https://$host.payture.com/apim/Pay",
34 status => "https://$host.payture.com/apim/PayStatus",
35 554 ahitrov };
36 $self->{result} = {};
37 525 ahitrov
38 bless $self, $class;
39
40 return $self;
41 }
42
43 sub init {
44 my $self = shift;
45 my (%opts) = @_;
46
47 529 ahitrov warn "PayTure Init: ".Dumper(\%opts) if $DEBUG;
48 525 ahitrov
49 529 ahitrov ### Session Type: Pay or Block
50 my $session_type = $opts{session_type} || 'Pay';
51
52 my $order_id = $opts{order_id};
53 554 ahitrov unless ( $order_id ) {
54 $self->{result}{error} = 'Не указан order_id';
55 return $self;
56 }
57 529 ahitrov
58 554 ahitrov my $uid = $opts{uid};
59 unless ( $uid ) {
60 $self->{result}{error} = 'Не указан user id';
61 return $self;
62 }
63
64 529 ahitrov ### Сумма в копейках. Если дробное - преобразуем
65 554 ahitrov my $total;
66 529 ahitrov my $sum = $opts{sum};
67 554 ahitrov if ( !$sum || $sum !~ /^[\d\,\.]+$/ ) {
68 $self->{result}{error} = 'Не указана или неправильно указана сумма транзакции';
69 return $self;
70 525 ahitrov }
71 554 ahitrov $sum =~ s/\,/\./;
72 $total = sprintf("%.02f", $sum);
73 $sum = int($sum * 100);
74 525 ahitrov
75 554 ahitrov my $operation = $keeper->get_documents(
76 class => 'payments::Operation',
77 status => $state->{payments}->{payture_test_mode},
78 order_id => $order_id,
79 order_by => 'ctime',
80 return_mode => 'array_ref',
81 );
82 if ( ref $operation eq 'ARRAY' && @$operation ) {
83 my $last = $operation->[-1];
84 564 ahitrov if ( $last->name eq 'suspend' || $last->name eq 'cancel' || $last->name eq 'close' ) {
85 $self->{result}{error} = 'Заказ закрыт, отменен или заморожен. Оплата по нему невозможна';
86 554 ahitrov return $self;
87 } else {
88 $operation = $last;
89 }
90 } else {
91 $operation = payments::Operation->new( $keeper );
92 $operation->status( $state->{payments}->{payture_test_mode} );
93 $operation->name( 'create' );
94 $operation->order_id( $order_id );
95 $operation->uid( $uid );
96 $operation->sum( $sum );
97 $operation->store;
98 }
99
100 529 ahitrov my $ip = $opts{ip};
101 554 ahitrov unless ( $ip && $ip =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/ ) {
102 $self->{result}{error} = 'Неверный IP-адрес';
103 return $self;
104 }
105 warn "IP: $ip\n" if $DEBUG;
106 525 ahitrov
107 529 ahitrov ### Пример: http://yoursite.com/result?orderid={orderid}&result={success}
108 my $url = $opts{url};
109 525 ahitrov
110 529 ahitrov my $template = $opts{teplate};
111 my $lang = $opts{lang};
112 my $params = $opts{params};
113
114 554 ahitrov my ($transaction) = $keeper->get_documents(
115 class => 'payments::Transaction',
116 status => $state->{payments}->{payture_test_mode},
117 limit => 1,
118 order_id => $order_id,
119 provider => $self->{payment_system},
120 );
121 if ( ref $transaction ) {
122 ### Init already exists
123 $self->{result}{success} = 1;
124 $self->{result}{session_id} = $transaction->session_id;
125 $self->{result}{transaction} = $transaction;
126 } else {
127 my $ua = LWP::UserAgent->new;
128 556 ahitrov if ( $DEBUG ) {
129 my $is_https = $ua->is_protocol_supported( 'https' );
130 warn "PayTure Init: protocol 'https' is $is_https\n";
131 }
132 554 ahitrov $ua->agent('Mozilla/5.0');
133 my $req = URI->new( $self->{api}{init} );
134 my @data = ( "SessionType=$session_type", "OrderId=$order_id", "Amount=$sum", "IP=$ip");
135 push @data, "Url=$url" if $url;
136 push @data, "TemplateTag=$template" if $template;
137 push @data, "Url=$url" if $url;
138 push @data, "Language=$lang" if $lang;
139 push @data, "Total=$total" unless exists $params->{Total};
140 if ( ref $params eq 'HASH' ) {
141 while ( my ($param, $val) = each %$params ) {
142 push @data, "$param=$val";
143 }
144 525 ahitrov }
145 554 ahitrov my $data_unescaped = join ';', @data;
146 warn Dumper "PayTure Init data: $data_unescaped\n" if $DEBUG;
147 my $data_str = URI::Escape::uri_escape( $data_unescaped );
148 warn "PayTure Init query: ".Dumper($req) if $DEBUG;
149
150 my $result = $ua->post( $req, Content => { Key => $self->id, Data => $data_str } );
151 my $return_data = {};
152 if ( $result->code == 200 ) {
153 warn "PayTure Init result: [".$result->content."]\n" if $DEBUG;
154 my $content = xml2hash $result->content;
155 warn Dumper $content if $DEBUG;
156 if ( ref $content && exists $content->{'Init'} && exists $content->{'Init'}{'-Success'} ) {
157 if ( $content->{'Init'}{'-Success'} eq 'True' ) {
158 my $now = Contenido::DateTime->new;
159 my $transaction = payments::Transaction->new( $keeper );
160 $transaction->dtime( $now->ymd('-').' '.$now->hms );
161 $transaction->provider( $self->{payment_system} );
162 $transaction->session_id( $content->{'Init'}{'-SessionId'} );
163 $transaction->status( $state->{payments}->{payture_test_mode} );
164 $transaction->order_id( $order_id );
165 $transaction->operation_id( $operation->id );
166 $transaction->currency_code( 'RUR' );
167 $transaction->sum( $sum );
168 $transaction->name( 'Init' );
169 $transaction->store;
170
171 $self->{result}{success} = 1;
172 $self->{result}{session_id} = $content->{'Init'}{'-SessionId'};
173 $self->{result}{transaction} = $transaction;
174 } else {
175 $self->{result}{error} = $content->{'Init'}{'-ErrCode'};
176 }
177 } else {
178 $self->{result}{error} = 'PayTure Init failed';
179 $self->{result}{responce} = $content;
180 warn $self->{result}{error}."\n";
181 warn "[$content]\n";
182 }
183 } else {
184 $self->{result}{error} = 'PayTure Init failed';
185 $self->{result}{responce} = $result->status_line;
186 warn $self->{result}{error}.": ".$result->status_line."\n";
187 warn Dumper $result;
188 }
189 525 ahitrov }
190 554 ahitrov return $self;
191 }
192 529 ahitrov
193 554 ahitrov sub pay {
194 my $self = shift;
195 my (%opts) = @_;
196
197 unless ( exists $self->{result} ) {
198 $self->init( %opts );
199 }
200 if ( $self->{result}{success} ) {
201 return $self->{api}{pay}.'?SessionId='.$self->{result}{session_id};
202 }
203 return undef;
204 }
205
206 sub status {
207 my $self = shift;
208 my (%opts) = @_;
209
210 warn "PayTure Status: ".Dumper(\%opts) if $DEBUG;
211
212 my $order_id = $opts{order_id};
213 unless ( $order_id ) {
214 $self->{result} = { error => 'Не указан order_id' };
215 return $self;
216 }
217
218 my ($transaction) = exists $self->{result}{transaction} ? ($self->{result}{transaction}) : $keeper->get_documents(
219 class => 'payments::Transaction',
220 status => $state->{payments}->{payture_test_mode},
221 limit => 1,
222 order_id => $order_id,
223 provider => $self->{payment_system},
224 );
225 if ( ref $transaction ) {
226 my $ua = LWP::UserAgent->new;
227 $ua->agent('Mozilla/5.0');
228 my $req = URI->new( $self->{api}{status} );
229 my $result = $ua->post( $req, Content => { Key => $self->id, OrderId => $order_id } );
230 my $return_data = {};
231 if ( $result->code == 200 ) {
232 warn "PayTure Status result: [".$result->content."]\n" if $DEBUG;
233 my $content = xml2hash $result->content;
234 warn Dumper $content if $DEBUG;
235 if ( ref $content && exists $content->{'PayStatus'} && exists $content->{'PayStatus'}{'-Success'} ) {
236 if ( $content->{'PayStatus'}{'-Success'} eq 'True' ) {
237 $self->{result} = {
238 success => 1,
239 status => $content->{'PayStatus'}{'-State'},
240 amount => $content->{'PayStatus'}{'-Amount'},
241 transaction => $transaction,
242 };
243 } else {
244 $self->{result} = {
245 error => $content->{'PayStatus'}{'-ErrCode'},
246 transaction => $transaction,
247 };
248 }
249 } else {
250 $self->{result}{error} = 'PayTure Status failed';
251 $self->{result}{responce} = $content;
252 warn $self->{result}{error}."\n";
253 warn "[$content]\n";
254 }
255 } else {
256 $self->{result}{error} = 'PayTure Status failed';
257 $self->{result}{responce} = $result->status_line;
258 warn $self->{result}{error}.": ".$result->status_line."\n";
259 warn Dumper $result;
260 }
261 529 ahitrov } else {
262 554 ahitrov $self->{result}{error} = "Не найдена транзакция для order_id=$order_id";
263 return $self;
264 529 ahitrov }
265 554 ahitrov
266 return $self;
267 525 ahitrov }
268
269 1;