Skip to content

Instantly share code, notes, and snippets.

@harshals
Last active August 18, 2022 13:15
Show Gist options
  • Save harshals/ac345fd6979fe2a5fb7fa1a398fe8430 to your computer and use it in GitHub Desktop.
Save harshals/ac345fd6979fe2a5fb7fa1a398fe8430 to your computer and use it in GitHub Desktop.
sample code to authenticate on einvoice sandbox
#!/usr/bin/perl
#clone of https://einv-apisandbox.nic.in/sample-code-in-java.html
# you need to save the public key of einv-apisandbox.nic.in in same folder
use Crypt::OpenSSL::Random qw/random_bytes/;
use Crypt::OpenSSL::RSA;
use Crypt::Mode::ECB;
use Crypt::PK::RSA;
use Mojo::File;
use Mojo::JSON qw/encode_json decode_json/;
use Mojo::Util qw/b64_encode b64_decode dumper/;
use Mojo::URL;
use Mojo::UserAgent;
use strict;
use warnings;
use feature 'say';
Crypt::OpenSSL::RSA->import_random_seed();
## Setting up bunch of defaults
use constant {
APP_KEY => random_bytes(32),
PUBLIC_KEY_FILE => 'einv_sandbox.pem', ## to be saved in same folder
CLIENT_ID => '<< Your Client ID >>',
CLIENT_SECRET => '<< Your Client Secret >>',
GSTIN => '<< Your Gstin >>',
USERNAME => '<< API Username >>',
PASSWORD => '<<API Password>>'
};
use constant {
DEFAULT_HEADERS => {
"client-id" => CLIENT_ID,
"client-secret" => CLIENT_SECRET,
"gstin" => GSTIN,
"KeepAlive" => "true",
"AllowAutoRedirect" => "false",
"Accept" => "application/json"
}
};
my $url = Mojo::URL->new;
my $ua = Mojo::UserAgent->new;
$url->scheme('https')
->host('einv-apisandbox.nic.in');
my ($token, $sek) = authenticate($url, $ua);
identify($url, $ua, $token, $sek, GSTIN);
cancelIRN($url, $ua, $token, $sek);
sub authenticate {
my ($url, $ua) = @_;
$url->path('/eivital/v1.04/auth');
my $path = Mojo::File->new(PUBLIC_KEY_FILE);
my $json = {
"username" => USERNAME,
"password" => PASSWORD,
"appkey" => b64_encode(APP_KEY,""),
"ForceRefreshAccessToken" => \0
};
#my $cipher = Crypt::OpenSSL::RSA->new_public_key($path->slurp);
#$cipher->use_pkcs1_padding();
#my $encrypted = $cipher->encrypt(b64_encode(encode_json($json)));
my $rsa_cipher = Crypt::PK::RSA->new(PUBLIC_KEY_FILE);
my $encrypted = $rsa_cipher->encrypt(b64_encode(encode_json($json)), "v1.5");
my $encoded_text = b64_encode($encrypted);
say "posting to $url";
my $tx = $ua->build_tx(POST => $url , json => { "Data" => $encoded_text } );
$tx->req->headers->from_hash(DEFAULT_HEADERS);
$tx = $ua->start($tx);
my $res = $tx->result;
if ($res->is_success) {
my $auth_data = decode_json($res->body)->{"Data"};
my $aes_cipher = new Crypt::Mode::ECB("AES", 1);
my $binary_sek = b64_decode($auth_data->{Sek});
my $decrypted_sek = $aes_cipher->decrypt($binary_sek, APP_KEY);
return ($auth_data->{AuthToken}, $decrypted_sek);
}
elsif ($res->is_error) { say $res->message }
elsif ($res->code == 301) { say $res->headers->location }
else { say 'Whatever...' };
}
sub identify {
my ($url, $ua, $token, $sek, $gstin ) = @_;
$url->path("/eivital/v1.04/Master/gstin/$gstin");
say "getting from $url";
my $tx = $ua->build_tx(GET => $url );
$tx->req->headers->from_hash(DEFAULT_HEADERS);
$tx->req->headers->header("AuthToken", $token);
$tx->req->headers->header("user_name", USERNAME);
$tx = $ua->start($tx);
my $res = $tx->result;
if ($res->is_success) { say dumper(decrypt($res->body, $sek)); }
elsif ($res->is_error) { say $res->message }
elsif ($res->code == 301) { say $res->headers->location }
else { say 'Whatever...' };
}
sub cancelIRN {
my ($url, $ua, $token, $sek) = @_;
$url->path("/eicore/v1.03/Invoice/Cancel");
my $payload = {
"Irn"=> "a5c12dca80e743321740b001fd70953e8738d109865d28ba4013750f2046d444",
"CnlRsn"=> "1",
"CnlRem"=> "cancel Remarks"
};
say "posting to $url";
my $tx = $ua->build_tx(POST => $url , json => { "Data" => encrypt($payload, $sek)} );
$tx->req->headers->from_hash(DEFAULT_HEADERS);
$tx->req->headers->header("AuthToken", $token);
$tx->req->headers->header("user_name", USERNAME);
$tx = $ua->start($tx);
my $res = $tx->result;
if ($res->is_success) { say $res->body }
elsif ($res->is_error) { say $res->message }
elsif ($res->code == 301) { say $res->headers->location }
else { say 'Whatever...' };
}
sub encrypt {
my ($data, $key) = @_;
my $encoded_json = encode_json($data);
my $aes_cipher = new Crypt::Mode::ECB("AES", 1);
my $encrypted_payload = $aes_cipher->encrypt($encoded_json, $key);
my $encoded_payload = b64_encode($encrypted_payload);
return $encoded_payload;
}
sub decrypt {
my ($data, $key, $data_key) = @_;
$data_key ||= "Data";
my $aes_cipher = new Crypt::Mode::ECB("AES", 1);
my $json = decode_json($data);
my $decrypted_data = $aes_cipher->decrypt(b64_decode($json->{$data_key}), $key);
return $decrypted_data;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment