package Business::OnlineThirdPartyPayment::FCMB;

use strict;
use base 'Business::OnlineThirdPartyPayment';

use strict;
use LWP;
use URI;
use Data::Dumper;
use Date::Format 'time2str';
use XML::LibXML;

our $VERSION = '0.01';
our $ENDPOINT_SANDBOX = 'localhost';
our $ENDPOINT_LIVE    = 'fcmbwebpay.firstcitygrouponline.com';

our $DEBUG = 3;

# ISO 4217 currency codes (the relevant ones)
our %ALPHA_TO_NUM = (
  USD => 840,
  UKP => 826,
  NGN => 566,
);

sub set_defaults {
  my $self = shift;
  my %args = @_;
  $self->build_subs(qw(username password host));
  if ( $args{debug} ) {
    $DEBUG = $args{debug};
  }
}

sub create {
  my $self = shift;
  my %content = @_;

  my %params;
  $params{'orderId'}  = time2str('%Y%m%d%H%M%S', time) . '-' .
                       sprintf('%06d', int(rand(1000000)));
  $params{'mercId'}   = $self->username
    or die "FCMB merchant ID (username) must be configured\n";
  $params{'currCode'} = $ALPHA_TO_NUM{$content{currency}};
  $params{'prod'}     = $content{'description'};
  $params{'email'}    = $content{'email'};
  $params{'amt'}      = $content{'amount'};

  my $host = $self->host;
  if ( $self->test_transaction ) {
    $host ||= $ENDPOINT_SANDBOX;
  } else {
    $host ||= $ENDPOINT_LIVE;
  }
  my $url = 'https://' . $host .
    '/customerportal/MerchantServices/MakePayment.aspx';

  warn Dumper \%params if $DEBUG > 2;

  # we don't post to the url ourselves, just give it to the user
  $self->is_success(1);
  $self->redirect($url);
  $self->post_params(\%params);

  $self->token( $params{'orderId'} );
}

sub execute {
  my $self = shift;
  my %params = @_;

# URL looks like
# http://merchantA.com/Success?OrderID=988676&TransactionReference=8765678998989779
  warn Dumper(\%params) if $DEBUG > 2;
 
  if ($params{'OrderID'}) {
    $self->token($params{'OrderID'});
  } else {
    die 'No order ID returned from processor';
  }
  if ($params{'TransactionReference'}) {
    $self->order_number($params{'TransactionReference'});
  } else {
    die 'No transaction reference returned from processor';
  }

  my $host = $self->host;
  if ( $self->test_transaction ) {
    $host ||= $ENDPOINT_SANDBOX;
    $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
  } else {
    $host ||= $ENDPOINT_LIVE;
  }
  my $url = 'https://' . $host .
    '/customerportal/MerchantServices/UpayTransactionStatus.ashx';
  my $ua = LWP::UserAgent->new;
  warn "Querying transaction status at $url\n" if $DEBUG;
  my $response = $ua->get($url,
    'MERCHANT_ID'  => $self->username,
    'ORDER_ID'     => $self->token
  );

  if ( $response->is_success ) {

    local $@;
    my $parser = XML::LibXML->new;
    my $doc = eval { $parser->parse_string($response->content) };
    if ( $@ ) {
      die "malformed response to transaction status request: $@\n".
          ($DEBUG ? ("\n\n".$response->content) : '');
    }
    my $root = $doc->documentElement;
    my %hash = map { $_->nodeName => $_->textContent }
               $root->nonBlankChildNodes;

    warn Dumper \%hash if $DEBUG > 2;
    if ( $hash{StatusCode} == 0 ) {
      $self->is_success(1);
      $self->authorization($hash{PaymentRef});
    } else {
      $self->is_success(0);
      $self->error_message($hash{Status} . ' - ' . $hash{ResponseDescription});
    }
  } else {
    die "No confirmation received: ".$response->status_line;
  }
}

1;
__END__

=head1 NAME

Business::OnlineThirdPartyPayment::FCMB

=head1 DESCRIPTION

Business::OnlineThirdPartyPayment interface to the First City Monument Bank
(Nigeria) web-based payment processing system.

=head1 NOTES

=over 4

=item FCMB requires the callback URL to be configured statically;
I<return_url> and I<cancel_url> parameters will be ignored.

=item Set I<username> to your merchant ID (5 digits).  No password is needed.

=item The only required transaction fields are currency (NGN, USD, or UKP),
amount, description, and email.

=item The 'faker' directory contains a simple mock-up of the FCMB interface
for testing.  This was created from the documentation, without reference
to the real FCMB system, and is NOT known to be accurate.

=back

=head1 AUTHOR

Mark Wells <mark@freeside.biz>

=head1 SEE ALSO

perl(1). L<Business::OnlineThirdPartyPayment>.

=cut

