| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- #!/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});
- };
- };
|