File Coverage

blib/lib/Business/OnlinePayment/PaperlessTrans.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::PaperlessTrans;
2 1     1   890 use 5.008;
  1         4  
  1         75  
3 1     1   6 use strict;
  1         1  
  1         35  
4 1     1   156 use warnings;
  1         2  
  1         59  
5              
6             our $VERSION = '0.001006'; # VERSION
7              
8 1     1   907 use parent 'Business::OnlinePayment';
  1         310  
  1         7  
9              
10 1     1   556 use XML::Compile::WSDL11;
  0            
  0            
11             use XML::Compile::SOAP11;
12             use XML::Compile::Transport::SOAPHTTP;
13             use Module::Load qw( load );
14             use File::ShareDir qw( dist_file );
15             use Carp qw( carp confess );
16              
17             my $dist = 'Business-OnlinePayment-PaperlessTrans';
18             my $ns = 'Business::PaperlessTrans::';
19              
20             sub submit { ## no critic ( ProhibitExcessComplexity )
21             my ( $self ) = @_;
22              
23             $self->required_fields(qw( amount currency login password ));
24              
25             my %content = $self->content;
26             my $action = lc $content{action};
27             my $trans_t = lc $self->transaction_type;
28             my $token = $self->_content_to_token( %content );
29             my $ident = $self->_content_to_ident( %content );
30             my $address = $self->_content_to_address( %content );
31              
32             $self->{debug} = $content{debug} ? $content{debug} : 0;
33              
34             my %args = (
35             Amount => $content{amount},
36             Currency => $content{currency},
37             Token => $token,
38             TestMode => $self->test_transaction ? 'true' : 'false',
39             CustomFields => {},
40             );
41              
42             my %payment_content = (
43             %content,
44             identification => $ident,
45             address => $address,
46             );
47              
48             if ( $trans_t eq 'cc' ) {
49             $args{CardPresent} = $content{track1} ? 1 : 0;
50             $args{Card} = $self->_content_to_card( %payment_content );
51             }
52             elsif ( $trans_t eq 'echeck' ) {
53             $args{CheckNumber} = $content{check_number};
54             $args{Check} = $self->_content_to_check( %payment_content );
55             }
56              
57             ## determine appropriate request class
58             my $type;
59             if ( $action eq 'authorization only' && $trans_t eq 'cc' ){
60             $type = 'AuthorizeCard';
61             }
62             elsif ( $action eq 'normal authorization' && $trans_t eq 'cc' ) {
63             $type = 'ProcessCard';
64             }
65             elsif ( $action eq 'normal authorization' && $trans_t eq 'echeck' ) {
66             $type = 'ProcessACH';
67             }
68              
69             my $response = $self->_transmit( \%args, $type );
70              
71             # code != 0 is a transmission error
72             if ( $response->{ResponseCode} == 0 ) {
73             # in future should consider making thse the same api?
74             if ( _bool( $response->{IsApproved} )
75             || _bool( $response->{IsAccepted} )
76             ) {
77             $self->is_success(1);
78             }
79             else {
80             $self->is_success(0);
81             $self->error_message( $response->{Message} );
82             }
83             }
84             else {
85             confess $response->{Message};
86             }
87              
88             $self->authorization( $response->{Authorization} );
89              
90             $self->order_number( $response->{TransactionID} );
91              
92             return;
93             }
94              
95             sub _bool {
96             my $val = shift;
97              
98             return 1 if defined $val && lc $val eq 'true';
99             return 0;
100             }
101              
102             sub _transmit {
103             my ( $self, $request, $type ) = @_;
104              
105             my %request = ( req => $request );
106              
107             if ($self->{debug} >= 1 ) {
108             load 'Data::Dumper', 'Dumper';
109             carp Dumper( \%request );
110             }
111              
112             my ( $answer, $trace ) = $self->_get_call( $type )->( %request );
113              
114             carp "REQUEST >\n" . $trace->request->as_string if $self->{debug} > 1;
115             carp "RESPONSE <\n" . $trace->response->as_string if $self->{debug} > 1;
116              
117             if ( $self->{debug} >= 1 ) {
118             carp Dumper( $answer );
119             }
120              
121             return $answer->{parameters}{$type . 'Result'};
122             }
123              
124             sub _get_call {
125             my ( $self, $type ) = @_;
126              
127             return $self->{calls}{$type} if defined $self->{calls}{$type};
128              
129             $self->_build_calls;
130              
131             return $self->_get_call( $type );
132             }
133              
134             sub _build_calls {
135             my $self = shift;
136              
137             my @calls = qw( AuthorizeCard ProcessCard ProcessACH );
138              
139             my %calls;
140             foreach my $call ( @calls ) {
141             $calls{$call} = $self->_wsdl->compileClient( $call );
142             }
143             $self->{calls} = \%calls;
144              
145             return;
146             }
147              
148             sub _wsdl {
149             my $self = shift;
150              
151             my $wsdl
152             = XML::Compile::WSDL11->new(
153             dist_file( $dist, 'svc.paperlesstrans.wsdl')
154             );
155              
156             foreach my $xsd ( $self->_list_xsd_files ) {
157             $wsdl->importDefinitions( $xsd );
158             }
159              
160             return $wsdl;
161             }
162              
163             sub _list_xsd_files {
164             my @xsd;
165             foreach my $i ( 0..6 ) {
166             push @xsd, dist_file( $dist, "svc.paperlesstrans.$i.xsd");
167             }
168             return @xsd;
169             }
170              
171             sub _content_to_ident {
172             my ( $self, %content ) = @_;
173              
174             return unless $content{license_num};
175              
176             my %mapped = (
177             IDType => 1, # B:OP 3.02 there is only drivers license
178             Number => $content{license_num},
179             );
180              
181             return \%mapped;
182             }
183              
184             sub _content_to_token {
185             my ( $self, %content ) = @_;
186              
187             my %mapped = (
188             TerminalID => $content{login},
189             TerminalKey => $content{password},
190             );
191              
192             return \%mapped;
193             }
194              
195             sub _content_to_address {
196             my ( $self, %content ) = @_;
197              
198             my %mapped = (
199             Street => $content{address},
200             City => $content{city},
201             State => $content{state},
202             Zip => $content{zip},
203             Country => $content{country},
204             );
205              
206             return \%mapped;
207             }
208              
209             sub _content_to_check {
210             my ( $self, %content ) = @_;
211              
212             $self->required_fields(qw( routing_code account_number account_name ));
213              
214             my %mapped = (
215             NameOnAccount => $content{account_name},
216             AccountNumber => $content{account_number},
217             RoutingNumber => $content{routing_code},
218             Identification => $content{identification} || {},
219             Address => $content{address},
220             EmailAddress => $content{email},
221             );
222              
223             return \%mapped;
224             }
225              
226             sub _content_to_card {
227             my ( $self, %content ) = @_;
228              
229             $self->required_fields(qw( expiration name card_number ));
230             # expiration api is bad but conforms to Business::OnlinePayment 3.02 Spec
231             $content{expiration} =~ m/^(\d\d)(\d\d)$/xms;
232             my ( $exp_month, $exp_year ) = ( $1, $2 ); ## no critic ( ProhibitCaptureWithoutTest )
233              
234             my %mapped = (
235             NameOnAccount => $content{name},
236             CardNumber => $content{card_number},
237             SecurityCode => $content{cvv2},
238             Identification => $content{identification} || {},
239             Address => $content{address},
240             EmailAddress => $content{email},
241             ExpirationMonth => $exp_month,
242             ExpirationYear => '20' . $exp_year,
243             );
244              
245             $mapped{TrackData} = $content{track1} . $content{track2}
246             if $content{track1} && $content{track2};
247              
248             return \%mapped;
249             }
250              
251             1;
252              
253             # ABSTRACT: Interface to Paperless Transaction Corporation BackOffice API
254              
255             __END__