You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ngcp-panel/lib/NGCP/Panel/Utils/Voucher.pm

189 lines
7.5 KiB

package NGCP::Panel::Utils::Voucher;
use Sipwise::Base;
use Crypt::Rijndael;
use MIME::Base64;
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Contract qw();
sub encrypt_code {
my ($c, $plain) = @_;
my $key = $c->config->{vouchers}->{key};
my $iv = $c->config->{vouchers}->{iv};
# pkcs#5 padding to 16 bytes blocksize
my $pad = 16 - (length $plain) % 16;
$plain .= pack('C', $pad) x $pad;
my $cipher = Crypt::Rijndael->new(
$key,
Crypt::Rijndael::MODE_CBC()
);
$cipher->set_iv($iv);
my $encrypted = $cipher->encrypt($plain);
my $b64 = encode_base64($encrypted, '');
return $b64;
}
sub decrypt_code {
my ($c, $code) = @_;
my $key = $c->config->{vouchers}->{key};
my $iv = $c->config->{vouchers}->{iv};
my $cipher = Crypt::Rijndael->new(
$key,
Crypt::Rijndael::MODE_CBC()
);
$cipher->set_iv($iv);
my $encrypted = decode_base64($code);
my $plain = $cipher->decrypt($encrypted) . "";
# remove padding
$plain =~ s/[\x01-\x1e]*$//;
return $plain;
}
sub check_topup {
my %params = @_;
my ($c,$plain_code,$voucher_id,$now,$subscriber_id,$contract_id,$contract,$package_id,$schema,$err_code,$entities,$resource) = @params{qw/c plain_code voucher_id now subscriber_id contract_id contract package_id schema err_code entities resource/};
$schema //= $c->model('DB');
$now //= NGCP::Panel::Utils::DateTime::current_local;
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return 0; };
}
my $reseller_id;
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$reseller_id = $c->user->reseller_id;
}
if (defined $subscriber_id) {
my $subscriber = $schema->resultset('voip_subscribers')->find($subscriber_id);
unless($subscriber) {
if (defined $resource) {
$resource->{subscriber_id} = undef if exists $resource->{subscriber_id};
$resource->{subscriber}->{id} = undef if (exists $resource->{subscriber} && exists $resource->{subscriber}->{id});
}
return 0 unless &{$err_code}("Unknown subscriber ID $subscriber_id.");
}
$entities->{subscriber} = $subscriber if defined $entities;
$contract //= $subscriber->contract;
} elsif (defined $contract_id) {
$contract = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c)->find($contract_id) unless $contract;
unless($contract) {
if (defined $resource) {
$resource->{contract_id} = undef if exists $resource->{contract_id};
$resource->{contract}->{id} = undef if (exists $resource->{contract} && exists $resource->{contract}->{id});
}
return 0 unless &{$err_code}("Unknown contract ID $contract_id.");
}
}
$entities->{contract} = $contract if defined $entities;
unless($contract->status eq 'active') {
return 0 unless &{$err_code}('Customer contract is not active.');
}
unless($contract->contact->reseller) {
return 0 unless &{$err_code}('Contract is not a customer contract.');
}
# if reseller, check if subscriber_id belongs to the calling reseller
if($reseller_id && $reseller_id != $contract->contact->reseller_id) {
return 0 unless &{$err_code}('Subscriber customer contract belongs to another reseller.');
}
if (defined $plain_code || defined $voucher_id) {
my $voucher;
my $dtf = $schema->storage->datetime_parser;
if (defined $plain_code) {
$voucher = $schema->resultset('vouchers')->search_rs({
code => encrypt_code($c, $plain_code),
used_at => { '=' => \"'0000-00-00 00:00:00'" } , #used_by_subscriber_id => undef,
valid_until => { '>=' => $dtf->format_datetime($now) },
reseller_id => $contract->contact->reseller_id,
},{
for => 'update',
})->first;
unless($voucher) {
if (defined $resource) {
$resource->{voucher_id} = undef if exists $resource->{voucher_id};
$resource->{voucher}->{id} = undef if (exists $resource->{voucher} && exists $resource->{voucher}->{id});
}
return 0 unless &{$err_code}("Invalid voucher code '$plain_code', already used or expired.");
}
} else {
$voucher = $schema->resultset('vouchers')->search_rs({
id => $voucher_id,
used_at => { '=' => \"'0000-00-00 00:00:00'" }, #used_by_subscriber_id => undef,
valid_until => { '>=' => $dtf->format_datetime($now) },
reseller_id => $contract->contact->reseller_id,
},{
for => 'update',
})->first;
unless($voucher) {
if (defined $resource) {
$resource->{voucher_id} = undef if exists $resource->{voucher_id};
$resource->{voucher}->{id} = undef if (exists $resource->{voucher} && exists $resource->{voucher}->{id});
}
return 0 unless &{$err_code}("Invalid voucher ID $voucher_id, already used or expired.");
}
}
$entities->{voucher} = $voucher if defined $entities;
if($voucher->customer_id && $contract->id != $voucher->customer_id) {
return 0 unless &{$err_code}('Voucher is reserved for a different customer.');
}
unless($voucher->reseller_id == $contract->contact->reseller_id) {
return 0 unless &{$err_code}('Voucher belongs to another reseller.');
}
} else {
my $package = undef;
if (defined $package_id) {
$package = $schema->resultset('profile_packages')->find($package_id);
unless($package) {
if (defined $resource) {
$resource->{package_id} = undef if exists $resource->{package_id};
$resource->{package}->{id} = undef if (exists $resource->{package} && exists $resource->{package}->{id});
}
return 0 unless &{$err_code}("Unknown profile package ID $package_id.");
}
$entities->{package} = $package if defined $entities;
if ($package->reseller_id && $package->reseller_id != $contract->contact->reseller_id) {
return 0 unless &{$err_code}('Profile package belongs to another reseller.');
}
}
}
# TODO: add and check billing.vouchers.active flag for internal/emergency use
return 1;
}
sub get_datatable_cols {
my ($c,$hide_package) = @_;
return (
{ name => "id", "search" => 1, "title" => $c->loc("#") },
$c->user->billing_data ? { name => "code", "search" => 1, "title" => $c->loc("Code") } : (),
{ name => "amount", "search" => 1, "title" => $c->loc("Amount") },
{ name => "reseller.name", "search" => 1, "title" => $c->loc("Reseller") },
$hide_package ? () : { name => "profile_package.name", "search" => 1, "title" => $c->loc("Profile Package") },
#{ name => "customer_contact_email", "search" => 1, "title" => $c->loc("Reserved for Customer") },
{ name => "customer_id", "search" => 1, "title" => $c->loc("For Contract #") },
{ name => "valid_until", "search" => 1, "title" => $c->loc("Valid Until") },
{ name => "used_at", "search" => 1, "title" => $c->loc("Used At") },
{ name => "used_by_subscriber.id", "search" => 1, "title" => $c->loc("Used By Subscriber #") },
);
}
1;