summaryrefslogtreecommitdiff
path: root/Jettis.pm
blob: 673bf67ab98d8ef5099e665e94666ef36aebd1e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
package Business::OnlinePayment::Jettis;

use strict;
use Carp;
use Business::OnlinePayment;
#use Business::CreditCard;
use Net::SSLeay qw( make_form post_https make_headers );
use URI;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $DEBUG);

require Exporter;

@ISA = qw(Exporter AutoLoader Business::OnlinePayment);
@EXPORT = qw();
@EXPORT_OK = qw();
$VERSION = '0.01';

$DEBUG = 0;

my %error = (
    0 => "Success",
    1 => "Missing Input",
    2 => "Missing CC Number",
    3 => "Missing First Name",
    4 => "Missing Last Name",
    5 => "Missing Zip Code",
    6 => "Missing Expiration Month",
    7 => "Missing Expiration Year",
    8 => "Missing Username",
    9 => "Missing V Password",
   10 => "No Agree Terms",
   11 => "No Agree Age",
   12 => "Missing City",
   13 => "Missing State",
   14 => "Username Length",
   15 => "Username Invalid Chars",
   16 => "Password Length",
   17 => "Different V Password",
   18 => "Same V Password",
   19 => "Invalid Email",
   20 => "Missing Address",
   21 => "Invalid Phone Number",
   22 => "Failed Mod 10",
   23 => "Invalid Expiration",
   24 => "Negative Database",
   25 => "Missing Data",
   26 => "Database Error",
   27 => "Username Exists",
   28 => "Invalid Store Params",
   29 => "Unknown",
   30 => "Procedure",
   31 => "CC Number",
   32 => "Invalid Version",
   33 => "Invalid Country Code",
   34 => "Country Bin",
   35 => "High Fraud Country",
   36 => "Different Country IP",
   37 => "No Account",
   38 => "IP Fraud",
   39 => "Password Maintenance",
   40 => "Password Invalid Chars",
   41 => "Duplicate Membership",
   42 => "Velocity",
   43 => "Too many consecutive errors",
   44 => "Missing Password",
   45 => "Zip Code Quotes",
   46 => "State Quotes",
   47 => "Street Quotes",
   48 => "Missing Country",
   49 => "Country Invalid Chars",
   50 => "Missing Quantity",
   51 => "Quantity Invalid Chars", 
   52 => "Missing IP Address",
   53 => "Invalid IP Address",  
   54 => "Merchant Text Area Quotes",
   55 => "First Name Length",
   56 => "Last Name Length",
   57 => "Zip Length",
   58 => "City Length",
   59 => "State Length",
   60 => "Email Length",
   61 => "Address Length",
   62 => "Merch Area Length",
   63 => "Quantity Limit Per Day Exceeded",
   64 => "Amount Limit Per Day Exceeded",
   65 => "Quantity Limit Per Month Exceeded",
   66 => "Amount Limit Per Month Exceeded",
   67 => "Credit CC Num Mismatch",
   68 => "Credit Price Mismatch",
   69 => "Merch ID Mismatch",
   70 => "Credit Prod ID Mismatch",
   71 => "Invalid Bill Item ID",
   72 => "Invalid Prod ID",
   73 => "Invalid Merch ID",
   74 => "Fraud Scrubbing", 
   75 => "Already Credited",
   76 => "Credit Card BIN Exclusion",
   77 => "Email Exclusion",
   78 => "IP not reversible",
   79 => "Invalid Bill ID",
   80 => "Auth already settled",
   81 => "Invalid Account Num",
   82 => "Mail Zip Code Exclusion",
   83 => "Missing IP Code",
   84 => "Username Mismatch",
   85 => "Password Mismatch",
  101 => "Bank Timeout",
  102 => "Invalid Request",
  103 => "Incomplete",
  104 => "Memory Allocation",
  105 => "Bugcheck",
  106 => "Inhibited",
  108 => "Reject",
  110 => "CC Number",
  111 => "Expiration Date",
  112 => "Prefix",
  113 => "Amount",
  114 => "Linkdown",
  115 => "SENO",
  116 => "Merchant Number",
  117 => "Request",
  118 => "Merchant Bank Down",
  119 => "Invalid Transaction Type",
  120 => "Call Center",
  121 => "Pickup",
  122 => "Declined",
  123 => "Account Declined",
  124 => "Fraud Alert",
  125 => "Overlimit",
  126 => "Too Small",
  127 => "Pin Error",
  128 => "Card Expired",
  129 => "Bank Invalid Email",
  130 => "Batch Unbalanced",
  131 => "Batch Unopened",
  140 => "Control Invalid",
  141 => "Control Readonly",
  142 => "Control Bad",
  150 => "Duplicate Address",
  151 => "Unknown Address",
  160 => "Duplicate Merchant Number",
  161 => "Merchant Busy",
  162 => "Merchant Inhibit",
  170 => "AVS",
  171 => "AVS Unmatched Void",
  172 => "AVS Void Failure",
  180 => "Invalid IP code",
  181 => "Invalid CVV2",
  182 => "Invalid Original Transaction Date",
  198 => "Server Timeout",
  199 => "Unrecognized",
  300 => "Re-Presented Check",
  301 => "Invalid ID",
  400 => "Failed Routing Mod 10",
  401 => "Missing Bank Name",
  402 => "Bank Name Quotes",
  403 => "Missing Bank Account Number",
  404 => "Invalid Bank Account Number", 
  405 => "Missing Bank Routing Number",
  406 => "Invalid Bank Routing Number",  
  407 => "Missing Check Number",
  408 => "Unsupported Transaction Type",
  409 => "Invalid Bank Name",
  999 => "Unknown Error",
);

