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 = URI->new('https://' . $host . '/customerportal/MerchantServices/UpayTransactionStatus.ashx'); $url->query_form('MERCHANT_ID' => $self->username, 'ORDER_ID' => $self->token); my $ua = LWP::UserAgent->new; warn "Querying transaction status at $url\n" if $DEBUG; my $response = $ua->get($url); 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 and I parameters will be ignored. =item Set I 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 =head1 SEE ALSO perl(1). L. =cut