File Coverage

blib/lib/Business/CyberSource/RequestPart/Card.pm
Criterion Covered Total %
statement 53 54 98.1
branch 12 24 50.0
condition n/a
subroutine 14 14 100.0
pod n/a
total 79 92 85.8


line stmt bran cond sub pod time code
1             package Business::CyberSource::RequestPart::Card;
2 6     6   18007 use 5.010;
  6         21  
  6         249  
3 6     6   31 use strict;
  6         11  
  6         221  
4 6     6   31 use warnings;
  6         11  
  6         189  
5 6     6   488 use namespace::autoclean;
  6         1198  
  6         77  
6              
7             our $VERSION = '0.010006'; # VERSION
8              
9 6     6   1094 use Moose;
  6         241066  
  6         71  
10             extends 'Business::CyberSource::MessagePart';
11             with 'MooseX::RemoteHelper::CompositeSerialization';
12              
13 6     6   40114 use MooseX::Aliases;
  6         2460  
  6         54  
14              
15 6     6   112500 use MooseX::Types::CyberSource qw( CvIndicator CardTypeCode );
  6         18  
  6         63  
16 6     6   56746 use MooseX::Types::Common::String qw( NonEmptySimpleStr );
  6         16  
  6         57  
17 6         55 use MooseX::Types::CreditCard 0.002 qw(
18             CardNumber
19             CardSecurityCode
20             CardExpiration
21 6     6   21681 );
  6         184731  
22              
23 6     6   12441 use Module::Runtime qw( use_module );
  6         137  
  6         47  
