initial commit
authorIvan Kohler <ivan@freeside.biz>
Wed, 11 Jul 2012 07:08:15 +0000 (00:08 -0700)
committerIvan Kohler <ivan@freeside.biz>
Wed, 11 Jul 2012 07:08:15 +0000 (00:08 -0700)
19 files changed:
BatchPayment.pm [new file with mode: 0644]
BatchPayment/Batch.pm [new file with mode: 0644]
BatchPayment/Debug.pm [new file with mode: 0644]
BatchPayment/Item.pm [new file with mode: 0644]
BatchPayment/Processor.pm [new file with mode: 0644]
BatchPayment/TestMode.pm [new file with mode: 0644]
BatchPayment/Transport.pm [new file with mode: 0644]
BatchPayment/Transport/File.pm [new file with mode: 0644]
BatchPayment/Transport/HTTPS.pm [new file with mode: 0644]
BatchPayment/Transport/SFTP.pm [new file with mode: 0644]
Changes [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
META.yml [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
ignore.txt [new file with mode: 0644]
t/00-load.t [new file with mode: 0644]
t/manifest.t [new file with mode: 0644]
t/pod.t [new file with mode: 0644]

diff --git a/BatchPayment.pm b/BatchPayment.pm
new file mode 100644 (file)
index 0000000..86ee6b9
--- /dev/null
@@ -0,0 +1,113 @@
+package Business::BatchPayment;
+
+use 5.006;
+use strict;
+use vars '$DEBUG';
+use warnings;
+
+use Class::MOP;
+use Business::BatchPayment::Processor;
+use Business::BatchPayment::Item;
+use Business::BatchPayment::Transport;
+
+$DEBUG = 0;
+
+=head1 NAME
+
+Business::BatchPayment - Batch-oriented payment processing
+
+=head1 VERSION
+
+Version 0.01
+
+=cut
+
+our $VERSION = '0.01';
+
+=head1 SYNOPSIS
+
+  use Business::BatchPayment;
+
+  my %options = ( merchant_id => '00451', password => 'opensesame' );
+  my $processor = Business::BatchPayment->create(MyGateway => %options);
+
+  my @request;
+  push @request, Business::BatchPayment->create(Item =>
+    action        => 'payment', # as opposed to 'credit'
+    payment_type  => 'CC', #credit card, or 'ECHECK' for check/ACH
+    amount        => '49.95',
+    tid           => '0001234', # transaction id, like a customer/order number
+    card_number   => '1234123412341238',
+    expiration    => '0100', # MM/YY
+    # these fields are optional
+    first_name    => 'John',
+    last_name     => 'Doe',
+    address       => '123 Main Street',
+    address2      => 'Suite H',
+    city          => 'Anytown',
+    state         => 'CA',
+    country       => 'US',
+    zip           => '99015',
+  ); # returns a Business::OnlinePayment::Item;
+
+  $processor->submit(@request);
+
+  # at some point in the future
+  
+  my @reply = $processor->receive();
+  foreach my $item (@reply) {
+    ... process items and record successful/failed payments
+  }
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item create MODULE[, OPTIONS ]
+
+Loads Business::BatchPayment::MODULE, then attempts to call 
+Business::BatchPayment::MODULE->new(OPTIONS).
+
+=cut
+
+# not a Moose method
+
+sub create {
+  my $class = shift;
+  my $subclass = shift;
+  $subclass = "Business::BatchPayment::$subclass";
+  Class::MOP::load_class($subclass);
+  $subclass->new(@_);
+}
+
+=back
+
+=head1 AUTHOR
+
+Mark Wells, C<< <mark at freeside.biz> >>
+
+=head1 BUGS
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+    perldoc Business::BatchPayment
+
+Commercial support is available from Freeside Internet Services,
+L<http://www.freeside.biz>.
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright 2012 Mark Wells.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of either: the GNU General Public License as published
+by the Free Software Foundation; or the Artistic License.
+
+See http://dev.perl.org/licenses/ for more information.
+
+
+=cut
+
+1; # End of Business::BatchPayment
diff --git a/BatchPayment/Batch.pm b/BatchPayment/Batch.pm
new file mode 100644 (file)
index 0000000..eb6716e
--- /dev/null
@@ -0,0 +1,52 @@
+=head1 NAME
+
+Business::BatchPayment::Batch
+
+=head1 DESCRIPTION
+
+A Business::BatchPayment::Batch object represents a group of payment 
+requests or confirmations (L<Business::BatchPayment::Item>) submitted
+to a bank (or returned from a bank) as a batch.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item batch_id - Batch identifier.  The format is processor-specific
+but usually must be a positive integer, if it's used at all.
+
+Processor modules may include C<batch_id> in a reply batch ONLY if 
+it is guaranteed to match the batch_id of a request batch AND all 
+the items in the reply batch come from that request batch.  Otherwise, 
+C<batch_id> must be null.  It must always be null when using one-way
+(receive-only) workflow, since there are no request batches.
+
+=item items - An arrayref of L<Business::BatchPayment::Item> objects
+included in the batch.
+
+=back
+
+=cut
+
+package Business::BatchPayment::Batch;
+
+use Moose;
+
+has batch_id => (
+  is => 'rw',
+  isa => 'Str',
+  default => '',
+);
+
+has items => (
+  traits => ['Array'],
+  is => 'rw',
+  isa => 'ArrayRef[Business::BatchPayment::Item]',
+  handles =>  {
+    count     => 'count',
+    elements  => 'elements',
+    push      => 'push',
+  },
+);
+
+1;
diff --git a/BatchPayment/Debug.pm b/BatchPayment/Debug.pm
new file mode 100644 (file)
index 0000000..71a9e6d
--- /dev/null
@@ -0,0 +1,20 @@
+package Business::BatchPayment::Debug;
+
+=head1 DESCRIPTION
+
+The 'debug' attribute.  All Business::BatchPayment classes should support 
+this.  $Business::BatchPayment::DEBUG can be set to turn on debugging 
+globally.
+
+=cut
+
+use Moose::Role;
+
+has 'debug' => (
+  is => 'rw',
+  isa => 'Int',
+  default => sub { $Business::BatchPayment::DEBUG || 0 },
+  lazy => 1,
+);
+
+1;
diff --git a/BatchPayment/Item.pm b/BatchPayment/Item.pm
new file mode 100644 (file)
index 0000000..6e1e8e5
--- /dev/null
@@ -0,0 +1,273 @@
+package Business::BatchPayment::Item;
+
+use Moose;
+use Moose::Util::TypeConstraints;
+use MooseX::UndefTolerant;
+use DateTime;
+
+=head1 NAME
+
+Business::BatchPayment::Item
+
+=head1 DESCRIPTION
+
+A Business::BatchPayment::Item represents a single payment request or 
+reply (approval or rejection).  When submitting a batch, the merchant 
+system constructs B::BP::Item objects for each attempted payment in 
+the batch.  Results downloaded from the gateway are returned as a 
+list of Items with the 'approved' field set to a true or false value. 
+
+=head1 REQUIRED ATTRIBUTES
+
+=over 4
+
+=item action
+
+"payment" or "credit".  Most processors support only "payment".
+"payment" is defined as "money transfer FROM the account identified in the 
+Item TO the account identified by the Processor object's login settings."
+"credit" is the other direction.
+
+=cut
+
+enum 'Action' => qw(payment credit);
+coerce 'Action', from 'Str', via { lc $_ };
+has action => (
+  is  => 'rw',
+  isa => 'Action',
+  default => 'payment',
+  required => 1,
+  coerce => 1,
+);
+
+=item payment_type
+
+"CC" or "ECHECK".  Most processors will only support 
+one or the other, and if set on the BBP::Processor object, this is not
+required.
+
+=cut
+
+# are we okay with these names?
+enum 'PaymentType' => qw( CC ECHECK );
+has payment_type => ( is  => 'rw', isa => 'PaymentType' );
+
+=item amount
+
+the amount, as a decimal number.  Required only in request
+items.
+
+=cut
+
+# perhaps we should apply roles that distinguish request and reply items?
+# they have different required fields.
+has amount => (
+  is  => 'rw',
+  isa => 'Num',
+);
+
+=item tid
+
+transaction identifier.  Requests must provide this.  It's 
+a token of some kind to be passed to the gateway and used to identify the 
+reply.  For now it's required to be an integer.  An invoice number would
+be a good choice.
+
+=cut
+
+has tid => ( is  => 'rw', isa => 'Int' );
+
+=back
+
+=head1 OPTIONAL ATTRIBUTES
+
+=head2 Customer Information
+
+=over 4
+
+=item customer_id
+
+A customer number or other identifier, for the merchant's
+use.
+
+=item first_name
+
+First name.
+
+=item last_name
+
+Last name.
+
+=item company
+
+Company name.
+
+=item address, address2, city, state, country, zip
+
+Billing address fields.  Credit card processors may use these (especially
+zip) for authentication.
+
+=cut
+
+has [ qw(
+  customer_id
+  first_name
+  last_name
+  company
+  address
+  address2
+  city
+  state
+  country
+  zip
+) ] => ( is => 'rw', isa => 'Str', default => '' );
+
+=back
+
+=head2 Transaction Information
+
+=over 4
+
+=item process_date
+
+The date requested for processing.
+
+=item invoice_number
+
+An invoice number, for your use.
+
+=cut
+
+class_type 'DateTime';
+coerce 'DateTime', from 'Int', via { DateTime->from_epoch($_) };
+has process_date    => ( is => 'rw', isa => 'DateTime', coerce => 1 );
+
+has invoice_number  => ( is => 'rw', isa => 'Str' );
+
+=back
+
+=head2 Bank Transfer / ACH / EFT
+
+=over 4
+
+=item account_number
+
+Bank account number.
+
+=item routing_code
+
+Bank's routing code.
+
+=item account_type
+
+Can be 'personal checking', 'personal savings'
+'business checking', or 'business savings'.
+
+=cut
+
+enum 'Account_Type' => [
+  'personal checking',
+  'personal savings',
+  'business checking',
+  'business savings',
+];
+coerce 'Account_Type', from 'Str', via { lc $_ };
+
+has account_number  => ( is => 'rw', isa => 'Str' );
+has routing_code    => ( is => 'rw', isa => 'Str' );
+has account_type    => ( is => 'rw', isa => 'Account_Type', coerce => 1 );
+
+=back
+
+=head2 Credit Card
+
+=over 4
+
+=item card_number
+
+Credit card number.
+
+=item expiration
+
+Credit card expiration, MMYY format.
+
+=cut
+
+has card_number     => ( is => 'rw', isa => 'Str' );
+has expiration      => ( is => 'rw', isa => 'Str' );
+
+=back
+
+=head2 Tokenized Payment
+
+=over 4
+
+=item pay_by_token
+
+If your gateway supports it, this may be 
+provided instead of card_number/account_number.  See also 
+C<assigned_token> below.
+
+=cut
+
+has pay_by_token    => ( is => 'rw', isa => 'Str' );
+
+=back
+
+=head1 REPLY ATTRIBUTES
+
+=over 4
+
+=item approved 
+
+Boolean field for whether the item was approved.  This 
+will always be set on replies.
+
+=item payment_date 
+
+The date the payment was processed, as a DateTime
+object.
+
+=item order_number 
+
+The transaction identifier returned by the gateway
+(not to be confused with 'tid', which is a transaction identifier assigned
+by the merchant system).  This is usually the identifier for performing 
+other operations on the transaction, like voiding or refunding it.
+
+=item authorization
+
+The authorization code, probably only meaningful for credit cards.  
+Should be undef (or not present) if the transaction wasn't approved.
+
+=item assigned_token
+
+In tokenized systems which store the customer's account number or 
+credit card for future transactions, this is the token assigned to 
+identify that account.  Pass it as 'pay_by_token' to use that payment 
+account again.
+
+=item error_message
+
+The message returned by the gateway.  This may 
+contain a value even if the payment was successful (use C<approved> 
+to determine that.)
+
+=back
+
+=cut
+
+has approved        => ( is => 'rw', isa => 'Maybe[Bool]' );
+
+has payment_date    => ( is => 'rw', isa => 'DateTime' );
+
+has [qw( 
+  authorization
+  error_message
+  order_number
+  assigned_token
+)] => ( is => 'rw', isa => 'Str');
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/BatchPayment/Processor.pm b/BatchPayment/Processor.pm
new file mode 100644 (file)
index 0000000..26f5ba0
--- /dev/null
@@ -0,0 +1,141 @@
+=head1 NAME
+
+Business::BatchPayment::Processor - Common interface for batch payment gateways
+
+=head1 DESCRIPTION
+
+Business::BatchPayment::Processor is a Moose role.  Modules implementing  
+the protocol to talk to specific payment processing services should compose 
+it.
+
+There are two general schemes for interacting with a batch payment gateway:
+
+=over 4
+
+=item Request/Reply
+
+In this mode, you (the merchant) assemble a batch of payment requests, 
+including account numbers, and send it to the gateway.  At some point in
+the future, the gateway sends back one or more reply batches indicating the
+results of processing the payments.
+
+When submitting a request batch, the merchant software marks each payment
+request with a unique transaction ID (the "tid" field).  This should be 
+stored somewhere.  When the reply batch is processed, each item will have 
+a tid matching its request.
+
+Note that some gateways will provide results only for approved payments,
+or even only for declined payments.  It is then up to the merchant software
+to follow a sensible policy for approving or declining payments whose 
+ultimate status is unconfirmed.
+
+=item One-Way
+
+In this mode, the gateway transmits a batch file containing approved 
+payments, without those payments being requested.  For example, most
+commercial banks provide check lockbox services and periodically 
+send the merchant a statement of payments received to the lockbox.  
+This statement would be processed as a one-way batch.
+
+=back
+
+=head1 ATTRIBUTES
+
+Most attributes for B::BP::Processor objects are defined by the module.
+
+=over 4
+
+=item transport - See L<Business::BatchPayment::Transport>.  This must 
+be set before calling submit() or receive().  Some modules will set it 
+themselves; others require a transport to be supplied.  Check for the 
+existence of a 'default_transport' method.
+
+=item debug - Debug level.  This may be interpreted in various ways by
+the module.
+
+=item test_mode - Communicate with a test server instead of the production
+gateway.  Not all processors support this.
+C<$processor->does('Business::BatchPayment::TestMode')> should tell whether
+it's supported.
+
+=back
+
+=head1 OTHER PARAMETERS
+
+=over 4
+
+=item input FILE
+
+=item output FILE
+
+If either of these is passed when constructing a Processor object, the 
+transport will be replaced with a File transport with those parameters.
+Specifying only 'input' will direct 'output' to /dev/null, and vice versa.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item submit BATCH
+
+Send a batch of requests to the gateway.  BATCH must be a 
+L<Business::BatchPayment::Batch>.
+
+=item receive
+
+Download/otherwise acquire the available confirmed transactions from the 
+gateway, parse them, and return a list of L<Business::BatchPayment::Batch>
+objects.  The items in these batches will have, at minimum, the 'approved' 
+field and either the 'tid' or 'amount' field set.
+
+=back
+
+=cut
+
+package Business::BatchPayment::Processor;
+
+use Moose::Role;
+with 'Business::BatchPayment::Debug';
+
+requires 'format_request', 'parse_response';
+
+has 'transport' => (
+  is      => 'rw',
+  does    => 'Business::BatchPayment::Transport',
+  # possibly this part should be a separate role
+  lazy    => 1,
+  builder => 'default_transport',
+);
+
+around BUILDARGS => sub {
+  my ($orig, $class, %args) = @_;
+  %args = %{ $class->$orig(%args) }; #process as usual
+  # then:
+  if ( $args{input} or $args{output} ) {
+    $args{transport} = Business::BatchPayment->create( 'Transport::File',
+      input => $args{input},
+      output => $args{output},
+    );
+  }
+  \%args;
+};
+
+sub submit {
+  my $self = shift;
+  my $batch = shift;
+  my @items = @_;
+  my $request = $self->format_request($batch);
+  warn $request if $self->debug >= 2;
+  $self->transport->upload($request);
+}
+
+sub receive {
+  my $self = shift;
+  my @responses = $self->transport->download;
+  warn join("\n\n",@responses) if $self->debug >= 2 and scalar(@responses);
+  map { $self->parse_response($_) } @responses;
+}
+
+1;
diff --git a/BatchPayment/TestMode.pm b/BatchPayment/TestMode.pm
new file mode 100644 (file)
index 0000000..551a9aa
--- /dev/null
@@ -0,0 +1,18 @@
+package Business::BatchPayment::TestMode;
+
+=head1 DESCRIPTION
+
+The 'test_mode' attribute.  A module that does this role is promising that
+requests will not be submitted to a live account if test_mode is true.
+
+=cut
+
+use Moose::Role;
+
+has 'test_mode' => (
+  is => 'rw',
+  isa => 'Bool',
+  default => 0,
+);
+
+1;
diff --git a/BatchPayment/Transport.pm b/BatchPayment/Transport.pm
new file mode 100644 (file)
index 0000000..4ac8b71
--- /dev/null
@@ -0,0 +1,32 @@
+package Business::BatchPayment::Transport;
+
+use Moose::Role;
+with 'Business::BatchPayment::Debug';
+
+requires 'upload';
+requires 'download';
+
+=head1 NAME
+
+Business::BatchPayment::Transport
+
+=head1 USAGE
+
+Business::BatchPayment::Processor classes must have a 'transport' attribute
+which does the Business::BatchPayment::Transport role (implements 'upload'
+and 'download').  The B::BP::Processor class can implement 'default_transport'
+to construct a B::BP::Transport object.
+
+'upload' must take a scalar argument containing the (correctly formatted)
+batch for upload.  On success, it may return nothing, or a scalar identifier
+for the batch (such as a serial number) if the gateway requires one.
+
+'download' takes the return value from 'upload' (if any) as its argument, 
+and returns any number of scalars containing batch files.  If there are no 
+batches available, it should return an empty list.
+
+Both of these methods should die on error.
+
+=cut
+
+1;
diff --git a/BatchPayment/Transport/File.pm b/BatchPayment/Transport/File.pm
new file mode 100644 (file)
index 0000000..eaaba49
--- /dev/null
@@ -0,0 +1,63 @@
+package Business::BatchPayment::Transport::File;
+
+=head2 File transport
+
+The simplest case.  Takes two arguments, 'input' and 'output'.  These can 
+be open filehandles or strings naming files.  If unspecified, they default 
+to /dev/null.
+
+=cut
+
+use IO::File;
+use Moose;
+with 'Business::BatchPayment::Transport';
+
+has 'input' => (
+  is => 'rw',
+  isa => 'Maybe[FileHandle|Str]',
+  default => sub {
+    warn "no input passed to file transport; using /dev/null";
+    '/dev/null'
+  },
+  #lazy => 1,
+);
+
+has 'output' => (
+  is => 'rw',
+  isa => 'Maybe[FileHandle|Str]',
+  default => sub {
+    warn "no output passed to file transport; using /dev/null";
+    '/dev/null'
+  },
+  #lazy => 1,
+);
+
+sub upload {
+  my $self = shift;
+  my $text = shift;
+  my $fh;
+  if ( ref $self->output ) {
+    $fh = $self->output;
+  } else {
+    $fh = IO::File->new();
+    $fh->open($self->output,'>')
+      or die "couldn't write to ".$self->output;
+  }
+  print $fh $text;
+}
+
+sub download {
+  my $self = shift;
+  my $fh;
+  if ( ref $self->input ) {
+    $fh = $self->input;
+  } else {
+    $fh = IO::File->new();
+    $fh->open($self->input,'<')
+      or die "couldn't read from ".$self->input;
+  }
+  local $/;
+  my $text = <$fh>;
+}
+
+1;
diff --git a/BatchPayment/Transport/HTTPS.pm b/BatchPayment/Transport/HTTPS.pm
new file mode 100644 (file)
index 0000000..c40a78f
--- /dev/null
@@ -0,0 +1,58 @@
+package Business::BatchPayment::Transport::HTTPS;
+
+=head2 HTTPS transport
+
+Sends a request by HTTPS POST, and downloads the response the same way.
+Options are 'server', 'port', 'get_path', 'put_path', optionally 
+'content_type'.
+
+=cut
+
+use Net::HTTPS::Any 0.10;
+use Moose;
+with 'Business::BatchPayment::Transport';
+
+has [ qw( host port get_path put_path ) ] => ( 
+  is => 'ro',
+  isa => 'Str'
+);
+
+has 'content_type' => (
+  is => 'rw',
+  isa => 'Str',
+  default => 'text/plain'
+);
+
+sub https_post {
+  my $self = shift;
+  my $path = shift;
+  my $content = shift;
+
+  warn "starting https_post...\n" if $self->debug;
+  my ( $page, $response, %reply_headers ) = Net::HTTPS::Any::https_post(
+    host => $self->host,
+    port => $self->port,
+    path => $path,
+    content => $content,
+    debug => ($self->debug >= 3),
+  );
+  warn "PAGE:\n$page\n\nRESPONSE:\n$response\n\n" if $self->debug >= 2;
+  return ($page, $response, %reply_headers);
+}
+
+sub upload {
+  my $self = shift;
+  my $content = shift;
+  $self->https_post($self->put_path, $content);
+}
+
+sub download {
+  # will probably need to be overridden in most cases
+  my $self = shift;
+  my ($page, $response, %reply_headers) = $self->https_post($self->get_path);
+  $page;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/BatchPayment/Transport/SFTP.pm b/BatchPayment/Transport/SFTP.pm
new file mode 100644 (file)
index 0000000..08d2fb7
--- /dev/null
@@ -0,0 +1,88 @@
+package Business::BatchPayment::Transport::SFTP;
+
+=head2 Business::BatchPayment::Transport::SFTP
+
+Import this role to use SFTP.  Requires "host", "port', "user", and
+"password" to be set.  The 'sftp' method returns a Net::SFTP::Foreign
+object providing 'get' and 'put' methods (among others).  All of these
+methods die on error.
+
+Also provides "get_path" and "put_path" attributes.  The default 'download'
+and 'upload' methods get and put to those paths on the SFTP host.  You 
+may find it useful to modify or override that behavior.
+
+=cut
+
+use Net::SFTP::Foreign;
+use File::Slurp qw(read_file);
+use Moose;
+with 'Business::BatchPayment::Transport';
+
+has [ qw( host login password ) ] => (
+  is => 'rw',
+  isa => 'Str',
+);
+
+has 'port' => (
+  is => 'rw',
+  isa => 'Int',
+  default => 22,
+);
+
+has [ 'get_path', 'put_path' ] => (
+  is => 'rw',
+  isa => 'Str',
+);
+
+has 'sftp' => (
+  is => 'ro',
+  isa => 'Net::SFTP::Foreign',
+  handles => [ 'get', 'put', 'ls', 'setcwd' ],
+  builder => '_sftp',
+  lazy => 1,
+);
+
+has 'timeout' => (
+  is => 'rw',
+  isa => 'Int',
+  default => 60,
+);
+
+sub _sftp {
+  my $self = shift;
+  my %args = (
+    host      => $self->host,
+    port      => $self->port,
+    user      => $self->login,
+    password  => $self->password,
+    timeout   => $self->timeout,
+    autodie   => 1,
+  );
+  $args{'more'} = '-v' if $self->debug >= 2;
+  Net::SFTP::Foreign->new(%args);
+}
+
+sub upload {
+  my $self = shift;
+  die "default SFTP upload requires 'put_path'\n" unless $self->put_path;
+  my $content = shift;
+  my ($out, $tmpfile) = tempfile();
+  die "failed to open temporary file $tmpfile\n" unless $out;
+  print $out $content;
+  close $out;
+  $self->sftp->put($tmpfile, $self->put_path);
+  unlink $tmpfile;
+}
+
+sub download { # mostly illustrative rather than directly useful
+  my $self = shift;
+  die "default SFTP download requires 'get_path'\n" unless $self->get_path;
+  my ($out, $tmpfile) = tempfile();
+  $self->sftp->get($self->get_path, $out);
+  close $out;
+  return read_file($tmpfile);
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..8d5f1fc
--- /dev/null
+++ b/Changes
@@ -0,0 +1,4 @@
+Revision history for Business-BatchPayment
+
+0.01    unreleased
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..4c44ff2
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,19 @@
+Changes
+ignore.txt
+Makefile.PL
+BatchPayment/Item.pm
+BatchPayment/Transport/HTTPS.pm
+BatchPayment/Transport/SFTP.pm
+BatchPayment/Transport/File.pm
+BatchPayment/Debug.pm
+BatchPayment/TestMode.pm
+BatchPayment/Processor.pm
+BatchPayment/Transport.pm
+BatchPayment/Batch.pm
+BatchPayment.pm
+t/manifest.t
+t/pod.t
+t/00-load.t
+README
+MANIFEST
+META.yml                                 Module meta-data (added by MakeMaker)
diff --git a/META.yml b/META.yml
new file mode 100644 (file)
index 0000000..10ceacf
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,28 @@
+--- #YAML:1.0
+name:               Business-BatchPayment
+version:            0.01
+abstract:           Batch-oriented payment processing
+author:
+    - Mark Wells <mark@freeside.biz>
+license:            perl
+distribution_type:  module
+configure_requires:
+    ExtUtils::MakeMaker:  0
+build_requires:
+    ExtUtils::MakeMaker:  0
+requires:
+    DateTime:             0
+    File::Slurp:          0
+    Moose:                2
+    MooseX::UndefTolerant:  0
+    Net::HTTPS::Any:      0.1
+    Net::SFTP::Foreign:   0
+    Test::More:           0
+no_index:
+    directory:
+        - t
+        - inc
+generated_by:       ExtUtils::MakeMaker version 6.57_05
+meta-spec:
+    url:      http://module-build.sourceforge.net/META-spec-v1.4.html
+    version:  1.4
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..34dc397
--- /dev/null
@@ -0,0 +1,26 @@
+use 5.006;
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+    NAME                => 'Business::BatchPayment',
+    AUTHOR              => q{Mark Wells <mark@freeside.biz>},
+    VERSION_FROM        => 'BatchPayment.pm',
+    ABSTRACT_FROM       => 'BatchPayment.pm',
+    ($ExtUtils::MakeMaker::VERSION >= 6.3002
+      ? ('LICENSE'=> 'perl')
+      : ()),
+    PL_FILES            => {},
+    PREREQ_PM => {
+        'Moose'    => 2.000,
+        'MooseX::UndefTolerant' => 0,
+        'DateTime' => 0,
+        'File::Slurp' => 0,
+        'Net::SFTP::Foreign' => 0,
+        'Net::HTTPS::Any' => 0.10,
+        'Test::More' => 0,
+    },
+    dist                => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+    clean               => { FILES => 'Business-BatchPayment-*' },
+);
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..664b691
--- /dev/null
+++ b/README
@@ -0,0 +1,35 @@
+Business::BatchPayment is a generic interface for processing credit card 
+or electronic checking payments in an asynchronous manner.  It fills a 
+similar role to Business::OnlinePayment, but works with processors that 
+expect periodic delivery of payment requests and return results in a 
+later step.
+
+INSTALLATION
+
+To install this module, run the following commands:
+
+       perl Makefile.PL
+       make
+       make test
+       make install
+
+SUPPORT AND DOCUMENTATION
+
+After installing, you can find documentation for this module with the
+perldoc command.
+
+    perldoc Business::BatchPayment
+
+Contact Freeside Internet Services <sales@freeside.biz> for commercial 
+support and custom development services.
+
+LICENSE AND COPYRIGHT
+
+Copyright (C) 2012 Mark Wells
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of either: the GNU General Public License as published
+by the Free Software Foundation; or the Artistic License.
+
+See http://dev.perl.org/licenses/ for more information.
+
diff --git a/ignore.txt b/ignore.txt
new file mode 100644 (file)
index 0000000..1bdd2ac
--- /dev/null
@@ -0,0 +1,13 @@
+.git*
+blib*
+Makefile
+Makefile.old
+Build
+Build.bat
+_build*
+pm_to_blib*
+*.tar.gz
+.lwpcookies
+cover_db
+pod2htm*.tmp
+Business-BatchPayment-*
diff --git a/t/00-load.t b/t/00-load.t
new file mode 100644 (file)
index 0000000..271e8d2
--- /dev/null
@@ -0,0 +1,9 @@
+#!perl -T
+
+use Test::More tests => 1;
+
+BEGIN {
+    use_ok( 'Business::BatchPayment' ) || print "Bail out!\n";
+}
+
+diag( "Testing Business::BatchPayment $Business::BatchPayment::VERSION, Perl $], $^X" );
diff --git a/t/manifest.t b/t/manifest.t
new file mode 100644 (file)
index 0000000..74d8cac
--- /dev/null
@@ -0,0 +1,13 @@
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More;
+
+unless ( $ENV{RELEASE_TESTING} ) {
+    plan( skip_all => "Author tests not required for installation" );
+}
+
+eval "use Test::CheckManifest 0.9";
+plan skip_all => "Test::CheckManifest 0.9 required" if $@;
+ok_manifest({ exclude => ['/.git', '/blib'] });
diff --git a/t/pod.t b/t/pod.t
new file mode 100644 (file)
index 0000000..ee8b18a
--- /dev/null
+++ b/t/pod.t
@@ -0,0 +1,12 @@
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More;
+
+# Ensure a recent version of Test::Pod
+my $min_tp = 1.22;
+eval "use Test::Pod $min_tp";
+plan skip_all => "Test::Pod $min_tp required for testing POD" if $@;
+
+all_pod_files_ok();