sub set_defaults {
    my $self = shift;
    $self->server('join.billingservices.com');
    $self->port('443');
    $self->path('/psys/txnUrl');
    $self->build_subs(qw( product_id merchant_id ));
}

sub revmap_fields {
    my($self, %map) = @_;
    my %content = $self->content();
    foreach(keys %map) {
        $content{$_} = ref($map{$_})
                         ? ${ $map{$_} }
                         : $content{$map{$_}};
    }
    $self->content(%content);
}

sub submit {
    my $self = shift;
    my %content = $self->content();

    my $action = lc($content{'action'});
    if ( $action eq 'normal authorization' ) {
    } else {
      croak "$action not (yet) supported";
    }
    
    my $type = lc($content{'type'});
    if ( $type eq 'check' ) {
    } else {
      croak "$type not (yet) supported";
    }

    $self->revmap_fields(
        SUCCESS_URL     => \'https://secure.suicidegirls.com/',
	PRODUCT_ID      => \($self->product_id()),
	MERCHANT_ID     => 'login',
	VERSION         => \'1.0',
	SOR             => \'Y',
	REMOTE_ADDR     => \'10.0.0.1',
	TERMS_AGREE     => \'Y',
	CHECK_AGE       => \'Y',
	PAY_METHOD_ID   => \'A', # ACH
	PRICE           => 'amount',
	QTY             => \'1',
	CHECK_NUM       => \'1000',
	BANK_ACCT_NUM   => 'account_number',
	BANK_ROUT_NUM   => 'routing_code',
	BANK_NAME       => 'bank_name',
	EMAIL           => 'email',
	FIRST_NAME      => 'first_name',
	LAST_NAME       => 'last_name',
	ADDR_STREET_1   => 'address',
	ADDR_CITY       => 'city',
	ADDR_STATE      => 'state',
	ADDR_ZIP        => 'zip',
	ADDR_COUNTRY    => \'840', # US
	MERCH_TEXT_AREA => 'description',
    );

    my %post_data = $self->get_fields(qw(
        SUCCESS_URL PRODUCT_ID MERCHANT_ID VERSION SOR REMOTE_ADDR
        TERMS_AGREE CHECK_AGE PAY_METHOD_ID PRICE QTY CHECK_NUM BANK_ACCT_NUM
        BANK_ROUT_NUM BANK_NAME EMAIL FIRST_NAME LAST_NAME ADDR_STREET_1
        ADDR_CITY ADDR_STATE ADDR_ZIP ADDR_COUNTRY MERCH_TEXT_AREA
    ));

    my $pd = make_form(%post_data);
    my $s = $self->server();
    my $p = $self->port();
    my $t = $self->path();
    my $headers = make_headers('Referer' => $content{'referer'} );
    my($page,$server_response,%headers) = post_https($s,$p,$t,$headers,$pd);

#    warn join('-',%headers);

    my $uri = new URI $headers{'LOCATION'} or die "no LOCATION: header!";
    my %response = $uri->query_form or die "no response in LOCATION: header!";

    if ( $response{'RESULT_MAIN'} eq '0' ) {
      $self->is_success(1);
      $self->result_code('0');
      $self->authorization($response{'AUTHORIZATION_CODE'});
    } else {
      $self->is_success(0);
      $self->result_code($response{'RESULT_MAIN'});
      $self->error_message($error{$response{'RESULT_MAIN'}});
    }

}

1;
__END__

=head1 NAME

Business::OnlinePayment::Jettis - Jettis backend for Business::OnlinePayment

=head1 SYNOPSIS

  use Business::OnlinePayment;

  my $tx = new Business::OnlinePayment("Jettis");
  $tx->content(
      type           => 'CHECK',
      login          => 'test', #ClientID
      action         => 'Normal Authorization',
      description    => 'Business::OnlinePayment test',
      amount         => '49.95',
      invoice_number => '100100',
      name           => 'Tofu Beast',
      account_number => '12345',
      routing_code   => '123456789',
      bank_name      => 'First National Test Bank',
  );
  $tx->submit();

  if($tx->is_success()) {
      print "Check processed successfully: ".$tx->authorization."\n";
  } else {
      print "Check was rejected: ".$tx->error_message."\n";
  }

=head1 DESCRIPTION

For detailed information see L<Business::OnlinePayment>.

=head1 NOTE

This module only implements 'CHECK' (ACH) functionality at this time.  Credit
card transactions are not (yet) supported.

=head1 COMPATIBILITY

This module implements an interface to Jettis.com's HTTPS API.  Unfortunately,
no documentation is publicly available.  Jettis won't even send their full
manual to their customers - they insist on sending only few-page snippets at a
time.

=head1 AUTHOR

Steve Simitzis <steve@saturn5.com>
Ivan Kohler <ivan-jettis@420.am>

=head1 SEE ALSO

perl(1). L<Business::OnlinePayment>

=cut