initial try at a module
[Business-OnlinePayment-FirstDataGlobalGateway.git] / lib / Business / OnlinePayment / FirstDataGlobalGateway.pm
1 package Business::OnlinePayment::FirstDataGlobalGateway;
2 use base qw( Business::OnlinePayment );
3
4 use warnings;
5 use strict;
6 use Data::Dumper;
7 use Business::CreditCard;
8 use SOAP::Lite +trace => 'all';
9 #SOAP::Lite->import(+trace=>'debug');
10
11 our $VERSION = '0.01';
12 $VERSION = eval $VERSION; # modperlstyle: convert the string into a number
13
14 our @alpha = ( 'a'..'z', 'A'..'Z', '0'..'9' );
15
16 sub _info {
17   {
18     'info_compat'       => '0.01',
19     'gateway_name'      => 'First Data Global Gateway e4',
20     'gateway_url'       => 'https://www.firstdata.com/en_us/products/merchants/ecommerce/online-payment-processing.html',
21     'module_version'    => $VERSION,
22     'supported_types'   => [ 'CC' ], #, 'ECHECK' ],
23     #'token_support'     => 1,
24     #'test_transaction'  => 1,
25
26     'supported_actions' => [ 'Normal Authorization',
27                              #'Credit',
28                            ],
29   };
30 }
31
32 sub set_defaults {
33     my $self = shift;
34     #my %opts = @_;
35
36     #$self->build_subs(qw( order_number avs_code cvv2_response
37     #                      response_page response_code response_headers
38     #                 ));
39
40     $self->build_subs(qw( avs_code ));
41
42 }
43
44 sub map_fields {
45     my($self) = @_;
46
47     my %content = $self->content();
48
49     # TYPE MAP
50     my %types = ( 'visa'               => 'CC',
51                   'mastercard'         => 'CC',
52                   'american express'   => 'CC',
53                   'discover'           => 'CC',
54                   'check'              => 'ECHECK',
55                 );
56     $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
57     $self->transaction_type($content{'type'});
58     
59     # ACTION MAP 
60     my $action = lc($content{'action'});
61     my %actions =
62       ( 'normal authorization' => '00', # Purchase
63         'authorization_only'   => '01', # 
64         'post authorization'   => '02', # Pre-Authorization Completion
65         # '' => '03', # Forced Post
66         'credit'               => '04', # Refund
67         # '' => '05', # Pre-Authorization Only
68         'void'                 => '13', # Void
69         #'reverse authorization' => '',
70
71         # '' => '07', # PayPal Order
72         # '' => '32', # Tagged Pre-Authorization Completion
73         # '' => '33', # Tagged Void
74         # '' => '34', # Tagged Refund
75         # '' => '83', # CashOut (ValueLink, v9 or higher end point only)
76         # '' => '85', # Activation (ValueLink, v9 or higher end point only)
77         # '' => '86', # Balance Inquiry (ValueLink, v9 or higher end point only)
78         # '' => '88', # Reload (ValueLink, v9 or higher end point only)
79         # '' => '89', # Deactivation (ValueLink, v9 or higher end point only)
80       );
81
82     $content{'action'} = $actions{$action} || $action;
83
84     # stuff it back into %content
85     $self->content(%content);
86
87 }
88
89 sub remap_fields {
90     my($self,%map) = @_;
91
92     my %content = $self->content();
93     foreach(keys %map) {
94         $content{$map{$_}} = $content{$_};
95     }
96     $self->content(%content);
97 }
98
99 sub submit {
100   my($self) = @_;
101
102   $self->map_fields;
103
104   $self->remap_fields(
105         'login'             => 'ExactID',
106         'password'          => 'Password',
107
108         'action'            => 'TransactionType',
109
110         'amount'            => 'DollarAmount',
111         'currency'          => 'Currency',
112         'card_number'       => 'Card_Number',
113         'track1'            => 'Track1',
114         'track2'            => 'Track2',
115         'expiration'        => 'Expiry_Date',
116         'name'              => 'CardHoldersName',
117         'cvv2'              => 'VerificationStr2',
118
119         'authorization'     => 'Authorization_Num',
120         'order_number'      => 'Reference_No',
121
122         'zip'               => 'ZipCode',
123         'tax'               => 'Tax1Amount',
124         'customer_id'       => 'Customer_Ref',
125         'customer_ip'       => 'Client_IP',
126         'email'             => 'Client_Email',
127
128         #account_type      => 'accountType',
129
130   );
131
132   my %content = $self->content();
133
134   #$content{'mop'} = $mop{ cardtype($content{creditCardNum}) }
135   #  if $content{'type'} eq 'CC';
136
137   #if ( $self->test_transaction ) {
138   #  $content{agentCode} = 'TEST88';
139   #  $content{password}  = 'TEST88';
140   #}
141
142   $content{Expiry_Date} =~ s/\///;
143
144   $content{country} ||= 'US';
145
146   $content{VerificationStr1} =
147     join('|', map $content{$_}, qw( address zip city state country ));
148   $content{VerificationStr1} .= '|'. $content{'phone'}
149     if $content{'type'} eq 'ECHECK';
150
151   $content{CVD_Presence_Ind} = '1' if length($content{VerificationStr2});
152
153   $content{'Reference_No'} ||= join('', map $alpha[int(rand(62))], (1..20) );
154
155   #XXX this should be exposed as a standard B:OP field, not just recurring/no
156   if ( defined($content{'recurring_billing'})
157        && $content{'recurring_billing'} =~ /^[y1]/ ) {
158     $content{'Ecommerce_Flag'} = '2';
159   } else {
160     #$content{'Ecommerce_Flag'} = '1'; 7?  if there's an IP?
161   }
162
163   my $base_uri;
164   if ( $self->test_transaction ) {
165     $base_uri =
166       'https://api.demo.globalgatewaye4.firstdata.com/transaction';
167   } else {
168     $base_uri =
169       'https://api.globalgatewaye4.firstdata.com/vplug-in/transaction';
170   }
171
172   my $proxy = "$base_uri/v11";
173   my $uri = "$base_uri/rpc-enc";
174
175   my %transaction = map { $_ => $content{$_} } (qw(
176     ExactID Password Transaction_Type DollarAmount Card_Number Transaction_Tag
177     Track1 Track2 Authorization_Num Expiry_Date CardHoldersName
178     VerificationStr1 VerificationStr2 CVD_Presence_Ind Reference_No ZipCode
179     Tax1Amount Tax1Number Tax2Amount Tax2Number Customer_Ref Reference_3
180     Language Client_IP Client_Email user_name Currency PartialRedemption
181     CAVV XID Ecommerce_Flag
182  ));
183    #TransarmorToken CardType EAN VirtualCard CardCost FraudSuspected
184    #CheckNumber CheckType BankAccountNumber BankRoutingNumber CustomerName
185    #CustomerIDType CustomerID
186
187   #my @opts = map { SOAP::Data->name($_)->value( $data{$_} ) }
188   #             keys %data;
189
190   my $result = SOAP::Lite
191                  ->proxy($proxy)
192
193                  ->default_ns($base_uri)
194                  ->uri($uri)
195
196                  ->on_action( sub { join '/', @_ } )
197                  #->on_action( sub { join '', @_ } )
198                  #->on_action(sub { qq("$_[0]") }) #? https://firstdata.zendesk.com/entries/407569-First-Data-Global-Gateway-e4-Web-Service-API-Sample-Code-Perl
199                  ->autotype(0)
200
201                  ->readable(1)
202
203                  ->ns($uri,'q1')
204                  ->SendAndCommit( SOAP::Data->name('Transaction')->value( \%transaction ) )
205
206                  ->result();
207
208   die Dumper($result);
209
210   die Dumper($result->result) if $result->fault;
211   #die $result->fault->faultstring if $result->fault;
212
213   die Dumper($result);
214
215   #$self->is_success
216   #$self->authorization
217   #$self->avs_code
218   #$self->error_message
219   #$self->result_code
220   ##$self->failure_status
221
222 }
223
224 1;
225
226 __END__
227
228 =head1 NAME
229
230 Business::OnlinePayment::FirstDataGlobalGateway - First Data Global Gateway e4 backend for Business::OnlinePayment
231
232 =head1 SYNOPSIS
233
234   use Business::OnlinePayment;
235
236   my $tx =
237     new Business::OnlinePayment( 'FirstDataGlobalGateway' );
238
239   $tx->content(
240       login          => 'TEST88', # ExactID
241       password       => 'TEST88', #password 
242
243       type           => 'CC',
244       action         => 'Normal Authorization',
245       amount         => '1.00',
246
247       first_name     => 'Tofu',
248       last_name      => 'Beast',
249       address        => '123 Anystreet',
250       city           => 'Anywhere',
251       state          => 'UT',
252       zip            => '84058',
253
254       card_number    => '4111111111111111',
255       expiration     => '09/20',
256       cvv2           => '124',
257
258       #optional
259       customer_ip    => '1.2.3.4',
260   );
261   $tx->submit();
262
263   if($tx->is_success()) {
264       print "Card processed successfully: ".$tx->authorization."\n";
265   } else {
266       print "Card was rejected: ".$tx->error_message."\n";
267   }
268
269 =head1 SUPPORTED TRANSACTION TYPES
270
271 =head2 CC, Visa, MasterCard, American Express, Discover
272
273 Content required: type, login, action, amount, card_number, expiration.
274
275 =head2 (NOT YET) Check
276
277 Content required: type, login, action, amount, name, account_number, routing_code.
278
279 =head1 DESCRIPTION
280
281 For detailed information see L<Business::OnlinePayment>.
282
283 =head1 METHODS AND FUNCTIONS
284
285 See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> or provide additional functions.  
286
287 =head2 result_code
288
289 Returns the response error code.
290
291 =head2 error_message
292
293 Returns the response error number.
294
295 =head2 action
296
297 The following actions are valid
298
299   Normal Authorization
300   Authorization Only
301   Post Authorization
302   Credit
303   Void
304
305 =head1 COMPATIBILITY
306
307 Business::OnlinePayment::FirstDataGlobalGateway uses the v11 version of the API
308 at this time.
309
310 =head1 AUTHORS
311
312 Ivan Kohler <ivan-firstdataglobalgateway@freeside.biz>
313
314 =head1 SEE ALSO
315
316 perl(1). L<Business::OnlinePayment>.
317
318 =cut
319