1 package Business::OnlinePayment::Jety;
5 use Business::OnlinePayment 3;
6 use Business::OnlinePayment::HTTPS;
7 use vars qw($VERSION @ISA $me $DEBUG);
12 @ISA = qw(Business::OnlinePayment::HTTPS);
14 $me = 'Business::OnlinePayment::Jety';
19 'normal authorization' => 'echeck',
21 'credit' => 'ereturn',
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',
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 )
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'}) },
51 'username' => 'login',
52 'password' => 'password',
53 'ref' => 'authorization',
54 'accountdda' => 'account_number',
55 'amount' => sub { my %c = @_; sprintf("%.02f",$c{'amount'}) },
58 $map{'credit'} = $map{'void'};
60 my %defaults = ( # using the B:OP names
61 'phone' => '111-111-1111',
62 'bank_name' => 'unknown',
63 'bank_city' => 'unknown',
68 'normal authorization' => [ qw(
95 $required{'credit'} = $required{'void'};
99 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,
111 $self->server('api.cardservicesportal.com');
113 $self->path('/servlet/drafts.echeck');
119 $Business::OnlinePayment::HTTPS::DEBUG = $DEBUG;
120 $DB::single = $DEBUG;
122 # strip existent but empty fields so that required_fields works right
123 foreach(keys(%{$self->{_content}})) {
124 delete $self->{_content}->{$_}
125 if (!defined($self->{_content}->{$_} ) or
126 $self->{_content}->{$_} eq '');
129 my %content = $self->content();
130 my $action = lc($content{'action'});
132 croak "Jety only supports ECHECK payments.\n"
133 if( lc($content{'type'}) ne 'echeck' );
134 croak "Unsupported transaction type: '$action'\n"
135 if( !exists($trans_type{$action}) );
137 $self->required_fields(@{ $required{$action} });
139 my @fields = @{ $map{$action} } ;
140 tie my %request, 'Tie::IxHash', ( 'function' => $trans_type{$action} );
142 my ($key, $value) = (shift (@fields), shift (@fields));
143 if( ref($value) eq 'CODE' ) {
144 $request{$key} = $value->(%content);
146 elsif (defined($content{$value}) and $content{$value} ne '') {
147 $request{$key} = $content{$value};
149 elsif (exists($defaults{$value})) {
150 $request{$key} = $defaults{$value};
154 $DB::single = $DEBUG;
155 if($self->test_transaction()) {
156 print "https://".$self->server.$self->path."\n";
157 print "$_\t".$request{$_}."\n" foreach keys(%request);
158 $self->error_message('test mode not supported');
159 $self->is_success(0);
162 my ($reply, $response, %reply_headers) = $self->https_post(\%request);
164 if(not $response =~ /^200/) {
165 croak "HTTPS error: '$response'";
168 # string looks like this:
169 # P1=1234&P2=General Status&P3=Specific Status
170 # P3 is not always there, though.
171 if($reply =~ /^P1=(\d+)&P2=([\w ]*)(&P3=(\S+))?/) {
173 $self->is_success(1);
174 $self->authorization($4);
177 $self->is_success(0);
178 $self->error_message($2.($4 ? "($4)" : ''));
182 croak "Malformed server response: '$reply'";
189 # Required parameters:
190 # ftp_user, ftp_pass, ftp_host, ftp_path
193 eval('use Date::Parse q!str2time!; use Net::FTP; use File::Temp q!tempdir!');
197 # $self->required_fields, for processor options
199 my ($user, $pass, $host, $path) = map {
200 if($self->can($_) and $self->$_) {
203 push @missing, $_; '';
205 } qw(ftp_user ftp_pass ftp_host);
206 die "missing gateway option(s): ".join(', ',@missing)."\n" if @missing;
207 my $ftp_path = $self->ftp_path if $self->can('ftp_path');
209 my $start = $self->{_content}->{start};
210 $start &&= str2time($start);
211 $start ||= time - 86400;
212 $start = time2str('%Y%m%d',$start);
214 my $end = $self->{_content}->{end};
215 $end &&= str2time($end);
217 $end = time2str('%Y%m%d',$end);
219 my $ftp = Net::FTP->new($host)
220 or die "FTP connection to '$host' failed.\n";
221 $ftp->login($user, $pass) or die "FTP login failed: ".$ftp->message."\n";
222 $ftp->cwd($path) or die "can't chdir to $path\n" if $path;
224 my $tmp = tempdir(CLEANUP => 1);
226 foreach my $filename ($ftp->ls) {
227 if($filename =~ /^\w+_RET(\d{8}).csv$/
230 $ftp->get($filename, "$tmp/$1") or die "Failed to download $filename: ".$ftp->message."\n";
237 foreach my $filename (@files) {
238 open IN, '<', "$tmp/$filename";
239 my @fields = split ',',<IN>; #fetch header row
240 my ($i) = grep { $fields[$_] eq 'AccountToID' } 0..(scalar @fields - 1);
243 my @fields = split ',', $_;
244 push @tids, $fields[$i];
256 Business::OnlinePayment::Jety - Jety Payments ACH backend for Business::OnlinePayment
260 use Business::OnlinePayment;
263 # Electronic check authorization.
266 my $tx = new Business::OnlinePayment("Jety");
269 login => 'testdrive',
270 password => 'testpass',
271 action => 'Normal Authorization',
272 description => '111-111-1111 www.example.com',
274 invoice_number => '100100',
275 first_name => 'Jason',
276 last_name => 'Kohles',
277 address => '123 Anystreet',
281 account_type => 'personal checking',
282 account_number => '1000468551234',
283 routing_code => '707010024',
284 check_number => '1001', # optional
288 if($tx->is_success()) {
289 print "Check processed successfully: ".$tx->authorization."\n";
291 print "Check was rejected: ".$tx->error_message."\n";
294 =head1 SUPPORTED TRANSACTION TYPES
298 Content required: type, login, password, action, amount, first_name, last_name, account_number, routing_code, description.
300 description should be set in the form "111-111-1111 www.example.com"
304 For detailed information see L<Business::OnlinePayment>.
306 =head1 METHODS AND FUNCTIONS
308 See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> or provide additional functions.
312 Returns the four-digit result code.
316 Returns a useful error message.
318 =head1 Handling of content(%content) data:
322 The following actions are valid:
330 Mark Wells <mark@freeside.biz>
334 perl(1). L<Business::OnlinePayment>.
338 Need a complete, open-source back-office and customer self-service solution?
339 The Freeside software includes support for credit card and electronic check
340 processing, integrated trouble ticketing, and customer signup and self-service
343 http://freeside.biz/freeside/