package Business::OnlinePayment::HTTPS;
use strict;
-use vars qw($VERSION @ISA $DEBUG $ssl_module $skip_NetSSLeay);
-#use URI;
-#use URI::QueryParam;
+use vars qw($VERSION $DEBUG $ssl_module $skip_NetSSLeay);
use URI::Escape;
use Tie::IxHash;
+use base qw(Business::OnlinePayment);
-@ISA = qw( Business::OnlinePayment );
+$VERSION = '0.05';
+$DEBUG = 0;
-$VERSION = '0.02';
+BEGIN {
-$DEBUG = 0;
+ $ssl_module = '';
-BEGIN {
+ eval {
+ die if defined($skip_NetSSLeay) && $skip_NetSSLeay;
+ require Net::SSLeay;
+ Net::SSLeay->VERSION(1.30);
- $ssl_module = '';
+ #import Net::SSLeay
+ # qw(get_https post_https make_form make_headers);
+ $ssl_module = 'Net::SSLeay';
+ };
+ if ($@) {
eval {
- die if defined($skip_NetSSLeay) && $skip_NetSSLeay;
- require Net::SSLeay;
- #import Net::SSLeay
- # qw(get_https post_https make_form make_headers);
- $ssl_module = 'Net::SSLeay';
- };
+ require LWP::UserAgent;
+ require HTTP::Request::Common;
+ require Crypt::SSLeay;
- if ($@) {
- eval {
- require LWP::UserAgent;
- require HTTP::Request::Common;
- require Crypt::SSLeay;
- #import HTTP::Request::Common qw(GET POST);
- $ssl_module = 'Crypt::SSLeay';
- };
- }
+ #import HTTP::Request::Common qw(GET POST);
+ $ssl_module = 'Crypt::SSLeay';
+ };
+ }
- unless ( $ssl_module ) {
- die "Net::SSLeay or Crypt::SSLeay (+LWP) is required";
- }
+ unless ($ssl_module) {
+ die "One of Net::SSLeay (v1.30 or later)"
+ . " or Crypt::SSLeay (+LWP) is required";
+ }
}
=head1 SYNOPSIS
- package Business::OnlinePayment::MyProcessor
- @ISA = qw( Business::OnlinePayment::HTTPS );
-
+ package Business::OnlinePayment::MyProcessor;
+ use base qw(Business::OnlinePayment::HTTPS);
+
sub submit {
- my $self = shift;
-
- #...
-
- # pass a list (order is preserved, if your gateway needs that)
- ($page, $response, %reply_headers)
- = $self->https_get( field => 'value', ... );
-
- #or a hashref
- my %hash = ( field => 'value', ... );
- ($page, $response_code, %reply_headers)
- = $self->https_get( $hashref );
-
- #...
+ my $self = shift;
+
+ #...
+
+ # pass a list (order is preserved, if your gateway needs that)
+ ( $page, $response, %reply_headers )
+ = $self->https_get( field => 'value', ... );
+
+ # or a hashref
+ my %hash = ( field => 'value', ... );
+ ( $page, $response_code, %reply_headers )
+ = $self->https_get( \%hash );
+
+ #...
}
=head1 DESCRIPTION
-This is a base class for HTTPS based gateways, providing useful code for
-implementors of HTTPS payment APIs.
+This is a base class for HTTPS based gateways, providing useful code
+for implementors of HTTPS payment APIs.
It depends on Net::SSLeay _or_ ( Crypt::SSLeay and LWP::UserAgent ).
=over 4
-=item https_get HASHREF | FIELD => VALUE, ...
-
-Accepts parameters as either a hashref or a list of fields and values. In the
-latter case, ordering is preserved (see L<Tie::IxHash> to do so when passing a
-hashref).
+=item https_get [ \%options ] HASHREF | FIELD => VALUE, ...
-Returns a list consisting of the page content as a string, the HTTP response
-code, and a list of key/value pairs representing the HTTP response headers.
+Accepts parameters as either a hashref or a list of fields and values.
+In the latter case, ordering is preserved (see L<Tie::IxHash> to do so
+when passing a hashref).
-=cut
-
-sub https_get {
- my $self = shift;
-
- #accept a hashref or a list (keep it ordered)
- my $post_data;
- if ( ref($_[0]) ) {
- $post_data = shift;
- } else {
- tie my %hash, 'Tie::IxHash', @_;
- $post_data = \%hash;
- }
+Returns a list consisting of the page content as a string, the HTTP
+response code, and a list of key/value pairs representing the HTTP
+response headers.
- my $path = $self->path;
- if ( keys %$post_data ) {
-
- #my $u = URI->new("", "https");
- #$u->query_param(%$post_data);
- #$path .= '?'. $u->query;
-
- $path .= '?'. join('&',
- map { uri_escape($_).'='. uri_escape($post_data->{$_}) }
- keys %$post_data
- );
- #warn $path;
+The options hashref supports setting headers and Content-Type:
+ {
+ headers => { 'X-Header1' => 'value', ... },
+ Content-Type => 'text/namevalue',
}
- my $referer = ''; ### XXX referer!!!
- my %headers;
- $headers{'Referer'} = $referer if length($referer);
+=cut
- if ( $ssl_module eq 'Net::SSLeay' ) {
+sub https_get {
+ my $self = shift;
- import Net::SSLeay qw(get_https make_headers);
- my $headers = make_headers(%headers);
- get_https( $self->server, $self->port, $path, $referer, $headers );
+ # handle optional options hashref
+ my $opts;
+ if ( scalar(@_) > 1 and ref( $_[0] ) eq "HASH" ) {
+ $opts = shift;
+ }
- } elsif ( $ssl_module eq 'Crypt::SSLeay' ) {
+ # accept a hashref or a list (keep it ordered)
+ my $post_data;
+ if ( ref( $_[0] ) eq 'HASH' ) {
+ $post_data = shift;
+ }
+ elsif ( scalar(@_) > 1 ) {
+ tie my %hash, 'Tie::IxHash', @_;
+ $post_data = \%hash;
+ }
+ elsif ( scalar(@_) == 1 ) {
+ $post_data = shift;
+ }
+ else {
+ die "https_get called with no params\n";
+ }
- import HTTP::Request::Common qw(GET);
+ $opts->{"Content-Type"} ||= "application/x-www-form-urlencoded";
- my $url = 'https://'. $self->server;
- $url .= ':'. $self->port
- unless $self->port == 443;
- $url .= '/'. $self->path;
+ ### XXX referer!!!
+ my %headers;
+ if ( ref( $opts->{headers} ) eq "HASH" ) {
+ %headers = %{ $opts->{headers} };
+ }
+ $headers{'Host'} ||= $self->server;
+
+ my $path = $self->path;
+ if ( keys %$post_data ) {
+ $path .= '?'
+ . join( '&',
+ map { uri_escape($_) . '=' . uri_escape( $post_data->{$_} ) }
+ keys %$post_data );
+ }
- my $ua = new LWP::UserAgent;
- my $res = $ua->request( GET( $url ) );
+ if ( $ssl_module eq 'Net::SSLeay' ) {
- #( $res->as_string, # wtf?
- ( $res->content,
- $res->code,
- map { $_ => $res->header($_) } $res->header_field_names
- );
+ import Net::SSLeay qw(get_https make_headers);
+ my $headers = make_headers(%headers);
+ get_https( $self->server, $self->port, $path, $headers, "",
+ $opts->{"Content-Type"} );
+ }
+ elsif ( $ssl_module eq 'Crypt::SSLeay' ) {
- } else {
+ import HTTP::Request::Common qw(GET);
- die "unknown SSL module $ssl_module";
+ my $url = 'https://' . $self->server;
+ $url .= ':' . $self->port
+ unless $self->port == 443;
+ $url .= "/$path";
- }
+ my $ua = new LWP::UserAgent;
+ foreach my $hdr ( keys %headers ) {
+ $ua->default_header( $hdr => $headers{$hdr} );
+ }
+ my $res = $ua->request( GET($url) );
+ (
+ $res->content, $res->code,
+ map { $_ => $res->header($_) } $res->header_field_names
+ );
+ }
+ else {
+ die "unknown SSL module $ssl_module";
+ }
}
-=item https_post HASHREF | FIELD => VALUE, ...
-
-Accepts parameters as either a hashref or a list of fields and values. In the
-latter case, ordering is preserved (see L<Tie::IxHash> to do so when passing a
-hashref).
+=item https_post [ \%options ] SCALAR | HASHREF | FIELD => VALUE, ...
-Returns a list consisting of the page content as a string, the HTTP response
-code, and a list of key/value pairs representing the HTTP response headers.
+Accepts form fields and values as either a hashref or a list. In the
+latter case, ordering is preserved (see L<Tie::IxHash> to do so when
+passing a hashref).
-=cut
+Also accepts instead a simple scalar containing the raw content.
-sub https_post {
- my $self = shift;
-
- #accept a hashref or a list (keep it ordered)
- my $post_data;
- if ( ref($_[0]) ) {
- $post_data = shift;
- } else {
- tie my %hash, 'Tie::IxHash', @_;
- $post_data = \%hash;
- }
+Returns a list consisting of the page content as a string, the HTTP
+response code, and a list of key/value pairs representing the HTTP
+response headers.
- my $referer = ''; ### XXX referer!!!
- my %headers;
- $headers{'Referer'} = $referer if length($referer);
- $headers{'Host'} = $self->server;
+The options hashref supports setting headers and Content-Type:
- if ( $DEBUG ) {
- warn join('', map { " $_ => ". $post_data->{$_}. "\n" } keys %$post_data );
+ {
+ headers => { 'X-Header1' => 'value', ... },
+ Content-Type => 'text/namevalue',
}
- if ( $ssl_module eq 'Net::SSLeay' ) {
+=cut
- #import Net::SSLeay qw(post_https make_headers make_form);
- import Net::SSLeay qw(make_headers make_form);
- my $headers = make_headers(%headers);
+sub https_post {
+ my $self = shift;
- if ( $DEBUG ) {
- warn $self->server. ':'. $self->port. $self->path. "\n";
- $Net::SSLeay::trace = 2;
+ # handle optional options hashref
+ my $opts;
+ if ( scalar(@_) > 1 and ref( $_[0] ) eq "HASH" ) {
+ $opts = shift;
}
- #post_https( $self->server, $self->port, $self->path,
- # $headers, make_form(%$post_data) );
- _my_post_https( $self->server, $self->port, $self->path,
- $headers, make_form(%$post_data) );
- } elsif ( $ssl_module eq 'Crypt::SSLeay' ) {
+ # accept a hashref or a list (keep it ordered)
+ my $post_data;
+ if ( ref( $_[0] ) eq 'HASH' ) {
+ $post_data = shift;
+ }
+ elsif ( scalar(@_) > 1 ) {
+ tie my %hash, 'Tie::IxHash', @_;
+ $post_data = \%hash;
+ }
+ elsif ( scalar(@_) == 1 ) {
+ $post_data = shift;
+ }
+ else {
+ die "https_post called with no params\n";
+ }
- import HTTP::Request::Common qw(POST);
+ $opts->{"Content-Type"} ||= "application/x-www-form-urlencoded";
- my $url = 'https://'. $self->server;
- $url .= ':'. $self->port
- unless $self->port == 443;
- $url .= $self->path;
+ ### XXX referer!!!
+ my %headers;
+ if ( ref( $opts->{headers} ) eq "HASH" ) {
+ %headers = %{ $opts->{headers} };
+ }
+ $headers{'Host'} ||= $self->server;
- if ( $DEBUG ) {
- warn $url;
+ if ( $DEBUG && ref($post_data) ) {
+ warn "post data:\n",
+ join( '',
+ map { " $_ => " . $post_data->{$_} . "\n" } keys %$post_data );
}
- my $ua = new LWP::UserAgent;
- my $res = $ua->request( POST( $url, [ %$post_data ] ) );
+ if ( $ssl_module eq 'Net::SSLeay' ) {
- #( $res->as_string, # wtf?
- ( $res->content,
- $res->code,
- map { $_ => $res->header($_) } $res->header_field_names
- );
+ import Net::SSLeay qw(post_https make_headers make_form);
+ my $headers = make_headers(%headers);
- } else {
+ if ($DEBUG) {
+ no warnings 'uninitialized';
+ warn $self->server . ':' . $self->port . $self->path . "\n";
+ $Net::SSLeay::trace = $DEBUG;
+ }
- die "unknown SSL module $ssl_module";
+ my $raw_data = ref($post_data) ? make_form(%$post_data) : $post_data;
+ post_https( $self->server, $self->port, $self->path, $headers,
+ $raw_data, $opts->{"Content-Type"} );
+ }
+ elsif ( $ssl_module eq 'Crypt::SSLeay' ) {
- }
+ import HTTP::Request::Common qw(POST);
-}
+ my $url = 'https://' . $self->server;
+ $url .= ':' . $self->port
+ unless $self->port == 443;
+ $url .= $self->path;
-# SecureHostingUPG (and presumably other IIS-based gateways?) doesn't like the
-# Host: $site:$port header auto-added by Net::SSLeay, which it adds regardless
-# if you supply one or not
-
-sub _my_post_https ($$$;***) { _my_do_httpx2(POST => 1, @_) }
-
-sub _my_do_httpx2 {
- my ($page, $response, $headers, $server_cert) = &_my_do_httpx3;
- Net::SSLeay::X509_free($server_cert) if defined $server_cert;
- my($h,$v);
- return ($page, $response,
- map( { ($h,$v)=/^(\S+)\:\s*(.*)$/; (uc($h),$v); }
- split(/\s?\n/, $headers)
- )
- );
-}
+ if ($DEBUG) {
+ warn $url;
+ }
-sub _my_do_httpx3 {
- my ($method, $usessl, $site, $port, $path, $headers,
- $content, $mime_type, $crt_path, $key_path) = @_;
- my ($response, $page, $h,$v);
+ my $ua = new LWP::UserAgent;
+ foreach my $hdr ( keys %headers ) {
+ $ua->default_header( $hdr => $headers{$hdr} );
+ }
- my $CRLF = $Net::SSLeay::CRLF;
+ my $res;
+ if ( ref($post_data) ) {
+ $res = $ua->request( POST( $url, [%$post_data] ) );
+ }
+ else {
+ my $req = new HTTP::Request( 'POST' => $url );
+ $req->content_type( $opts->{"Content-Type"} );
+ $req->content($post_data);
+ $res = $ua->request($req);
+ }
- if ($content) {
- $mime_type = "application/x-www-form-urlencoded" unless $mime_type;
- my $len = Net::SSLeay::blength($content);
- $content = "Content-Type: $mime_type$CRLF"
- . "Content-Length: $len$CRLF$CRLF$content";
- } else {
- $content = "$CRLF$CRLF";
+ (
+ $res->content, $res->code,
+ map { $_ => $res->header($_) } $res->header_field_names
+ );
}
-
- my $req = "$method $path HTTP/1.0$CRLF";
- unless ( defined $headers && $headers =~ /^Host:/m ) {
- $req .= "Host: $site";
- unless ( ( $port==80 && !$usessl ) || ( $port==443 && $usessl ) ) {
- $req .= ":$port";
- }
- $req .= $CRLF;
+ else {
+ die "unknown SSL module $ssl_module";
}
- $req .= (defined $headers ? $headers : '') . "Accept: */*$CRLF$content";
-
- warn "do_httpx3($method,$usessl,$site:$port)" if $Net::SSLeay::trace;
- my ($http, $errs, $server_cert)
- = Net::SSLeay::httpx_cat($usessl, $site, $port, $req, $crt_path, $key_path);
- return (undef, "HTTP/1.0 900 NET OR SSL ERROR$CRLF$CRLF$errs") if $errs;
-
- $http = '' if !defined $http;
- ($headers, $page) = split /\s?\n\s?\n/, $http, 2;
- warn "headers >$headers< page >>$page<< http >>>$http<<<" if $Net::SSLeay::trace>1;
- ($response, $headers) = split /\s?\n/, $headers, 2;
- return ($page, $response, $headers, $server_cert);
}
=back
-=head1 SEE ALSO
+=head1 SEE ALSO
L<Business::OnlinePayment>
=cut
1;
-