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.
		
		
		
		
		
			
		
			
				
					
					
						
							179 lines
						
					
					
						
							6.9 KiB
						
					
					
				
			
		
		
	
	
							179 lines
						
					
					
						
							6.9 KiB
						
					
					
				| package NGCP::Panel::Utils::Voucher;
 | |
| use Sipwise::Base;
 | |
| use Crypt::Rijndael;
 | |
| use MIME::Base64;
 | |
| use NGCP::Panel::Utils::DateTime;
 | |
| 
 | |
| 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,$package_id,$schema,$err_code,$entities,$resource) = @params{qw/c plain_code voucher_id now subscriber_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;
 | |
|     }
 | |
| 
 | |
|     $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;
 |