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 $base_uri = 'https://api.demo.globalgatewaye4.firstdata.com/transaction';
178 my $proxy = "$base_uri/v11";
180 my @transaction = map { SOAP::Data->name($_)->value( $content{$_} ) }
181 grep { defined($content{$_}) }
183 ExactID Password Transaction_Type DollarAmount Card_Number Transaction_Tag
184 Track1 Track2 Authorization_Num Expiry_Date CardHoldersName
185 VerificationStr1 VerificationStr2 CVD_Presence_Ind Reference_No ZipCode
186 Tax1Amount Tax1Number Tax2Amount Tax2Number Customer_Ref Reference_3
187 Language Client_IP Client_Email User_Name Currency PartialRedemption
188 CAVV XID Ecommerce_Flag
190 #TransarmorToken CardType EAN VirtualCard CardCost FraudSuspected
191 #CheckNumber CheckType BankAccountNumber BankRoutingNumber CustomerName
192 #CustomerIDType CustomerID
194 my $wsdl = "$proxy/wsdl";
195 my $client = SOAP::Lite->service($wsdl)->proxy($proxy)->readable(1);
196 my $action_prefix = 'http://secure2.e-xact.com/vplug-in/transaction/rpc-enc';
197 my $type_prefix = $action_prefix . '/encodedTypes';
198 $client->on_action( sub { $action_prefix . '/' . $_[1] } );
199 my $source = SOAP::Data->name('SendAndCommitSource')
200 ->value(\@transaction)
201 ->type("$type_prefix:Transaction");
203 my $som = eval { $client->call('SendAndCommit', $source) };
205 if ($som->fault) { # indicates a protocol error
206 die $som->faultstring;
210 $som->match('/Envelope/Body/SendAndCommitResponse/SendAndCommitResult');
211 my $result = $som->valueof; # hashref of the result properties
212 $self->is_success( $result->{Transaction_Approved} );
213 $self->authorization( $result->{Authorization_Num} );
214 $self->order_number( $result->{SequenceNo} );
215 $self->avs_code( $result->{AVS} );
216 $self->cvv2_response( $result->{CVV2} );
218 if (!$self->is_success) {
219 # note spelling of "EXact_Resp_Code"
220 if ($result->{EXact_Resp_Code} ne '00') {
221 # then there's something wrong with the transaction inputs
222 # (invalid card number, malformed amount, attempt to refund a
223 # transaction that didn't happen, etc.)
224 $self->error_message($result->{EXact_Message});
225 $self->result_code($result->{EXact_Resp_Code});
226 $self->failure_status('');
227 # not a decline, as the transaction was never really detected
229 $self->error_message($result->{Bank_Message});
230 $self->result_code($result->{Bank_Resp_Code});
231 $self->failure_status(
232 $failure_status{$result->{Bank_Resp_Code}} || 'declined'
244 Business::OnlinePayment::FirstDataGlobalGateway - First Data Global Gateway e4 backend for Business::OnlinePayment
248 use Business::OnlinePayment;
251 new Business::OnlinePayment( 'FirstDataGlobalGateway' );
254 login => 'TEST88', # ExactID
255 password => 'TEST88', #password
258 action => 'Normal Authorization',
261 first_name => 'Tofu',
262 last_name => 'Beast',
263 address => '123 Anystreet',
268 card_number => '4111111111111111',
269 expiration => '09/20',
273 customer_ip => '1.2.3.4',
277 if($tx->is_success()) {
278 print "Card processed successfully: ".$tx->authorization."\n";
280 print "Card was rejected: ".$tx->error_message."\n";
283 =head1 SUPPORTED TRANSACTION TYPES
285 =head2 CC, Visa, MasterCard, American Express, Discover
287 Content required: type, login, action, amount, card_number, expiration.
289 =head2 (NOT YET) Check
291 Content required: type, login, action, amount, name, account_number, routing_code.
295 For detailed information see L<Business::OnlinePayment>.
297 =head1 METHODS AND FUNCTIONS
299 See L<Business::OnlinePayment> for the complete list. The following methods either override the methods in L<Business::OnlinePayment> or provide additional functions.
303 Returns the response error code.
307 Returns the response error number.
311 The following actions are valid
321 Business::OnlinePayment::FirstDataGlobalGateway uses the v11 version of the API
326 Ivan Kohler <ivan-firstdataglobalgateway@freeside.biz>
330 perl(1). L<Business::OnlinePayment>.