File Coverage

blib/lib/Business/OnlinePayment/Jety.pm
Criterion Covered Total %
statement 21 107 19.6
branch 0 40 0.0
condition 0 31 0.0
subroutine 7 11 63.6
pod 1 3 33.3
total 29 192 15.1


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::Jety;
2              
3 1     1   34173 use strict;
  1         3  
  1         47  
4 1     1   6 use Carp 'croak';
  1         2  
  1         68  
5 1     1   980 use Business::OnlinePayment 3;
  1         3760  
  1         32  
6 1     1   1021 use Business::OnlinePayment::HTTPS;
  1         46328  
  1         37  
7 1     1   11 use vars qw($VERSION @ISA $me $DEBUG);
  1         2  
  1         81  
8              
9 1     1   1097 use Date::Format;
  1         9070  
  1         94  
10 1     1   13 use Tie::IxHash;
  1         2  
  1         1808  
11              
12             @ISA = qw(Business::OnlinePayment::HTTPS);
13             $VERSION = '0.09';
14             $me = 'Business::OnlinePayment::Jety';
15              
16             $DEBUG = 0;
17              
18             my %trans_type = (
19             'normal authorization' => 'echeck',
20             'void' => 'ereturn',
21             'credit' => 'ereturn',
22             );
23              
24             my %map = (
25             # 'function' will always be prepended
26             'normal authorization' => [ # note array-ness
27             'username' => 'login',
28             'password' => 'password',
29             'firstname' => 'first_name',
30             'lastname' => 'last_name',
31             'address1' => 'address',
32             'address2' => 'address2',
33             'city' => 'city',
34             'state' => 'state',
35             'zip' => 'zip',
36             'email' => 'email',
37             'phone' => 'phone',
38             'programdesc' => 'description',
39             'ref' => sub { my %c = @_;
40             $c{'authorization'} ||
41             substr( time2str('%Y%m%d%H%M%S',time). int(rand(10000)), -15 )
42             },
43             'bankname' => 'bank_name',
44             'bankcity' => 'bank_city',
45             'bankstate' => 'bank_state',
46             'accountaba' => 'routing_code',
47             'accountdda' => 'account_number',
48             'amount' => sub { my %c = @_; sprintf("%.02f",$c{'amount'}) },
49             ],
50             'void' => [
51             'username' => 'login',
52             'password' => 'password',
53             'ref' => 'authorization',
54             'accountdda' => 'account_number',
55             'amount' => sub { my %c = @_; sprintf("%.02f",$c{'amount'}) },
56             ],
57             );
58             $map{'credit'} = $map{'void'};
59              
60             my %defaults = ( # using the B:OP names
61             'phone' => '111-111-1111',
62             'bank_name' => 'unknown',
63             'bank_city' => 'unknown',
64             'bank_state' => 'XX',
65             );
66              
67             my %required = (
68             'normal authorization' => [ qw(
69             type
70             action
71             login
72             password
73             first_name
74             last_name
75             address
76             city
77             state
78             zip
79             email
80             account_number
81             routing_code
82             amount
83             description
84             ) ],
85             'void' => [ qw(
86             type
87             action
88             login
89             password
90             authorization
91             account_number
92             amount
93             ) ],
94             );
95             $required{'credit'} = $required{'void'};
96              
97             sub _info {
98             {
99 0     0     info_compat => '0.01',
100             gateway_name => 'Jety',
101             gateway_url => 'http://www.jetypay.com',
102             module_version => $VERSION,
103             supported_types => [ 'ECHECK' ],
104             supported_actions => [ 'Normal Authorization', 'Void', 'Credit' ],
105             ECHECK_void_requires_account => 1,
106             }
107             }
108              
109             sub set_defaults {
110 0     0 0   my $self = shift;
111 0           $self->server('api.cardservicesportal.com');
112 0           $self->port(443);
113 0           $self->path('/servlet/drafts.echeck');
114 0           return;
115             }
116              
117             sub submit {
118 0     0 1   my $self = shift;
119 0           $Business::OnlinePayment::HTTPS::DEBUG = $DEBUG;
120 0           $DB::single = $DEBUG;
121              
122             # strip existent but empty fields so that required_fields works right
123 0           foreach(keys(%{$self->{_content}})) {
  0            
124 0 0 0       delete $self->{_content}->{$_}
125             if (!defined($self->{_content}->{$_} ) or
126             $self->{_content}->{$_} eq '');
127             }
128              
129 0           my %content = $self->content();
130 0           my $action = lc($content{'action'});
131              
132 0 0         croak "Jety only supports ECHECK payments.\n"
133             if( lc($content{'type'}) ne 'echeck' );
134 0 0         croak "Unsupported transaction type: '$action'\n"
135             if( !exists($trans_type{$action}) );
136              
137 0           $self->required_fields(@{ $required{$action} });
  0            
138              
139 0           my @fields = @{ $map{$action} } ;
  0            
140 0           tie my %request, 'Tie::IxHash', ( 'function' => $trans_type{$action} );
141 0           while(@fields) {
142 0           my ($key, $value) = (shift (@fields), shift (@fields));
143 0 0 0       if( ref($value) eq 'CODE' ) {
    0          
    0          
144 0           $request{$key} = $value->(%content);
145             }
146             elsif (defined($content{$value}) and $content{$value} ne '') {
147 0           $request{$key} = $content{$value};
148             }
149             elsif (exists($defaults{$value})) {
150 0           $request{$key} = $defaults{$value};
151             } # else do nothing
152             }
153              
154 0           $DB::single = $DEBUG;
155 0 0         if($self->test_transaction()) {
156 0           print "https://".$self->server.$self->path."\n";
157 0           print "$_\t".$request{$_}."\n" foreach keys(%request);
158 0           $self->error_message('test mode not supported');
159 0           $self->is_success(0);
160 0           return;
161             }
162 0           my ($reply, $response, %reply_headers) = $self->https_post(\%request);
163            
164 0 0         if(not $response =~ /^200/) {
165 0           croak "HTTPS error: '$response'";
166             }
167              
168             # string looks like this:
169             # P1=1234&P2=General Status&P3=Specific Status
170             # P3 is not always there, though.
171 0 0         if($reply =~ /^P1=(\d+)&P2=([\w ]*)(&P3=(\S+))?/) {
172 0 0         if($1 == 0) {
173 0           $self->is_success(1);
174 0           $self->authorization($4);
175             }
176             else {
177 0           $self->is_success(0);
178 0 0         $self->error_message($2.($4 ? "($4)" : ''));
179             }
180             }
181             else {
182 0           croak "Malformed server response: '$reply'";
183             }
184              
185 0           return;
186             }
187              
188             sub get_returns {
189             # Required parameters:
190             # ftp_user, ftp_pass, ftp_host, ftp_path
191             # Optional:
192             # start, end
193 0     0 0   eval('use Date::Parse q!str2time!; use Net::FTP; use File::Temp q!tempdir!');
194 0 0         die $@ if $@;
195              
196 0           my $self = shift;
197             # $self->required_fields, for processor options
198 0           my @missing;
199             my ($user, $pass, $host, $path) = map {
200 0 0 0       if($self->can($_) and $self->$_) {
  0            
201 0           $self->$_ ;
202             } else {
203 0           push @missing, $_; '';
  0            
204             }
205             } qw(ftp_user ftp_pass ftp_host);
206 0 0         die "missing gateway option(s): ".join(', ',@missing)."\n" if @missing;
207 0 0         my $ftp_path = $self->ftp_path if $self->can('ftp_path');
208              
209 0           my $start = $self->{_content}->{start};
210 0   0       $start &&= str2time($start);
211 0   0       $start ||= time - 86400;
212 0           $start = time2str('%Y%m%d',$start);
213              
214 0           my $end = $self->{_content}->{end};
215 0   0       $end &&= str2time($end);
216 0   0       $end ||= time;
217 0           $end = time2str('%Y%m%d',$end);
218            
219 0 0         my $ftp = Net::FTP->new($host)
220             or die "FTP connection to '$host' failed.\n";
221 0 0         $ftp->login($user, $pass) or die "FTP login failed: ".$ftp->message."\n";
222 0 0 0       $ftp->cwd($path) or die "can't chdir to $path\n" if $path;
223            
224 0           my $tmp = tempdir(CLEANUP => 1);
225 0           my @files;
226 0           foreach my $filename ($ftp->ls) {
227 0 0 0       if($filename =~ /^\w+_RET(\d{8}).csv$/
      0        
228             and $1 >= $start
229             and $1 <= $end ) {
230 0 0         $ftp->get($filename, "$tmp/$1") or die "Failed to download $filename: ".$ftp->message."\n";
231 0           push @files, $1;
232             }
233             }
234 0           $ftp->close;
235              
236 0           my @tids;
237 0           foreach my $filename (@files) {
238 0           open IN, '<', "$tmp/$filename";
239 0           my @fields = split ',',; #fetch header row
240 0           my ($i) = grep { $fields[$_] eq 'AccountToID' } 0..(scalar @fields - 1);
  0            
241 0   0       $i ||= 1;
242 0           while() {
243 0           my @fields = split ',', $_;
244 0           push @tids, $fields[$i];
245             }
246 0           close IN;
247             }
248 0           return @tids;
249             }
250              
251             1;
252             __END__