George Baugh vor 1 Jahr
Commit
aba72828c1
1 geänderte Dateien mit 167 neuen und 0 gelöschten Zeilen
  1. 167 0
      clover.pl

+ 167 - 0
clover.pl

@@ -0,0 +1,167 @@
+#!/usr/bin/env starman
+package Clover::Proxy;
+
+use strict;
+use warnings;
+
+use HTTP::Tiny;
+use HTTP::Body;
+use URL::Encode;
+use Data::Dumper;
+
+=head1 Clover::Proxy
+
+Application which proxies requests to the clover API, handling the oauth parts for you
+
+=cut
+
+# Clover generally expects that you have an HTTP endpoint on your end that behaves a particular way.
+# Example: $my_site/clover?code=AUTH_CODE&merchant_id=MERCH_ID
+# It is then expected to POST to the oauth endpoint.
+
+# Ye olde PSGI boilerstrap
+our $CHUNK_SIZE = 1024000;
+
+our %content_types = (
+    text  => "text/plain",
+    html  => "text/html",
+    json  => "application/json",
+    blob  => "application/octet-stream",
+    xml   => "text/xml",
+    xsl   => "text/xsl",
+    css   => "text/css",
+    rss   => "application/rss+xml",
+    email => "multipart/related",
+);
+
+our %byct = reverse %content_types;
+
+my $ct      = 'Content-type';
+my $cur_query = {};
+
+my %routes = (
+	'/oauth' => \&oauth,
+);
+
+my $clover_uri = "https://apisandbox.dev.clover.com";
+
+# On startup, let's oauth.
+sub new {
+	my $self = {};
+ 	$self->{agent} = HTTP::Tiny->new();
+
+	# TODO Grab our 2 secrets from config
+
+	bless($self, __PACKAGE__);
+
+	return $self;
+}
+
+sub agent { $_[0]->{agent} }
+
+sub oauth {
+	my ($self, $data) = @_;
+
+	my $oauth_endpoint = 'oauth/v2/authorize';
+	my $token_endpoint = 'oauth/v2/token';
+
+	my $client_id     = 'W5QCN9ZCQX4WT';
+	my $client_secret = 'df5ac68e-2cc5-8e4d-0788-928ea78615c1';
+
+	# This is going to be provided to us by the HTTP endpoint listed above
+	my $auth_code     = $data->{code};
+    return badrequest() unless $auth_code;
+
+	my $oauth_data = {
+		client_id          => $client_id,
+		client_secret      => $client_secret,
+		authorization_code => $auth_code,
+	};
+
+    return [200, ['Content-type', 'text/plain'], [Dumper($oauth_data)]];
+
+	# Should give us an access_token and refresh_token
+	die Dumper($self->agent->request('POST', qq{$clover_uri/$oauth_endpoint}, $oauth_data ));
+
+	# TODO store this data in ye olde DB, and do cache invalidation etc
+
+}
+
+sub badrequest {
+    return _generic(400, "Bad Request");
+}
+
+sub error {
+	return _generic(500, "Internal Server Error");
+}
+
+sub unavailable {
+	return _generic(503, "Service Unavailable");
+}
+
+sub toolong {
+	return _generic(419, "Excessive URI Length");
+}
+
+sub _generic {
+	my ($code, $msg) = @_;
+	return [$code, [$ct => $content_types{html}], [$msg]];
+}
+
+sub proxy {
+	my ($self, $method, $route, $data) = @_;
+	die Dumper($self->agent->request($method, qq{$clover_uri/$route}, $data));
+}
+
+sub _parse_request {
+    my ($env) = @_;
+
+    my $query = URL::Encode::url_params_mixed( $env->{QUERY_STRING} ) if $env->{QUERY_STRING};
+
+    #Actually parse the POSTDATA and dump it into the QUERY object if this is a POST
+    if ( $env->{REQUEST_METHOD} eq 'POST' ) {
+
+        my $body = HTTP::Body->new( $env->{CONTENT_TYPE}, $env->{CONTENT_LENGTH} );
+        while ( $env->{'psgi.input'}->read( my $buf, $CHUNK_SIZE ) ) {
+            $body->add($buf);
+        }
+
+        @$query{ keys( %{ $body->param } ) }  = values( %{ $body->param } );
+        @$query{ keys( %{ $body->upload } ) } = values( %{ $body->upload } );
+    }
+
+    return $query;
+}
+
+# Just dispatch it
+sub app {
+	my ($self, $env) = @_;
+
+	return toolong() if length( $env->{REQUEST_URI} ) > 2048;
+
+	my $route = $env->{PATH_INFO};
+	my $method = $env->{REQUEST_METHOD};
+	my $data = _parse_request($env);
+
+	return $routes{$route}->($self, $data) if $routes{$route};
+	# Otherwise just proxy stuffs if we have a token
+	return unavailable() unless $self->_has_valid_token();
+	return $self->proxy($route, $method, $data);
+}
+
+my %options;
+
+# Don't ever put anything past $app
+our $app = sub {
+    my $self = __PACKAGE__->new(%options);
+    return eval { $self->app(@_) } || do {
+        my $env = shift;
+        $env->{'psgi.errors'}->print($@);
+
+        # Redact the stack trace past line 1, it usually has things which should not be shown
+        $self->{cur_query}->{message} = $@;
+        $self->{cur_query}->{message} =~ s/\n.*//g if $self->{cur_query}->{message};
+
+        return error($self->{cur_query});
+	};
+};