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;