1 package Business::BatchPayment::Item;
5 use Moose::Util::TypeConstraints;
6 use MooseX::UndefTolerant;
11 Business::BatchPayment::Item
15 A Business::BatchPayment::Item represents a single payment request or
16 reply (approval or rejection). When submitting a batch, the merchant
17 system constructs B::BP::Item objects for each attempted payment in
18 the batch. Results downloaded from the gateway are returned as a
19 list of Items with the 'approved' field set to a true or false value.
21 =head1 REQUIRED ATTRIBUTES
27 "payment" or "credit". Most processors support only "payment".
28 "payment" is defined as "money transfer FROM the account identified in the
29 Item TO the account identified by the Processor object's login settings."
30 "credit" is the other direction.
34 enum 'Action' => [qw(payment credit)];
35 coerce 'Action', from 'Str', via { lc $_ };
46 "CC" or "ECHECK". Most processors will only support one or the other,
47 and if set on the Processor object, this is not required.
51 # are we okay with these names?
52 enum 'PaymentType' => [qw( CC ECHECK )];
53 has payment_type => ( is => 'rw', isa => 'PaymentType' );
57 the amount, as a decimal number. Required only in request
62 # perhaps we should apply roles that distinguish request and reply items?
63 # they have different required fields.
71 transaction identifier. Requests must provide this. It's a token of
72 some kind to be passed to the gateway and used to identify the reply.
73 For now it's required to be an integer. An invoice number would be
78 has tid => ( is => 'rw', isa => 'Int' );
82 =head1 OPTIONAL ATTRIBUTES
84 =head2 Customer Information
90 A customer number or other identifier, for the merchant's use.
104 =item address, address2, city, state, country, zip
106 Billing address fields. Credit card processors may use these (especially
107 zip) for authentication.
111 Customer phone number.
127 ) ] => ( is => 'rw', isa => 'Str', default => '' );
131 =head2 Transaction Information
137 The date requested for processing. This is meaningful only if the
138 processor allows different processing dates for items in the same
143 An invoice number, for your use.
145 =item recurring_billing
147 A flag indicating whether this is a "recurring" transaction. Different
148 processors interpret this differently, but the interface defines three kinds
151 N: non-recurring; the customer is not expected to make another payment using
152 the same card or bank account.
154 F: first use; the customer has never used this card/account before but is
155 expected to use it again.
157 S: subsequent; the customer has used the card/account in the past.
161 class_type 'DateTime';
162 coerce 'DateTime', from 'Int', via { DateTime->from_epoch($_) };
163 has process_date => ( is => 'rw', isa => 'DateTime', coerce => 1 );
165 has invoice_number => ( is => 'rw', isa => 'Str' );
167 enum 'Recurring_Flag' => [ 'N', 'F', 'S' ];
168 has recurring_billing => ( is => 'rw', isa => 'Recurring_Flag' );
172 =head2 Bank Transfer / ACH / EFT
186 Can be 'personal checking', 'personal savings', 'business checking',
187 or 'business savings'.
191 enum 'Account_Type' => [
197 coerce 'Account_Type', from 'Str', via { lc $_ };
199 has account_number => ( is => 'rw', isa => 'Str' );
200 has routing_code => ( is => 'rw', isa => 'Str' );
201 has account_type => ( is => 'rw', isa => 'Account_Type', coerce => 1 );
215 Credit card expiration, MMYY format.
219 has card_number => ( is => 'rw', isa => 'Str' );
220 has ['expiration_month', 'expiration_year'] => ( is => 'rw', isa => 'Int' );
223 # gets/sets expiration_month and _year in MMYY format
227 # well, we said it's in MMYY format
228 my ($m, $y) = _parse_expiration($arg);
229 $self->expiration_month($m);
230 $self->expiration_year($y);
232 return sprintf('%02d/%02d',
233 $self->expiration_month,
234 $self->expiration_year % 2000);
237 sub _parse_expiration {
239 if ( $arg =~ /^(\d\d)(\d\d)$/ ) {
240 return ($1, 2000 + $2);
241 } elsif ( $arg =~ /^(\d\d?)\W(\d\d)$/ ) {
242 return ($1, 2000 + $2);
243 } elsif ( $arg =~ /^(\d\d?)\W(\d\d\d\d)$/ ) {
245 } elsif ( $arg =~ /^(\d\d?)\W\d\d?\W(\d\d\d\d)$/) {
248 die "can't parse expiration date '$arg'";
253 # gets/sets either the card number, or the account number + routing code
254 # depending on the payment type
256 if ( $self->payment_type eq 'CC' ) {
257 $self->card_number(@_);
258 } elsif ( $self->payment_type eq 'ECHECK' ) {
261 $arg =~ /^(\d+)@(\d+)$/ or die "Validation failed for payinfo";
262 $self->account_number($1);
263 $self->routing_code($2);
265 return ($self->account_number . '@' . $self->routing_code);
271 =head2 Tokenized Payment
277 If your gateway supports it, this may be
278 provided instead of card_number/account_number. See also
279 C<assigned_token> below.
283 has pay_by_token => ( is => 'rw', isa => 'Str' );
287 =head1 REPLY ATTRIBUTES
293 Boolean field for whether the item was approved. This
294 will always be set on replies.
298 The date the payment was processed, as a DateTime
303 The transaction identifier returned by the gateway
304 (not to be confused with 'tid', which is a transaction identifier assigned
305 by the merchant system). This is usually the identifier for performing
306 other operations on the transaction, like voiding or refunding it.
310 The authorization code, probably only meaningful for credit cards.
311 Should be undef (or not present) if the transaction wasn't approved.
315 The check number, probably only meaningful if this transaction was
316 processed from a paper check.
320 In tokenized systems which store the customer's account number or
321 credit card for future transactions, this is the token assigned to
322 identify that account. Pass it as 'pay_by_token' to use that payment
327 The message returned by the gateway. This may contain a value even
328 if the payment was successful (use C<approved> to determine that.)
332 A normalized failure status, from the following list:
338 =item nsf (non-sufficient funds / credit limit)
348 =item decline (other card/transaction declines)
356 has approved => ( is => 'rw', isa => 'Maybe[Bool]' );
358 has payment_date => ( is => 'rw', isa => 'DateTime' );
365 )] => ( is => 'rw', isa => 'Str');
367 enum FailureStatus => [ qw(
376 has failure_status => ( is => 'rw', isa => 'Maybe[FailureStatus]' );
378 has check_number => ( is => 'rw', isa => 'Int' );
380 around 'BUILDARGS' => sub {
381 my ($orig, $self, %args) = @_;
382 if ( $args{expiration} ) {
383 @args{'expiration_month', 'expiration_year'} =
384 _parse_expiration($args{expiration});
389 __PACKAGE__->meta->make_immutable;