Revision 554
- Date:
- 2016/03/16 22:57:06
- Files:
-
- /utf8/plugins/payments/comps/www/payment.backend/payture_pay.html (Diff) (Checkout)
- /utf8/plugins/payments/lib/payments/Provider/PayTure.pm (Diff) (Checkout)
- /utf8/plugins/payments/lib/payments/SQL/TransactionsTable.pm (Diff) (Checkout)
- /utf8/plugins/payments/sql/TOAST/transactions.552.sql (Diff) (Checkout)
- /utf8/plugins/payments/sql/TOAST/transactions.sql (Diff) (Checkout)
Legend:
- Added
- Removed
- Modified
-
utf8/plugins/payments/comps/www/payment.backend/payture_pay.html
1 <%once> 2 3 my %NAMES = ( 4 'MerchantBlock' => 'Authorized', 5 'MerchantPay' => 'Charged', 6 'MerchantRefund' => 'Refunded', 7 ); 8 9 </%once> 10 <%args> 11 12 $SessionId => undef 13 $OrderId => undef 14 $Notification => undef 15 $Success => undef 16 $Amount => undef 17 $CardNumber => undef 18 $MerchantContract => undef 19 20 </%args> 21 <%init> 22 23 warn Dumper \%ARGS; 24 25 my $provider = payments::Provider::PayTure->new; 26 my (@operations, $transaction); 27 if ( $OrderId ) { 28 @operations = $keeper->get_documents( 29 class => 'payments::Operation', 30 order_id => $OrderId, 31 order_by => 'ctime', 32 ); 33 ($transaction) = $keeper->get_documents ( 34 class => 'payments::Transaction', 35 provider => $provider->payment_system, 36 order_id => $OrderId, 37 order_by => 'ctime desc', 38 limit => 1, 39 ); 40 } 41 if ( ref $transaction ) { 42 $transaction->name( $NAMES{$Notification} ); 43 $transaction->success( $Success eq 'True' ? 1 : 0 ); 44 $transaction->store; 45 46 if ( $keeper->can('_payture_handler') ) { 47 $keeper->_payture_handler( $transaction ); 48 } 49 } 50 51 </%init> -
utf8/plugins/payments/lib/payments/Provider/PayTure.pm
10 10 use Data::Dumper; 11 11 use URI; 12 12 use URI::QueryParam; 13 use XML::Fast; 13 14 14 15 our $init_url = ''; 15 16 … … 26 27 $self->{currency} = $state->{payments}->{$prefix."_currency_code"}; 27 28 $self->{test_mode} = $state->{payments}->{$prefix."_test_mode"}; 28 29 29 $self->{api}{init} = 'https://sandbox.payture.com/apim/Init'; 30 $self->{api} = $self->{test_mode} ? 31 { 32 init => 'https://sandbox.payture.com/apim/Init', 33 pay => 'https://sandbox.payture.com/apim/Pay', 34 status => 'https://sandbox.payture.com/apim/PayStatus', 35 } : { 36 }; 37 $self->{result} = {}; 30 38 31 39 bless $self, $class; 32 40 … … 43 51 my $session_type = $opts{session_type} || 'Pay'; 44 52 45 53 my $order_id = $opts{order_id}; 46 return 0 unless $order_id; 54 unless ( $order_id ) { 55 $self->{result}{error} = 'Не указан order_id'; 56 return $self; 57 } 47 58 59 my $uid = $opts{uid}; 60 unless ( $uid ) { 61 $self->{result}{error} = 'Не указан user id'; 62 return $self; 63 } 64 48 65 ### Сумма в копейках. Если дробное - преобразуем 66 my $total; 49 67 my $sum = $opts{sum}; 50 return 0 unless $sum; 51 if ( $sum =~ /[\.\,]/ ) { 52 $sum =~ s/\,/\./; 53 $sum = int($sum * 100); 68 if ( !$sum || $sum !~ /^[\d\,\.]+$/ ) { 69 $self->{result}{error} = 'Не указана или неправильно указана сумма транзакции'; 70 return $self; 54 71 } 72 $sum =~ s/\,/\./; 73 $total = sprintf("%.02f", $sum); 74 $sum = int($sum * 100); 55 75 76 my $operation = $keeper->get_documents( 77 class => 'payments::Operation', 78 status => $state->{payments}->{payture_test_mode}, 79 order_id => $order_id, 80 order_by => 'ctime', 81 return_mode => 'array_ref', 82 ); 83 if ( ref $operation eq 'ARRAY' && @$operation ) { 84 my $last = $operation->[-1]; 85 if ( $last->name eq 'suspend' || $last->name eq 'cancel' ) { 86 $self->{result}{error} = 'Заказ был отменен или заморожен'; 87 return $self; 88 } else { 89 $operation = $last; 90 } 91 } else { 92 $operation = payments::Operation->new( $keeper ); 93 $operation->status( $state->{payments}->{payture_test_mode} ); 94 $operation->name( 'create' ); 95 $operation->order_id( $order_id ); 96 $operation->uid( $uid ); 97 $operation->sum( $sum ); 98 $operation->store; 99 } 100 56 101 my $ip = $opts{ip}; 57 return 0 unless $ip && $ip =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/; 58 warn "IP: $ip\n"; 102 unless ( $ip && $ip =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/ ) { 103 $self->{result}{error} = 'Неверный IP-адрес'; 104 return $self; 105 } 106 warn "IP: $ip\n" if $DEBUG; 59 107 60 108 ### Пример: http://yoursite.com/result?orderid={orderid}&result={success} 61 109 my $url = $opts{url}; … … 64 112 my $lang = $opts{lang}; 65 113 my $params = $opts{params}; 66 114 67 my $ua = LWP::UserAgent->new; 68 $ua->agent('Mozilla/5.0'); 69 $ua->ssl_opts( verify_hostname => 0, SSL_ca_path => '/usr/local/share/certs' ); 70 my $req = URI->new( $self->{api}{init} ); 71 # $req->query_param( Key => $self->id ); 72 my @data = ( "SessionType=$session_type", "OrderId=$order_id", "Amount=$sum", "IP=$ip"); 73 push @data, "Url=$url" if $url; 74 push @data, "TemplateTag=$template" if $template; 75 push @data, "Url=$url" if $url; 76 push @data, "Language=$lang" if $lang; 77 if ( ref $params eq 'HASH' ) { 78 while ( my ($param, $val) = each %$params ) { 79 push @data, "$param=$val"; 115 my ($transaction) = $keeper->get_documents( 116 class => 'payments::Transaction', 117 status => $state->{payments}->{payture_test_mode}, 118 limit => 1, 119 order_id => $order_id, 120 provider => $self->{payment_system}, 121 ); 122 if ( ref $transaction ) { 123 ### Init already exists 124 $self->{result}{success} = 1; 125 $self->{result}{session_id} = $transaction->session_id; 126 $self->{result}{transaction} = $transaction; 127 } else { 128 my $ua = LWP::UserAgent->new; 129 my $is_https = $ua->is_protocol_supported( 'https' ); 130 warn "PayTure Init: protocol 'https' is $is_https\n" if $DEBUG; 131 $ua->agent('Mozilla/5.0'); 132 $ua->ssl_opts( verify_hostname => 0, SSL_ca_path => '/usr/local/share/certs' ); 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 } 80 144 } 145 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 } 81 189 } 82 my $data_unescaped = join ';', @data; 83 warn Dumper "PayTure Init data: $data_unescaped\n"; 84 my $data_str = URI::Escape::uri_escape( $data_unescaped ); 85 # $req->query_param( Data => $data_str ); 86 warn "PayTure Init query: ".Dumper($req) if $DEBUG; 190 return $self; 191 } 87 192 88 my $result = $ua->post( $req, Content => { Key => $self->id, Data => $data_str } ); 89 if ( $result->code == 200 ) { 90 warn Dumper $result->content; 91 return 1; 193 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 } 92 261 } else { 93 warn Dumper $result; 94 warn "PayTure Init failed; ".$result->status_line."\n"; 262 $self->{result}{error} = "Не найдена транзакция для order_id=$order_id"; 263 return $self; 95 264 } 96 return 0; 265 266 return $self; 97 267 } 98 268 99 269 1; -
utf8/plugins/payments/lib/payments/SQL/TransactionsTable.pm
26 26 27 27 _operation_id_filter 28 28 _order_id_filter 29 _session_id_filter 30 _success_filter 29 31 _provider_filter 32 _name_exact_filter 30 33 ); 31 34 32 35 return \@available_filters; … … 66 69 'db_type' => 'numeric', 67 70 'db_opts' => "default 0", 68 71 }, 72 { 73 'attr' => 'session_id', 74 'type' => 'string', 75 'rusname' => 'Ключ сессии', 76 'db_field' => 'session_id', 77 'db_type' => 'text', 78 }, 69 79 { # ID заказа 70 80 'attr' => 'order_id', 71 81 'type' => 'integer', … … 74 84 'db_type' => 'integer', 75 85 'db_opts' => "not null", 76 86 }, 77 { # ID заказа 87 { # ID операции в таблице операций 78 88 'attr' => 'operation_id', 79 89 'type' => 'integer', 80 90 'rusname' => 'ID транзакции', … … 117 127 'db_field' => 'payment_system', 118 128 'db_type' => 'text', 119 129 }, 130 { # Результат транзакции 131 'attr' => 'success', 132 'type' => 'checkbox', 133 'rusname' => 'Транзакция прошла успешно', 134 'db_field' => 'success', 135 'db_type' => 'smallint', 136 'db_opts' => "default 1", 137 }, 120 138 ); 121 139 } 122 140 … … 134 152 return &SQL::Common::_generic_int_filter('d.order_id', $opts{order_id}); 135 153 } 136 154 155 sub _success_filter { 156 my ($self,%opts)=@_; 157 return undef unless ( exists $opts{success} ); 158 return &SQL::Common::_generic_int_filter('d.success', $opts{success}); 159 } 160 137 161 sub _provider_filter { 138 162 my ($self,%opts)=@_; 139 163 return undef unless ( exists $opts{provider} ); 140 164 return &SQL::Common::_generic_text_filter('d.provider', $opts{provider}); 141 165 } 142 166 167 sub _session_id_filter { 168 my ($self,%opts)=@_; 169 return undef unless ( exists $opts{session_id} ); 170 return &SQL::Common::_generic_int_filter('d.session_id', $opts{session_id}); 171 } 172 173 sub _name_exact_filter { 174 my ($self,%opts)=@_; 175 return undef unless ( exists $opts{name_exact} ); 176 return &SQL::Common::_generic_text_filter('d.name', $opts{name_exact}); 177 } 178 143 179 1; -
utf8/plugins/payments/sql/TOAST/transactions.552.sql
1 ALTER TABLE payments_transactions ADD COLUMN session_id text; 2 ALTER TABLE payments_transactions ADD COLUMN success smallint default 1; 3 CREATE INDEX payments_transactions_sessions ON payments_transactions USING btree (provider, session_id) WHERE session_id is not null; -
utf8/plugins/payments/sql/TOAST/transactions.sql
9 9 provider text, 10 10 name text, 11 11 account_id numeric, 12 session_id text, 12 13 order_id integer not null, 13 14 operation_id numeric not null, 14 15 currency_code varchar(4), … … 16 17 account_user text, 17 18 payment_system text, 18 19 account_corr text, 20 success smallint default 1, 19 21 data text 20 22 ); 21 23 CREATE INDEX payments_transactions_operations ON payments_transactions USING btree (provider, operation_id); 24 CREATE INDEX payments_transactions_sessions ON payments_transactions USING btree (provider, session_id) WHERE session_id is not null; 22 25 CREATE INDEX payments_transactions_orders ON payments_transactions USING btree (order_id);