24              
25             our @CARP_NOT = ( __PACKAGE__, qw( Class::MOP::Method::Wrapped ) );
26              
27             sub _build_type {
28 10     10   20 my $self = shift;
29              
30 10         40 use_module('Business::CreditCard');
31 10         648 my $ct = Business::CreditCard::cardtype( $self->account_number );
32              
33 10 50       319 die ## no critic ( ErrorHandling::RequireCarping )
34             use_module('Business::CyberSource::Exception::NotACreditCard')->new
35             if $ct =~ /not a credit card/ixms
36             ;
37              
38 10         49 $ct =~ s/[\s]card//xms;
39              
40 10         384 return uc $ct;
41             }
42              
43             sub _build_expired {
44 3     3   6 my $self = shift;
45 3         11 use_module('DateTime');
46              
47 3         80 return $self->_compare_date_against_expiration( DateTime->now );
48             }
49              
50             sub _compare_date_against_expiration {
51 15     15   6585 my ( $self, $date ) = @_;
52              
53 15         607 my $exp = $self->expiration->clone;
54             # add 2 days so that we allow for a 24 hour period where
55             # the card could be expired at UTC but not the issuer
56 15         176 $exp->add( days => 1 );
57              
58 15         7685 use_module('DateTime');
59 15         365 my $cmp = DateTime->compare( $date, $exp );
60              
61 15 100       1044 if ( $cmp == -1 ) { # current date is before than the expiration date
    100          
    50          
62 6         150 return 0;
63             }
64             elsif ( $cmp == 0 ) { # expiration equal to current date
65 3         19 return 0;
66             }
67             elsif ( $cmp == 1 ) { # current date is past the expiration date
68 6         32 return 1;
69             }
70 0         0 return; # da F*? should never hit this
71             }
72              
73             sub _build_card_type_code {
74 9     9   16474 my $self = shift;
75              
76 9 0       338 my $code
    0          
    0          
    0          
    50          
    100          
    100          
77             = $self->type =~ /visa /ixms ? '001'
78             : $self->type =~ /mastercard /ixms ? '002'
79             : $self->type =~ /discover /ixms ? '004'
80             : $self->type =~ /jcb /ixms ? '007'
81             : $self->type =~ /enroute /ixms ? '014'
82             : $self->type =~ /laser /ixms ? '035'
83             : $self->type =~ /american\ express/ixms ? '003'
84             : undef
85             ;
86              
87 9         22 my $exception_ns = 'Business::CyberSource::Exception::';
88 9 50       28 die ## no critic ( ErrorHandling::RequireCarping )
89             use_module( $exception_ns . 'UnableToDetectCardTypeCode')
90             ->new( type => $self->type) unless $code;
91              
92 9         133 return $code;
93             }
94              
95             has account_number => (
96             isa => CardNumber,
97             remote_name => 'accountNumber',
98             alias => [ qw( credit_card_number card_number ) ],
99             required => 1,
100             is => 'ro',
101             coerce => 1,
102             trigger => sub { shift->type },
103             );
104              
105             has type => (
106             isa => 'Str',
107             lazy => 1,
108             is => 'ro',
109             builder => '_build_type',
110             );
111              
112             has expiration => (
113             isa => CardExpiration,
114             required => 1,
115             is => 'ro',
116             coerce => 1,
117             handles => [ qw( month year ) ],
118             );
119              
120             has is_expired => (
121             isa => 'Bool',
122             builder => '_build_expired',
123             lazy => 1,
124             is => 'ro',
125             );
126              
127             has security_code => (
128             isa => CardSecurityCode,
129             remote_name => 'cvNumber',
130             alias => [ qw( cvn cvv cvv2 cvc2 cid ) ],
131             predicate => 'has_security_code',
132             traits => [ 'SetOnce' ],
133             is => 'rw',
134             );
135              
136             has holder => (
137             isa => NonEmptySimpleStr,
138             remote_name => 'fullName',
139             alias => [ qw( name full_name card_holder ) ],
140             predicate => 'has_holder',
141             traits => [ 'SetOnce' ],
142             is => 'rw',
143             );
144              
145             has card_type_code => (
146             isa => CardTypeCode,
147             remote_name => 'cardType',
148             lazy => 1,
149             is => 'ro',
150             builder => '_build_card_type_code',
151             );
152              
153             has cv_indicator => (
154             isa => CvIndicator,
155             remote_name => 'cvIndicator',
156             lazy => 1,
157             predicate => 'has_cv_indicator',
158             traits => [ 'SetOnce' ],
159             is => 'rw',
160             default => sub { $_[0]->has_security_code ? 1 : 0 },
161             );
162              
163             has _expiration_month => (
164             remote_name => 'expirationMonth',
165             isa => 'Int',
166             is => 'ro',
167             lazy => 1,
168             reader => undef,
169             writer => undef,
170             init_arg => undef,
171             default => sub { $_[0]->expiration->month },
172             );
173              
174             has _expiration_year => (
175             remote_name => 'expirationYear',
176             isa => 'Int',
177             is => 'ro',
178             lazy => 1,
179             reader => undef,
180             writer => undef,
181             init_arg => undef,
182             default => sub { $_[0]->expiration->year },
183             );
184              
185             my @deprecated = ( qw(
186             credit_card_number
187             card_number
188             cvn cvv cvv2
189             cvc2 cid name
190             full_name
191             card_holder
192             ));
193              
194             around BUILDARGS => sub {
195             my $orig = shift;
196             my $self = shift;
197              
198             my $args = $self->$orig( @_ );
199              
200             foreach my $attr (@deprecated ) {
201             if ( exists $args->{$attr} ) {
202             warnings::warnif('deprecated', # this is due to Moose::Exception conflict
203             "$attr deprecated check the perldoc for the actual attribute"
204             );
205             }
206             }
207              
208             return $args;
209             };
210              
211             foreach my $attr ( @deprecated ) {
212             my $deprecated = sub {
213             warnings::warnif('deprecated', # this is due to Moose::Exception conflict
214             "$attr deprecated check the perldoc for the actual attribute"
215             );
216             };
217              
218             before( $attr, $deprecated );
219             }
220             __PACKAGE__->meta->make_immutable;
221             1;
222             # ABSTRACT: Credit Card Helper Class
223              
224             __END__
225              
226             =pod
227              
228             =encoding UTF-8
229              
230             =head1 NAME
231              
232             Business::CyberSource::RequestPart::Card - Credit Card Helper Class
233              
234             =head1 VERSION
235              
236             version 0.010006
237              
238             =head1 EXTENDS
239              
240             L<Business::CyberSource::MessagePart>
241              
242             =head1 ATTRIBUTES
243              
244             =head2 account_number
245              
246             This is the Credit Card Number
247              
248             =head2 expiration
249              
250             my $card = Business::CyberSource::RequestPart::Card->new({
251             account_number => '4111111111111111',
252             expiration => {
253             year => '2025',
254             month => '04',
255             },
256             });
257              
258             A DateTime object, you should construct it by passing a hashref with keys for
259             month, and year, it will actually contain the last day of that month/year. You
260             can pass a L<DateTime> object, as long as it was built using the
261             L<last_day_of_month|DateTime/"DateTime-last_day_of_month-...-"> factory method.
262              
263             =head2 security_code
264              
265             The 3 digit security number on the back of the card.
266              
267             =head2 holder
268              
269             The full name of the card holder as printed on the card.
270              
271             =head2 is_expired
272              
273             Boolean, returns true if the card is older than
274             L<expiration date|/"expiration"> plus one day. This is done to compensate for
275             unknown issuer time zones as we can't be sure that all issuers shut cards of on
276             the first of every month UTC. In fact I have been told that some issuers will
277             allow renewed cards to be run with expired dates. Use this at your discretion.
278              
279             =head2 cv_indicator
280              
281             Boolean, true if the L<security code|/"security_code"> was passed.
282              
283             =head2 type
284              
285             The card issuer, e.g. VISA, MasterCard. it is generated from the card number.
286              
287             =head2 card_type_code
288              
289             Type of card to authorize. This should be auto detected, but if it's not you
290             can specify the value manually.
291              
292             Possible values:
293              
294             =over
295              
296             =item 001: Visa
297              
298             =item 002: MasterCard, Eurocard*
299              
300             European regional brand of MasterCard
301              
302             =item 003: American Express
303              
304             =item 004: Discover
305              
306             =item 005: Diners Club
307              
308             see Discover Acquisitions and Alliances.
309              
310             =item 006: Carte Blanche*
311              
312             =item 007: JCB*
313              
314             =item 014: EnRoute*
315              
316             =item 021: JAL*
317              
318             =item 024: Maestro (UK Domestic)*
319              
320             =item 031: Delta*
321              
322             use this value only for Global Collect. For other processors, use
323             001 for all Visa card types.
324              
325             =item 033: Visa Electron*
326              
327             =item 034: Dankort*
328              
329             =item 035: Laser*
330              
331             =item 036: Carte Bleue*
332              
333             =item 037: Carta Si*
334              
335             =item 039: Encoded account number*
336              
337             =item 040: UATP*
338              
339             =item 042: Maestro (International)*
340              
341             =item 043: Santander card*
342              
343             before setting up your system to work with Santander
344             cards, contact the CyberSource UK Support Group.
345              
346             =back
347              
348             =head1 BUGS
349              
350             Please report any bugs or feature requests on the bugtracker website
351             https://github.com/xenoterracide/business-cybersource/issues
352              
353             When submitting a bug or request, please include a test-file or a
354             patch to an existing test-file that illustrates the bug or desired
355             feature.
356              
357             =head1 AUTHOR
358              
359             Caleb Cushing <xenoterracide@gmail.com>
360              
361             =head1 COPYRIGHT AND LICENSE
362              
363             This software is Copyright (c) 2015 by Caleb Cushing <xenoterracide@gmail.com>.
364              
365             This is free software, licensed under:
366              
367             The Artistic License 2.0 (GPL Compatible)
368              
369             =cut