add a full timestamp to the 'ref' autogeneration to avoid collisions
[Business-OnlinePayment-Jety.git] / lib / Business / OnlinePayment / Jety.pm
1 package Business::OnlinePayment::Jety;
2
3 use strict;
4 use Carp;
5 use Business::OnlinePayment 3;
6 use Business::OnlinePayment::HTTPS;
7 use vars qw($VERSION @ISA $me $DEBUG);
8
9 use Date::Format;
10 use Tie::IxHash;
11
12 @ISA = qw(Business::OnlinePayment::HTTPS);
13 $VERSION = '0.04';
14 $me = 'Business::OnlinePayment::Jety';
15
16 $DEBUG = 0;
17
18 my @fields = (qw(
19   username
20   password
21   function
22   firstname
23   lastname
24   address1
25   address2
26   city
27   state
28   zip
29   email
30   phone
31   programdesc
32   ref
33   bankname
34   bankcity
35   bankstate
36   accountaba
37   accountdda
38   amount
39 ));
40
41 my %map = (
42   'login'          => 'username',
43   'first_name'     => 'firstname',
44   'last_name'      => 'lastname',
45   'address'        => 'address1',
46   'bank_name'      => 'bankname',
47   'bank_city'      => 'bankcity',
48   'bank_state'     => 'bankstate',
49   'account_number' => 'accountdda',
50   'routing_code'   => 'accountaba',
51   'description'    => 'programdesc',
52 );
53
54 sub set_defaults {
55   my $self = shift;
56   $self->server('api.cardservicesportal.com');
57   $self->port(443);
58   $self->path('/servlet/drafts.echeck');
59   return;
60 }
61
62 sub map_fields {
63   my $self = shift;
64   my $content = $self->{_content};
65
66   $self->remap_fields(%map);
67
68   die "Jety API only supports ECHECK payments.\n" 
69     if(lc($content->{type}) ne 'echeck');
70   die "Jety interface only supports Normal Authorization.\n"
71     if(lc($content->{action}) ne 'normal authorization');
72
73   $content->{'function'} = 'echeck';
74   $content->{'ref'} =
75     substr( time2str('%Y%m%d%H%M%S',time). int(rand(10000)), -15 );
76
77   $content->{'phone'} ||= '111-111-1111';
78
79   $content->{'bankname'} ||= 'unknown';
80   $content->{'bankcity'} ||= 'unknown';
81   $content->{'bankstate'} ||= 'XX';
82
83   return;
84 }
85
86 sub submit {
87   my $self = shift;
88   $Business::OnlinePayment::HTTPS::DEBUG = $DEBUG;
89   $DB::single = $DEBUG; 
90
91   # strip existent but empty fields so that required_fields works right
92   foreach(keys(%{$self->{_content}})) {
93     delete $self->{_content}->{$_} 
94       if (!defined($self->{_content}->{$_} ) or
95            $self->{_content}->{$_} eq '');
96   }
97
98   $self->required_fields(qw(
99     type
100     action
101     first_name
102     last_name
103     address
104     city
105     state
106     zip
107     email
108     account_number
109     routing_code
110     amount
111     description
112   ));
113   $self->map_fields;
114   tie my %request, 'Tie::IxHash', map { $_ => $self->{_content}->{$_} } @fields;
115
116   $DB::single = $DEBUG;
117   if($self->test_transaction()) {
118     print "https://".$self->server.$self->path."\n";
119     print "$_\t".$request{$_}."\n" foreach keys(%request);
120     $self->error_message('test mode not supported');
121     $self->is_success(0);
122     return;
123   }
124   my ($reply, $response, %reply_headers) = $self->https_post(\%request);
125   
126   if(not $response =~ /^200/) {
127     croak "HTTPS error: '$response'";
128   }
129
130   # string looks like this:
131   # P1=1234&P2=General Status&P3=Specific Status
132   # P3 is not always there, though.
133   if($reply =~ /^P1=(\d+)&P2=([\w ]*)(&P3=(\S+))?/) {
134     if($1 == 0) {
135       $self->is_success(1);
136       $self->authorization($4);
137     }
138     else {
139       $self->is_success(0);
140       $self->error_message($2.($4 ? "($4)" : ''));
141     }
142   }
143   else {
144     croak "Malformed server response: '$reply'";
145   }
146
147   return;
148 }
149
150 1;
151 __END__
152
153 =head1 NAME
154
155 Business::OnlinePayment::Jety - Jety Payments ACH backend for Business::OnlinePayment
156
157 =head1 SYNOPSIS
158
159   use Business::OnlinePayment;
160
161   ####
162   # Electronic check authorization.  We only support 
163   # 'Normal Authorization'.
164   ####
165
166   my $tx = new Business::OnlinePayment("Jety");
167   $tx->content(
168       type           => 'ECHECK',
169       login          => 'testdrive',
170       password       => 'testpass',
171       action         => 'Normal Authorization',
172       description    => '111-111-1111 www.example.com',
173       amount         => '49.95',
174       invoice_number => '100100',
175       first_name     => 'Jason',
176       last_name      => 'Kohles',
177       address        => '123 Anystreet',
178       city           => 'Anywhere',
179       state          => 'UT',
180       zip            => '84058',
181       account_type   => 'personal checking',
182       account_number => '1000468551234',
183       routing_code   => '707010024',
184       check_number   => '1001', # optional
185   );
186   $tx->submit();
187
188   if($tx->is_success()) {
189       print "Check processed successfully: ".$tx->authorization."\n";
190   } else {
191       print "Check was rejected: ".$tx->error_message."\n";
192   }
193
194 =head1 SUPPORTED TRANSACTION TYPES
195
196 =head2 ECHECK
197
198 Content required: type, login, password, action, amount, first_name, last_name, account_number, routing_code, description.
199
200 description should be set in the form "111-111-1111 www.example.com"
201
202 =head1 DESCRIPTION
203
204 For detailed information see L<Business::OnlinePayment>.
205
206 =head1 METHODS AND FUNCTIONS
207
208 See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> or provide additional functions.  
209
210 =head2 result_code
211
212 Returns the four-digit result code.
213
214 =head2 error_message
215
216 Returns a useful error message.
217
218 =head1 Handling of content(%content) data:
219
220 =head2 action
221
222 The following actions are valid:
223
224   normal authorization
225
226 =head1 AUTHOR
227
228 Mark Wells <mark@freeside.biz>
229
230 =head1 SEE ALSO
231
232 perl(1). L<Business::OnlinePayment>.
233
234 =cut
235