1 package Business::OnlinePayment::FirstDataGlobalGateway;
2 use base qw( Business::OnlinePayment );
7 use Business::CreditCard;
8 use SOAP::Lite; #+trace => 'all';
9 #SOAP::Lite->import(+trace=>'debug');
11 our $VERSION = '0.01';
12 $VERSION = eval $VERSION; # modperlstyle: convert the string into a number
14 our @alpha = ( 'a'..'z', 'A'..'Z', '0'..'9' );
16 our %failure_status = (
20 503 => 'stolen', # "Fraud/Security violation"
29 # others are all "declined"
35 'info_compat' => '0.01',
36 'gateway_name' => 'First Data Global Gateway e4',
37 'gateway_url' => 'https://www.firstdata.com/en_us/products/merchants/ecommerce/online-payment-processing.html',
38 'module_version' => $VERSION,
39 'supported_types' => [ 'CC' ], #, 'ECHECK' ],
40 #'token_support' => 1, # "Transarmor" is this, but not implemented yet
41 'test_transaction' => 1,
43 'supported_actions' => [ 'Normal Authorization',
56 $self->build_subs(qw( order_number avs_code cvv2_response
57 authorization failure_status result_code
64 my %content = $self->content();
67 my %types = ( 'visa' => 'CC',
69 'american express' => 'CC',
73 $content{'type'} = $types{lc($content{'type'})} || $content{'type'};
74 $self->transaction_type($content{'type'});
77 my $action = lc($content{'action'});
79 ( 'normal authorization' => '00', # Purchase
80 'authorization_only' => '01', #
81 'post authorization' => '02', # Pre-Authorization Completion
82 # '' => '03', # Forced Post
83 'credit' => '04', # Refund
84 # '' => '05', # Pre-Authorization Only
85 'void' => '13', # Void
86 #'reverse authorization' => '',
88 # '' => '07', # PayPal Order
89 # '' => '32', # Tagged Pre-Authorization Completion
90 # '' => '33', # Tagged Void
91 # '' => '34', # Tagged Refund
92 # '' => '83', # CashOut (ValueLink, v9 or higher end point only)
93 # '' => '85', # Activation (ValueLink, v9 or higher end point only)
94 # '' => '86', # Balance Inquiry (ValueLink, v9 or higher end point only)
95 # '' => '88', # Reload (ValueLink, v9 or higher end point only)
96 # '' => '89', # Deactivation (ValueLink, v9 or higher end point only)
99 $content{'action'} = $actions{$action} || $action;
101 # make sure there's a combined name
102 $content{name} ||= $content{first_name} . ' ' . $content{last_name};
104 # stuff it back into %content
105 $self->content(%content);
112 my %content = $self->content();
114 $content{$map{$_}} = $content{$_};
116 $self->content(%content);
125 'login' => 'ExactID',
126 'password' => 'Password',
128 'action' => 'Transaction_Type',
130 'amount' => 'DollarAmount',
131 'currency' => 'Currency',
132 'card_number' => 'Card_Number',
133 'track1' => 'Track1',
134 'track2' => 'Track2',
135 'expiration' => 'Expiry_Date',
136 'name' => 'CardHoldersName',
137 'cvv2' => 'VerificationStr2',
139 'authorization' => 'Authorization_Num',
140 'order_number' => 'Reference_No',
143 'tax' => 'Tax1Amount',
144 'customer_id' => 'Customer_Ref',
145 'customer_ip' => 'Client_IP',
146 'email' => 'Client_Email',
148 #account_type => 'accountType',
152 my %content = $self->content();
154 $content{Expiry_Date} =~ s/\///;
156 $content{country} ||= 'US';
158 $content{VerificationStr1} =
159 join('|', map $content{$_}, qw( address zip city state country ));
160 $content{VerificationStr1} .= '|'. $content{'phone'}
161 if $content{'type'} eq 'ECHECK';
163 $content{CVD_Presence_Ind} = '1' if length($content{VerificationStr2});
165 $content{'Reference_No'} ||= join('', map $alpha[int(rand(62))], (1..20) );
167 #XXX this should be exposed as a standard B:OP field, not just recurring/no
168 if ( defined($content{'recurring_billing'})
169 && $content{'recurring_billing'} =~ /^[y1]/ ) {
170 $content{'Ecommerce_Flag'} = '2';
172 #$content{'Ecommerce_Flag'} = '1'; 7? if there's an IP?
176 if ( $self->test_transaction ) {
178 'https://api.demo.globalgatewaye4.firstdata.com/transaction';
181 'https://api.globalgatewaye4.firstdata.com/vplug-in/transaction';
184 my $proxy = "$base_uri/v11";
186 my @transaction = map { SOAP::Data->name($_)->value( $content{$_} ) }
187 grep { defined($content{$_}) }
189 ExactID Password Transaction_Type DollarAmount Card_Number Transaction_Tag
190 Track1 Track2 Authorization_Num Expiry_Date CardHoldersName
191 VerificationStr1 VerificationStr2 CVD_Presence_Ind Reference_No ZipCode
192 Tax1Amount Tax1Number Tax2Amount Tax2Number Customer_Ref Reference_3
193 Language Client_IP Client_Email User_Name Currency PartialRedemption
194 CAVV XID Ecommerce_Flag
196 #TransarmorToken CardType EAN VirtualCard CardCost FraudSuspected
197 #CheckNumber CheckType BankAccountNumber BankRoutingNumber CustomerName
198 #CustomerIDType CustomerID
200 my $wsdl = "$proxy/wsdl";
201 my $client = SOAP::Lite->service($wsdl)->proxy($proxy)->readable(1);
202 my $action_prefix = 'http://secure2.e-xact.com/vplug-in/transaction/rpc-enc';
203 my $type_prefix = $action_prefix . '/encodedTypes';
204 $client->on_action( sub { $action_prefix . '/' . $_[1] } );
205 my $source = SOAP::Data->name('SendAndCommitSource')
206 ->value(\@transaction)
207 ->type("$type_prefix:Transaction");
209 my $som = eval { $client->call('SendAndCommit', $source) };
211 if ($som->fault) { # indicates a protocol error
212 die $som->faultstring;
216 $som->match('/Envelope/Body/SendAndCommitResponse/SendAndCommitResult');
217 my $result = $som->valueof; # hashref of the result properties
218 $self->is_success( $result->{Transaction_Approved} );
219 $self->authorization( $result->{Authorization_Num} );
220 $self->order_number( $result->{SequenceNo} );
221 $self->avs_code( $result->{AVS} );
222 $self->cvv2_response( $result->{CVV2} );
224 if (!$self->is_success) {
225 # note spelling of "EXact_Resp_Code"
226 if ($result->{EXact_Resp_Code} ne '00') {
227 # then there's something wrong with the transaction inputs
228 # (invalid card number, malformed amount, attempt to refund a
229 # transaction that didn't happen, etc.)
230 $self->error_message($result->{EXact_Message});
231 $self->result_code($result->{EXact_Resp_Code});
232 $self->failure_status('');
233 # not a decline, as the transaction was never really detected
235 $self->error_message($result->{Bank_Message});
236 $self->result_code($result->{Bank_Resp_Code});
237 $self->failure_status(
238 $failure_status{$result->{Bank_Resp_Code}} || 'declined'
250 Business::OnlinePayment::FirstDataGlobalGateway - First Data Global Gateway e4 backend for Business::OnlinePayment
254 use Business::OnlinePayment;
257 new Business::OnlinePayment( 'FirstDataGlobalGateway' );
260 login => 'TEST88', # ExactID
261 password => 'TEST88', #password
264 action => 'Normal Authorization',
267 first_name => 'Tofu',
268 last_name => 'Beast',
269 address => '123 Anystreet',
274 card_number => '4111111111111111',
275 expiration => '09/20',
279 customer_ip => '1.2.3.4',
283 if($tx->is_success()) {
284 print "Card processed successfully: ".$tx->authorization."\n";
286 print "Card was rejected: ".$tx->error_message."\n";
289 =head1 SUPPORTED TRANSACTION TYPES
291 =head2 CC, Visa, MasterCard, American Express, Discover
293 Content required: type, login, action, amount, card_number, expiration.
295 =head2 (NOT YET) Check
297 Content required: type, login, action, amount, name, account_number, routing_code.
301 For detailed information see L<Business::OnlinePayment>.
303 =head1 METHODS AND FUNCTIONS
305 See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> or provide additional functions.
309 Returns the response error code.
313 Returns the response error number.
317 The following actions are valid
327 Business::OnlinePayment::FirstDataGlobalGateway uses the v11 version of the API
332 Ivan Kohler <ivan-firstdataglobalgateway@freeside.biz>
336 perl(1). L<Business::OnlinePayment>.