mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Merge remote-tracking branch 'upstream/v2' into v2-2805-client-signup
This commit is contained in:
commit
22aa182250
3
.babelrc
Normal file
3
.babelrc
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"plugins": ["@babel/plugin-proposal-class-properties"]
|
||||
}
|
@ -43,6 +43,7 @@ MAIL_FROM_ADDRESS='user@example.com'
|
||||
MAIL_FROM_NAME='Self Hosted User'
|
||||
|
||||
POSTMARK_API_TOKEN=
|
||||
REQUIRE_HTTPS=true
|
||||
|
||||
GOOGLE_MAPS_API_KEY=
|
||||
API_SECRET=superdoopersecrethere
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -26,6 +26,5 @@ local_version.txt
|
||||
# Ignore local migrations
|
||||
storage/migrations
|
||||
nbproject
|
||||
/composer.lock
|
||||
|
||||
.php_cs.cache
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5"
|
||||
"trailingComma": "es5",
|
||||
"arrowParens": "always"
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ npm i
|
||||
npm run production
|
||||
```
|
||||
|
||||
Please Note: Your APP_KEY in the .env file is used to encrypt data, if you loose this you will not be able to run the application.
|
||||
Please Note: Your APP_KEY in the .env file is used to encrypt data, if you lose this you will not be able to run the application.
|
||||
|
||||
Run if you want to load sample data, remember to configure .env
|
||||
```
|
||||
|
@ -299,7 +299,7 @@ class CheckData extends Command
|
||||
|
||||
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
|
||||
|
||||
if($ledger && $invoice_balance != $client->balance)
|
||||
if($ledger && number_format($invoice_balance, 4) != number_format($client->balance, 4))
|
||||
{
|
||||
$wrong_balances++;
|
||||
$this->logMessage($client->present()->name . " - " . $client->id . " - balances do not match {$invoice_balance} - {$client->balance} - {$ledger->balance}");
|
||||
|
@ -25,6 +25,7 @@ use App\Models\PaymentType;
|
||||
use App\Models\Product;
|
||||
use App\Models\User;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
use Faker\Factory;
|
||||
@ -35,7 +36,7 @@ use Illuminate\Support\Str;
|
||||
|
||||
class CreateTestData extends Command
|
||||
{
|
||||
use MakesHash;
|
||||
use MakesHash, GeneratesCounter;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@ -233,7 +234,7 @@ class CreateTestData extends Command
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
|
||||
for($x=0; $x<$this->count; $x++)
|
||||
for($x=0; $x<$this->count*100; $x++)
|
||||
{
|
||||
$client = $company->clients->random();
|
||||
|
||||
@ -314,7 +315,7 @@ class CreateTestData extends Command
|
||||
]);
|
||||
|
||||
|
||||
factory(\App\Models\Product::class, 50)->create([
|
||||
factory(\App\Models\Product::class, 15000)->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
@ -324,7 +325,7 @@ class CreateTestData extends Command
|
||||
$this->info('Creating '.$this->count. ' clients');
|
||||
|
||||
|
||||
for ($x=0; $x<$this->count; $x++) {
|
||||
for ($x=0; $x<$this->count*1000; $x++) {
|
||||
$z = $x+1;
|
||||
$this->info("Creating client # ".$z);
|
||||
|
||||
@ -393,6 +394,10 @@ class CreateTestData extends Command
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id
|
||||
]);
|
||||
|
||||
$client->id_number = $this->getNextClientNumber($client);
|
||||
$client->save();
|
||||
|
||||
}
|
||||
|
||||
private function createExpense($client)
|
||||
|
@ -13,6 +13,7 @@ namespace App\Console;
|
||||
|
||||
use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
use App\Jobs\Ninja\CheckDbStatus;
|
||||
use App\Jobs\Util\ReminderJob;
|
||||
use App\Jobs\Util\SendFailedEmails;
|
||||
use App\Jobs\Util\UpdateExchangeRates;
|
||||
@ -52,6 +53,7 @@ class Kernel extends ConsoleKernel
|
||||
if(Ninja::isHosted()) {
|
||||
$schedule->job(new AdjustEmailQuota())->daily();
|
||||
$schedule->job(new SendFailedEmails())->daily();
|
||||
$schedule->job(new CheckDbStatus())->everyFiveMinutes();
|
||||
}
|
||||
/* Run queue's in shared hosting with this*/
|
||||
if (Ninja::isSelfHost()) {
|
||||
|
@ -33,7 +33,7 @@ class CompanySettings extends BaseSettings
|
||||
public $enable_client_portal_dashboard = true; //implemented
|
||||
public $signature_on_pdf = false;
|
||||
public $document_email_attachment = false;
|
||||
public $send_portal_password = false;
|
||||
//public $send_portal_password = false;
|
||||
|
||||
public $portal_design_id = '1';
|
||||
|
||||
@ -113,7 +113,7 @@ class CompanySettings extends BaseSettings
|
||||
public $invoice_terms = '';
|
||||
public $quote_terms = '';
|
||||
public $invoice_taxes = 0;
|
||||
public $enabled_item_tax_rates = 0;
|
||||
// public $enabled_item_tax_rates = 0;
|
||||
public $invoice_design_id = 'VolejRejNm';
|
||||
public $quote_design_id = 'VolejRejNm';
|
||||
public $credit_design_id = 'VolejRejNm';
|
||||
@ -274,7 +274,7 @@ class CompanySettings extends BaseSettings
|
||||
'email_template_statement' => 'string',
|
||||
'email_subject_statement' => 'string',
|
||||
'signature_on_pdf' => 'bool',
|
||||
'send_portal_password' => 'bool',
|
||||
// 'send_portal_password' => 'bool',
|
||||
'quote_footer' => 'string',
|
||||
'page_size' => 'string',
|
||||
'font_size' => 'int',
|
||||
@ -344,7 +344,7 @@ class CompanySettings extends BaseSettings
|
||||
'invoice_design_id' => 'string',
|
||||
'invoice_fields' => 'string',
|
||||
'invoice_taxes' => 'int',
|
||||
'enabled_item_tax_rates' => 'int',
|
||||
//'enabled_item_tax_rates' => 'int',
|
||||
'invoice_footer' => 'string',
|
||||
'invoice_labels' => 'string',
|
||||
'invoice_terms' => 'string',
|
||||
|
@ -33,7 +33,7 @@ class FreeCompanySettings extends BaseSettings
|
||||
public $custom_value3 = '';
|
||||
public $custom_value4 = '';
|
||||
public $date_format_id = '';
|
||||
public $enabled_item_tax_rates = 0;
|
||||
// public $enabled_item_tax_rates = 0;
|
||||
public $expense_number_pattern = '';
|
||||
public $expense_number_counter = 1;
|
||||
public $inclusive_taxes = false;
|
||||
|
@ -63,7 +63,7 @@ class Bold extends AbstractDesign
|
||||
</div>
|
||||
<div class="col-span-5">
|
||||
<div class="bg-teal-600 px-5 py-3 text-white">
|
||||
<div class="w-80 flex flex-col text-white">
|
||||
<div class="w-80 flex flex-col text-white flex-wrap">
|
||||
$entity_details
|
||||
</div>
|
||||
</div>
|
||||
@ -76,7 +76,7 @@ class Bold extends AbstractDesign
|
||||
<thead class="text-left">
|
||||
$product_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$product_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -84,7 +84,7 @@ class Bold extends AbstractDesign
|
||||
<thead class="text-left">
|
||||
$task_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$task_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -52,10 +52,10 @@ $custom_css
|
||||
<div class="col-span-2 p-3">
|
||||
$company_logo
|
||||
</div>
|
||||
<div class="col-span-2 p-3 flex flex-col">
|
||||
<div class="col-span-2 p-3 flex flex-col flex-wrap">
|
||||
$company_details
|
||||
</div>
|
||||
<div class="col-span-2 p-3 flex flex-col">
|
||||
<div class="col-span-2 p-3 flex flex-col flex-wrap">
|
||||
$company_address
|
||||
</div>
|
||||
</div>';
|
||||
@ -64,11 +64,11 @@ $custom_css
|
||||
public function body()
|
||||
{
|
||||
return '<div class="grid grid-cols-12 gap-1 mt-8">
|
||||
<div class="col-span-7 p-3 flex flex-col">
|
||||
<div class="col-span-7 p-3 flex flex-col flex-wrap">
|
||||
$client_details
|
||||
</div>
|
||||
<div class="col-span-5 p-3 flex flex-col bg-orange-600 px-4 py-4 h-auto rounded-lg">
|
||||
<div class="flex flex-col text-white">
|
||||
<div class="flex flex-col text-white flex-wrap">
|
||||
$entity_details
|
||||
</div>
|
||||
</div>
|
||||
@ -78,7 +78,7 @@ $custom_css
|
||||
<thead class="text-left">
|
||||
$product_table_header
|
||||
</thead>
|
||||
<tbody class="bg-gray-200">
|
||||
<tbody class="bg-gray-200 whitespace-pre-line">
|
||||
$product_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -86,7 +86,7 @@ $custom_css
|
||||
<thead class="text-left">
|
||||
$task_table_header
|
||||
</thead>
|
||||
<tbody class="bg-gray-200">
|
||||
<tbody class="bg-gray-200 whitespace-pre-line">
|
||||
$task_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -48,10 +48,10 @@ class Clean extends AbstractDesign
|
||||
<div class="h-14 w-14">$company_logo</div>
|
||||
</div>
|
||||
<div class="w-auto flex">
|
||||
<div class="mr-10 text-gray-600 flex flex-col">
|
||||
<div class="mr-10 text-gray-600 flex flex-col flex-wrap">
|
||||
$company_details
|
||||
</div>
|
||||
<div class="ml-5 text-gray-600 flex flex-col">
|
||||
<div class="ml-5 text-gray-600 flex flex-col flex-wrap">
|
||||
$company_address
|
||||
</div>
|
||||
</div>
|
||||
@ -68,13 +68,13 @@ class Clean extends AbstractDesign
|
||||
|
||||
<div class="ml-4 py-4">
|
||||
<div class="flex">
|
||||
<div class="w-40 flex flex-col">
|
||||
<div class="w-40 flex flex-col flex-wrap">
|
||||
$entity_labels
|
||||
</div>
|
||||
<div class="w-48 flex flex-col">
|
||||
<div class="w-48 flex flex-col flex-wrap">
|
||||
$entity_details
|
||||
</div>
|
||||
<div class="w-56 flex flex-col">
|
||||
<div class="w-56 flex flex-col flex-wrap">
|
||||
$client_details
|
||||
</div>
|
||||
</div>
|
||||
@ -84,7 +84,7 @@ class Clean extends AbstractDesign
|
||||
<thead class="text-left">
|
||||
$product_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$product_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -92,7 +92,7 @@ class Clean extends AbstractDesign
|
||||
<thead class="text-left">
|
||||
$task_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$task_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -60,14 +60,14 @@ $custom_css
|
||||
<div class="col-span-7">
|
||||
<p class="text-4xl text-pink-700">#$entity_number</p>
|
||||
</div>
|
||||
<div class="col-span-5 flex flex-col">$entity_details</div>
|
||||
<div class="col-span-5 flex flex-col flex-wrap">$entity_details</div>
|
||||
</div>
|
||||
|
||||
<table class="w-full table-auto border-t-4 border-pink-700 bg-white mt-8">
|
||||
<thead class="text-left rounded-lg">
|
||||
$product_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$product_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -75,7 +75,7 @@ $custom_css
|
||||
<thead class="text-left rounded-lg">
|
||||
$task_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$task_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -107,7 +107,7 @@ class Designer
|
||||
</div>'
|
||||
;
|
||||
|
||||
$signature = '<div></div>'; /** @wip */
|
||||
$signature = '<img class="h-40" src="$contact.signature" />';
|
||||
$logo = '<div></div>';
|
||||
|
||||
if (!$this->entity->user->account->isPaid()) {
|
||||
|
@ -47,7 +47,7 @@ class Elegant extends AbstractDesign
|
||||
<div class="col-span-8">
|
||||
$company_logo
|
||||
</div>
|
||||
<div class="col-span-4 flex flex-col">
|
||||
<div class="col-span-4 flex flex-col flex-wrap">
|
||||
$entity_details
|
||||
</div>
|
||||
</div>
|
||||
@ -57,13 +57,13 @@ class Elegant extends AbstractDesign
|
||||
public function body()
|
||||
{
|
||||
return '<div class="grid grid-cols-12 gap-4 mt-8">
|
||||
<div class="col-span-4 mr-6 flex flex-col pr-2 border-r border-dashed border-black">
|
||||
<div class="col-span-4 mr-6 flex flex-col pr-2 border-r border-dashed border-black flex-wrap">
|
||||
$client_details
|
||||
</div>
|
||||
<div class="col-span-4 flex flex-col mr-6">
|
||||
<div class="col-span-4 flex flex-col mr-6 flex-wrap">
|
||||
$company_details
|
||||
</div>
|
||||
<div class="col-span-4 flex flex-col">
|
||||
<div class="col-span-4 flex flex-col flex-wrap">
|
||||
$company_address
|
||||
</div>
|
||||
</div>
|
||||
@ -71,7 +71,7 @@ class Elegant extends AbstractDesign
|
||||
<thead class="text-left border-dashed border-b border-black">
|
||||
$product_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$product_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -79,7 +79,7 @@ class Elegant extends AbstractDesign
|
||||
<thead class="text-left border-dashed border-b border-black">
|
||||
$task_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$task_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -48,15 +48,15 @@ $custom_css
|
||||
<div class="w-1/2 border-l pl-4 border-black mr-4">
|
||||
<p class="font-semibold uppercase text-yellow-600">From:</p>
|
||||
<div class="flex">
|
||||
<div class="flex flex-col mr-5">
|
||||
<div class="flex flex-col mr-5 flex-wrap">
|
||||
$company_details
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col flex-wrap">
|
||||
$company_address
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/3 border-l pl-4 border-black flex flex-col">
|
||||
<div class="w-1/3 border-l pl-4 border-black flex flex-col flex-wrap">
|
||||
<p class="font-semibold uppercase text-yellow-600">To:</p>
|
||||
$client_details
|
||||
</div>
|
||||
@ -90,7 +90,7 @@ $custom_css
|
||||
<thead class="text-left">
|
||||
$product_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$product_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -98,7 +98,7 @@ $custom_css
|
||||
<thead class="text-left">
|
||||
$task_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$task_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -45,10 +45,10 @@ $custom_css
|
||||
<div class="col-span-2 p-3">
|
||||
<h1 class="text-white font-bold text-3xl">$company.name</h1>
|
||||
</div>
|
||||
<div class="col-span-2 p-3 flex flex-col text-white">
|
||||
<div class="col-span-2 p-3 flex flex-col text-white flex-wrap">
|
||||
$company_details
|
||||
</div>
|
||||
<div class="col-span-2 p-3 flex flex-col text-white">
|
||||
<div class="col-span-2 p-3 flex flex-col text-white flex-wrap">
|
||||
$entity_details
|
||||
</div>
|
||||
</div>
|
||||
@ -64,7 +64,7 @@ $custom_css
|
||||
<div class="col-span-2 p-3">
|
||||
$company_logo
|
||||
</div>
|
||||
<div class="col-span-3 p-3 flex flex-col">
|
||||
<div class="col-span-3 p-3 flex flex-col flex-wrap">
|
||||
$client_details
|
||||
</div>
|
||||
</div>
|
||||
@ -74,7 +74,7 @@ $custom_css
|
||||
<thead class="text-left text-white bg-gray-900 display: table-header-group;">
|
||||
$product_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$product_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -82,7 +82,7 @@ $custom_css
|
||||
<thead class="text-left text-white bg-gray-900 display: table-header-group;">
|
||||
$task_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$task_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -141,10 +141,10 @@ $custom_css
|
||||
return '
|
||||
<div class="footer bg-orange-600 flex justify-between py-8 px-12" style="page-break-inside: avoid;">
|
||||
<div class="grid grid-cols-12 gap-4">
|
||||
<div class="col-start-4 col-span-4 p-3 flex flex-col text-white text-right">
|
||||
<div class="col-start-4 col-span-4 p-3 flex flex-col text-white text-right flex-wrap">
|
||||
$company_details
|
||||
</div>
|
||||
<div class="col-span-4 p-3 flex flex-col text-white text-right">
|
||||
<div class="col-span-4 p-3 flex flex-col text-white text-right flex-wrap">
|
||||
$company_address
|
||||
</div>
|
||||
</div>
|
||||
|
@ -53,7 +53,7 @@ $custom_css
|
||||
$company_logo
|
||||
</div>
|
||||
<div class="col-span-5">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col flex-wrap">
|
||||
$entity_details
|
||||
</div>
|
||||
</div>
|
||||
@ -67,13 +67,13 @@ $custom_css
|
||||
<div class="flex flex-col">
|
||||
<div class="flex">
|
||||
<p class="uppercase text-orange-800">$to_label:</p>
|
||||
<div class="flex flex-col ml-2">
|
||||
<div class="flex flex-col ml-2 flex-wrap">
|
||||
$client_details
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mt-5">
|
||||
<p class="uppercase text-orange-800">$from_label:</p>
|
||||
<div class="flex flex-col ml-2">
|
||||
<div class="flex flex-col ml-2 flex-wrap">
|
||||
$company_details
|
||||
</div>
|
||||
</div>
|
||||
@ -84,7 +84,7 @@ $custom_css
|
||||
<thead class="text-left border-b-4 border-black">
|
||||
$product_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$product_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -92,7 +92,7 @@ $custom_css
|
||||
<thead class="text-left border-b-4 border-black">
|
||||
$task_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$task_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -45,10 +45,10 @@ $custom_css
|
||||
<div class="col-span-2 p-3">
|
||||
$company_logo
|
||||
</div>
|
||||
<div class="col-span-2 p-3 flex flex-col">
|
||||
<div class="col-span-2 p-3 flex flex-col flex-wrap">
|
||||
$company_details
|
||||
</div>
|
||||
<div class="col-span-2 p-3 flex flex-col">
|
||||
<div class="col-span-2 p-3 flex flex-col flex-wrap">
|
||||
$entity_details
|
||||
</div>
|
||||
</div>';
|
||||
@ -56,7 +56,7 @@ $custom_css
|
||||
|
||||
public function body()
|
||||
{
|
||||
return '<div class="flex flex-col mt-8">
|
||||
return '<div class="flex flex-col mt-8 flex-wrap">
|
||||
$client_details
|
||||
</div>
|
||||
<table class="w-full table-auto mt-8">
|
||||
|
@ -48,7 +48,7 @@ $custom_css
|
||||
$company_logo
|
||||
</div>
|
||||
<div class="col-span-5 bg-teal-600 p-5 text-white">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col flex-wrap">
|
||||
$entity_details
|
||||
</div>
|
||||
</div>
|
||||
@ -62,7 +62,7 @@ $custom_css
|
||||
<div class="flex flex-col">
|
||||
<p class="font-semibold text-teal-600 pl-4">$to_label:</p>
|
||||
<div class="flex border-dashed border-t-4 border-b-4 border-teal-600 py-4 mt-4 pl-4">
|
||||
<section class="flex flex-col">
|
||||
<section class="flex flex-col flex-wrap">
|
||||
$client_details
|
||||
</section>
|
||||
</div>
|
||||
@ -72,7 +72,7 @@ $custom_css
|
||||
<div class="flex flex-col">
|
||||
<p class="font-semibold text-teal-600 pl-4">$from_label:</p>
|
||||
<div class="flex border-dashed border-t-4 border-b-4 border-teal-600 py-4 mt-4 pl-4">
|
||||
<section class="flex flex-col">
|
||||
<section class="flex flex-col flex-wrap">
|
||||
$company_details
|
||||
</section>
|
||||
</div>
|
||||
@ -83,7 +83,7 @@ $custom_css
|
||||
<thead class="text-left bg-teal-600 rounded-lg">
|
||||
$product_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$product_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
@ -91,7 +91,7 @@ $custom_css
|
||||
<thead class="text-left bg-teal-600 rounded-lg">
|
||||
$task_table_header
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="whitespace-pre-line">
|
||||
$task_table_body
|
||||
</tbody>
|
||||
</table>
|
||||
|
11
app/Exceptions/GenericPaymentDriverFailure.php
Normal file
11
app/Exceptions/GenericPaymentDriverFailure.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class GenericPaymentDriverFailure extends Exception
|
||||
{
|
||||
// ..
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use App\Exceptions\GenericPaymentDriverFailure;
|
||||
use Exception;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
@ -115,8 +116,15 @@ class Handler extends ExceptionHandler
|
||||
return response()->json(['message' => 'The given data was invalid.', 'errors' => $exception->validator->getMessageBag()], 422);
|
||||
} elseif ($exception instanceof RelationNotFoundException && $request->expectsJson()) {
|
||||
return response()->json(['message' => $exception->getMessage()], 400);
|
||||
} elseif ($exception instanceof GenericPaymentDriverFailure && $request->expectsJson()) {
|
||||
return response()->json(['message' => $exception->getMessage()], 400);
|
||||
} elseif ($exception instanceof GenericPaymentDriverFailure) {
|
||||
$data['message'] = $exception->getMessage();
|
||||
dd($data);
|
||||
// return view('errors.layout', $data);
|
||||
}
|
||||
|
||||
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ class InvoiceItemSum
|
||||
}
|
||||
|
||||
private function sumLineItem()
|
||||
{
|
||||
{ //todo need to support quantities less than the precision amount
|
||||
$this->setLineTotal($this->formatValue($this->item->cost, $this->currency->precision) * $this->formatValue($this->item->quantity, $this->currency->precision));
|
||||
return $this;
|
||||
}
|
||||
|
@ -265,8 +265,8 @@ class BaseController extends Controller
|
||||
'company.payments.paymentables',
|
||||
'company.quotes.invitations.contact',
|
||||
'company.quotes.invitations.company',
|
||||
'company.credits',
|
||||
'company.payment_terms',
|
||||
'company.credits.invitations.company',
|
||||
'company.payment_terms.company',
|
||||
//'company.credits.invitations.contact',
|
||||
//'company.credits.invitations.company',
|
||||
'company.vendors.contacts',
|
||||
@ -291,7 +291,7 @@ class BaseController extends Controller
|
||||
* Thresholds for displaying large account on first load
|
||||
*/
|
||||
if (request()->has('first_load') && request()->input('first_load') == 'true') {
|
||||
if (auth()->user()->getCompany()->invoices->count() > 1000) {
|
||||
if (auth()->user()->getCompany()->invoices->count() > 1000 || auth()->user()->getCompany()->products->count() > 1000 || auth()->user()->getCompany()->clients->count() > 1000) {
|
||||
$data = $mini_load;
|
||||
} else {
|
||||
$data = $first_load;
|
||||
@ -314,11 +314,10 @@ class BaseController extends Controller
|
||||
|
||||
public function flutterRoute()
|
||||
{
|
||||
|
||||
// // Ensure all request are over HTTPS in production
|
||||
// if (! request()->secure()) {
|
||||
// return redirect()->secure(request()->path());
|
||||
// }
|
||||
|
||||
if (config('ninja.require_https') && !request()->isSecure()) {
|
||||
return redirect()->secure(request()->getRequestUri());
|
||||
}
|
||||
|
||||
if ((bool)$this->checkAppSetup() !== false && Schema::hasTable('accounts') && $account = Account::all()->first()) {
|
||||
$data = [];
|
||||
|
@ -52,6 +52,10 @@ class InvoiceController extends Controller
|
||||
'invoice' => $invoice,
|
||||
];
|
||||
|
||||
if ($request->query('mode') === 'fullscreen') {
|
||||
return $this->render('invoices.show.fullscreen', $data);
|
||||
}
|
||||
|
||||
return $this->render('invoices.show', $data);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\ClientPortal;
|
||||
|
||||
use App\Filters\PaymentFilters;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\Invoice\InjectSignature;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
@ -90,12 +91,18 @@ class PaymentController extends Controller
|
||||
$invoices->map(function ($invoice) {
|
||||
$invoice->balance = Number::formatMoney($invoice->balance, $invoice->client);
|
||||
$invoice->due_date = $this->formatDate($invoice->due_date, $invoice->client->date_format());
|
||||
|
||||
return $invoice;
|
||||
});
|
||||
|
||||
if ((bool) request()->signature) {
|
||||
$invoices->each(function ($invoice) {
|
||||
InjectSignature::dispatch($invoice, request()->signature);
|
||||
});
|
||||
}
|
||||
|
||||
$payment_methods = auth()->user()->client->getPaymentMethods($amount);
|
||||
$gateway = CompanyGateway::find(request()->input('company_gateway_id'));
|
||||
|
||||
$payment_method_id = request()->input('payment_method_id');
|
||||
|
||||
// Place to calculate gateway fee.
|
||||
@ -110,14 +117,19 @@ class PaymentController extends Controller
|
||||
'hashed_ids' => request()->invoices,
|
||||
];
|
||||
|
||||
|
||||
return $gateway->driver(auth()->user()->client)->processPaymentView($data);
|
||||
return $gateway
|
||||
->driver(auth()->user()->client)
|
||||
->setPaymentMethod($payment_method_id)
|
||||
->processPaymentView($data);
|
||||
}
|
||||
|
||||
public function response(Request $request)
|
||||
{
|
||||
$gateway = CompanyGateway::find($request->input('company_gateway_id'));
|
||||
|
||||
return $gateway->driver(auth()->user()->client)->processPaymentResponse($request);
|
||||
return $gateway
|
||||
->driver(auth()->user()->client)
|
||||
->setPaymentMethod($request->input('payment_method_id'))
|
||||
->processPaymentResponse($request);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ use App\Events\Payment\Methods\MethodDeleted;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\CreatePaymentMethodRequest;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\PaymentDrivers\AuthorizePaymentDriver;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@ -42,13 +44,8 @@ class PaymentMethodController extends Controller
|
||||
{
|
||||
$gateway = auth()->user()->client->getCreditCardGateway();
|
||||
|
||||
$data = [
|
||||
'gateway' => $gateway,
|
||||
'gateway_type_id' => 1,
|
||||
'token' => false,
|
||||
];
|
||||
return $gateway->driver(auth()->user()->client)->authorizeView(GatewayType::CREDIT_CARD);
|
||||
|
||||
return $gateway->driver(auth()->user()->client)->authorizeCreditCardView($data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,8 +57,9 @@ class PaymentMethodController extends Controller
|
||||
public function store(Request $request)
|
||||
{
|
||||
$gateway = auth()->user()->client->getCreditCardGateway();
|
||||
|
||||
return $gateway->driver(auth()->user()->client)->authorizeResponseView($request->all());
|
||||
|
||||
return $gateway->driver(auth()->user()->client)->authorizeCreditCardResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,6 +98,26 @@ class PaymentMethodController extends Controller
|
||||
//
|
||||
}
|
||||
|
||||
public function verify(ClientGatewayToken $payment_method)
|
||||
{
|
||||
$gateway = auth()->user()->client->getCreditCardGateway();
|
||||
|
||||
return $gateway
|
||||
->driver(auth()->user()->client)
|
||||
->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH')
|
||||
->verificationView($payment_method);
|
||||
}
|
||||
|
||||
public function processVerification(ClientGatewaytoken $payment_method)
|
||||
{
|
||||
$gateway = auth()->user()->client->getCreditCardGateway();
|
||||
|
||||
return $gateway
|
||||
->driver(auth()->user()->client)
|
||||
->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH')
|
||||
->processVerification($payment_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
|
@ -37,9 +37,15 @@ class QuoteController extends Controller
|
||||
*/
|
||||
public function show(ShowQuoteRequest $request, Quote $quote)
|
||||
{
|
||||
return $this->render('quotes.show', [
|
||||
$data = [
|
||||
'quote' => $quote,
|
||||
]);
|
||||
];
|
||||
|
||||
if ($request->query('mode') === 'fullscreen') {
|
||||
return $this->render('quotes.show.fullscreen', $data);
|
||||
}
|
||||
|
||||
return $this->render('quotes.show', $data);
|
||||
}
|
||||
|
||||
public function bulk(ProcessQuotesInBulkRequest $request)
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Http\Requests\Company\CreateCompanyRequest;
|
||||
use App\Http\Requests\Company\DestroyCompanyRequest;
|
||||
@ -218,6 +219,7 @@ class CompanyController extends BaseController
|
||||
'is_locked' => 0,
|
||||
'permissions' => '',
|
||||
'settings' => null,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
//'settings' => DefaultSettings::userSettings(),
|
||||
]);
|
||||
|
||||
@ -464,11 +466,12 @@ class CompanyController extends BaseController
|
||||
*/
|
||||
public function destroy(DestroyCompanyRequest $request, Company $company)
|
||||
{
|
||||
|
||||
$company_count = $company->account->companies->count();
|
||||
$account = $company->account;
|
||||
|
||||
if ($company_count == 1) {
|
||||
|
||||
|
||||
$company->company_users->each(function ($company_user) {
|
||||
$company_user->user->forceDelete();
|
||||
});
|
||||
@ -480,11 +483,13 @@ class CompanyController extends BaseController
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
$company_id = $company->id;
|
||||
$company->delete();
|
||||
|
||||
//If we are deleting the default companies, we'll need to make a new company the default.
|
||||
if ($account->default_company_id == $company_id) {
|
||||
|
||||
$new_default_company = Company::whereAccountId($account->id)->first();
|
||||
$account->default_company_id = $new_default_company->id;
|
||||
$account->save();
|
||||
|
@ -424,4 +424,73 @@ class CompanyGatewayController extends BaseController
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform bulk actions on the list view
|
||||
*
|
||||
* @param BulkCompanyGatewayRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/company_gateways/bulk",
|
||||
* operationId="bulkCompanyGateways",
|
||||
* tags={"company_gateways"},
|
||||
* summary="Performs bulk actions on an array of company_gateways",
|
||||
* description="",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/index"),
|
||||
* @OA\RequestBody(
|
||||
* description="Array of company gateway IDs",
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="application/json",
|
||||
* @OA\Schema(
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="integer",
|
||||
* description="Array of hashed IDs to be bulk 'actioned",
|
||||
* example="[0,1,2,3]",
|
||||
* ),
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="The Company Gateways response",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/CompanyGateway"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function bulk()
|
||||
{
|
||||
$action = request()->input('action');
|
||||
|
||||
$ids = request()->input('ids');
|
||||
|
||||
$company_gateways = CompanyGateway::withTrashed()->find($this->transformKeys($ids));
|
||||
|
||||
$company_gateways->each(function ($company_gateway, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $company_gateway)) {
|
||||
$this->company_repo->{$action}($company_gateway);
|
||||
}
|
||||
});
|
||||
|
||||
return $this->listResponse(CompanyGateway::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
}
|
||||
}
|
||||
|
@ -662,6 +662,13 @@ class InvoiceController extends BaseController
|
||||
case 'download':
|
||||
return response()->download(TempFile::path($invoice->pdf_file_path()), basename($invoice->pdf_file_path()));
|
||||
break;
|
||||
case 'restore':
|
||||
$this->invoice_repo->restore($invoice);
|
||||
|
||||
if (!$bulk) {
|
||||
return $this->listResponse($invoice);
|
||||
}
|
||||
break;
|
||||
case 'archive':
|
||||
$this->invoice_repo->archive($invoice);
|
||||
|
||||
|
@ -66,7 +66,6 @@
|
||||
* @OA\Property(property="invoice_terms", type="string", example="Invoice Terms are...", description="The default invoice terms"),
|
||||
* @OA\Property(property="quote_terms", type="string", example="Quote Terms are...", description="The default quote terms"),
|
||||
* @OA\Property(property="invoice_taxes", type="number", example="1", description="Taxes can be applied to the invoice"),
|
||||
* @OA\Property(property="enabled_item_tax_rates", type="number", example="1", description="Taxes can be applied to the invoice items"),
|
||||
* @OA\Property(property="invoice_design_id", type="string", example="1", description="The default design id (invoice, quote etc)"),
|
||||
* @OA\Property(property="quote_design_id", type="string", example="1", description="The default design id (invoice, quote etc)"),
|
||||
* @OA\Property(property="invoice_footer", type="string", example="1", description="The default invoice footer"),
|
||||
@ -130,7 +129,6 @@
|
||||
* @OA\Property(property="email_template_statement", type="string", example="template matter", description="____________"),
|
||||
* @OA\Property(property="email_subject_statement", type="string", example="subject matter", description="____________"),
|
||||
* @OA\Property(property="signature_on_pdf", type="boolean", example=false, description="____________"),
|
||||
* @OA\Property(property="send_portal_password", type="boolean", example=false, description="____________"),
|
||||
* @OA\Property(property="quote_footer", type="string", example="the quote footer", description="____________"),
|
||||
* @OA\Property(property="email_subject_custom1", type="string", example="Custom Subject 1", description="____________"),
|
||||
* @OA\Property(property="email_subject_custom2", type="string", example="Custom Subject 2", description="____________"),
|
||||
|
@ -12,7 +12,6 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use Codedge\Updater\UpdaterManager;
|
||||
use Composer\Factory;
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Installer;
|
||||
@ -61,8 +60,10 @@ class SelfUpdateController extends BaseController
|
||||
* )
|
||||
*
|
||||
*/
|
||||
public function update(UpdaterManager $updater)
|
||||
public function update()
|
||||
{
|
||||
define('STDIN',fopen("php://stdin","r"));
|
||||
|
||||
if (Ninja::isNinja()) {
|
||||
return response()->json(['message' => 'Self update not available on this system.'], 403);
|
||||
}
|
||||
|
@ -29,7 +29,8 @@ class Kernel extends HttpKernel
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\App\Http\Middleware\Cors::class,
|
||||
\App\Http\Middleware\Cors::class,
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
@ -54,6 +55,7 @@ class Kernel extends HttpKernel
|
||||
'bindings',
|
||||
'query_logging',
|
||||
\App\Http\Middleware\StartupCheck::class,
|
||||
# \App\Http\Middleware\Cors::class,
|
||||
],
|
||||
'contact' => [
|
||||
'throttle:60,1',
|
||||
|
@ -17,7 +17,7 @@ class CreditsTable extends Component
|
||||
public function render()
|
||||
{
|
||||
$query = Credit::query()
|
||||
->where('company_id', auth('contact')->user()->company->id)
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->paginate($this->per_page);
|
||||
|
||||
|
@ -30,6 +30,7 @@ class InvoicesTable extends Component
|
||||
|
||||
public function render()
|
||||
{
|
||||
|
||||
$query = Invoice::query()
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc');
|
||||
|
||||
@ -48,7 +49,7 @@ class InvoicesTable extends Component
|
||||
}
|
||||
|
||||
$query = $query
|
||||
->where('company_id', auth('contact')->user()->company->id)
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.invoices-table', [
|
||||
|
@ -24,7 +24,6 @@ class PaymentMethodsTable extends Component
|
||||
{
|
||||
$query = ClientGatewayToken::query()
|
||||
->with('gateway_type')
|
||||
->where('company_id', auth('contact')->user()->company->id)
|
||||
->where('client_id', $this->client->id)
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->paginate($this->per_page);
|
||||
|
@ -24,7 +24,7 @@ class PaymentsTable extends Component
|
||||
{
|
||||
$query = Payment::query()
|
||||
->with('type', 'client')
|
||||
->where('company_id', auth('contact')->user()->company->id)
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->paginate($this->per_page);
|
||||
|
||||
|
@ -46,7 +46,7 @@ class QuotesTable extends Component
|
||||
}
|
||||
|
||||
$query = $query
|
||||
->where('company_id', auth('contact')->user()->company->id)
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.quotes-table', [
|
||||
|
@ -18,7 +18,7 @@ class RecurringInvoicesTable extends Component
|
||||
$query = RecurringInvoice::query();
|
||||
|
||||
$query = $query
|
||||
->where('company_id', auth('contact')->user()->company->id)
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_COMPLETED])
|
||||
->orderBy('status_id', 'asc')
|
||||
->with('client')
|
||||
|
@ -37,6 +37,7 @@ class Cors
|
||||
$response->headers->set('Access-Control-Allow-Origin', '*');
|
||||
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
$response->headers->set('Access-Control-Allow-Headers', 'X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range');
|
||||
$response->headers->set('Access-Control-Expose-Headers', 'X-APP-VERSION');
|
||||
$response->headers->set('X-APP-VERSION', config('ninja.app_version'));
|
||||
$response->headers->set('X-API-VERSION', config('ninja.api_version'));
|
||||
|
||||
|
@ -50,8 +50,8 @@ class QueryLogging
|
||||
|
||||
Log::info($request->method() . ' - ' . $request->url() . ": $count queries - " . $time);
|
||||
|
||||
// if($count > 50)
|
||||
// Log::info($queries);
|
||||
//if($count > 10)
|
||||
// Log::info($queries);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
namespace App\Http\Requests\Company;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Company;
|
||||
|
||||
class DestroyCompanyRequest extends Request
|
||||
{
|
||||
|
@ -67,6 +67,10 @@ class StoreInvoiceRequest extends Request
|
||||
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
|
||||
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
|
||||
}
|
||||
|
||||
if (isset($input['client_contacts'])) {
|
||||
foreach ($input['client_contacts'] as $key => $contact) {
|
||||
if (!array_key_exists('send_email', $contact) || !array_key_exists('id', $contact)) {
|
||||
|
@ -65,6 +65,10 @@ class UpdateInvoiceRequest extends Request
|
||||
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
|
||||
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
|
||||
}
|
||||
|
||||
if (isset($input['invitations'])) {
|
||||
foreach ($input['invitations'] as $key => $value) {
|
||||
if (is_numeric($input['invitations'][$key]['id'])) {
|
||||
|
@ -35,9 +35,10 @@ class UpdatePaymentRequest extends Request
|
||||
|
||||
|
||||
public function rules()
|
||||
{
|
||||
{//min:1 removed
|
||||
return [
|
||||
'invoices' => ['required','array','min:1',new PaymentAppliedValidAmount,new ValidCreditsPresentRule],
|
||||
'invoices' => ['required','array',new PaymentAppliedValidAmount,new ValidCreditsPresentRule],
|
||||
'invoices.*.invoice_id' => 'distinct',
|
||||
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
||||
];
|
||||
}
|
||||
@ -77,4 +78,11 @@ class UpdatePaymentRequest extends Request
|
||||
}
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'distinct' => 'Attemping duplicate payment on the same invoice Invoice',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ class PortalComposer
|
||||
$data['company'] = auth()->user()->company;
|
||||
$data['client'] = auth()->user()->client;
|
||||
$data['settings'] = auth()->user()->client->getMergedSettings();
|
||||
$data['currencies'] = TranslationHelper::getCurrencies();
|
||||
|
||||
$data['multiple_contacts'] = ClientContact::where('email', auth('contact')->user()->email)->whereNotNull('email')->distinct('company_id')->get();
|
||||
|
||||
@ -63,9 +64,9 @@ class PortalComposer
|
||||
$data[] = [ 'title' => ctrans('texts.invoices'), 'url' => 'client.invoices.index', 'icon' => 'file-text'];
|
||||
$data[] = [ 'title' => ctrans('texts.recurring_invoices'), 'url' => 'client.recurring_invoices.index', 'icon' => 'file'];
|
||||
$data[] = [ 'title' => ctrans('texts.payments'), 'url' => 'client.payments.index', 'icon' => 'credit-card'];
|
||||
$data[] = [ 'title' => ctrans('texts.payment_methods'), 'url' => 'client.payment_methods.index', 'icon' => 'shield'];
|
||||
$data[] = [ 'title' => ctrans('texts.quotes'), 'url' => 'client.quotes.index', 'icon' => 'align-left'];
|
||||
$data[] = [ 'title' => ctrans('texts.credits'), 'url' => 'client.credits.index', 'icon' => 'credit-card'];
|
||||
$data[] = [ 'title' => ctrans('texts.payment_methods'), 'url' => 'client.payment_methods.index', 'icon' => 'shield'];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -50,14 +50,14 @@ class CreateCompanyPaymentTerms
|
||||
{
|
||||
|
||||
$paymentTerms = [
|
||||
['num_days' => 0, 'name' => 'Net 0', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 7, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 10, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 14, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 15, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 30, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 60, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 90, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 0, 'name' => 'Net 0', 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
|
||||
['num_days' => 7, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
|
||||
['num_days' => 10, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
|
||||
['num_days' => 14, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
|
||||
['num_days' => 15, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
|
||||
['num_days' => 30, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
|
||||
['num_days' => 60, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
|
||||
['num_days' => 90, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
|
||||
];
|
||||
|
||||
PaymentTerm::insert($paymentTerms);
|
||||
|
56
app/Jobs/Invoice/InjectSignature.php
Normal file
56
app/Jobs/Invoice/InjectSignature.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Invoice;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class InjectSignature implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var App\Models\Invoice
|
||||
*/
|
||||
public $invoice;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $signature;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Invoice $invoice, string $signature)
|
||||
{
|
||||
$this->invoice = $invoice;
|
||||
|
||||
$this->signature = $signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$invitation = $this->invoice->invitations->whereNotNull('signature_base64')->first();
|
||||
|
||||
if (!$invitation) {
|
||||
return;
|
||||
}
|
||||
|
||||
$invitation->signature_base64 = $this->signature;
|
||||
$invitation->save();
|
||||
|
||||
CreateInvoicePdf::dispatch($invitation);
|
||||
}
|
||||
}
|
54
app/Jobs/Ninja/CheckDbStatus.php
Normal file
54
app/Jobs/Ninja/CheckDbStatus.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Ninja;
|
||||
|
||||
use App\Helpers\Email\InvoiceEmail;
|
||||
use App\Jobs\Invoice\EmailInvoice;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Models\SystemLog;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Turbo124\Beacon\Jobs\Database\MySQL\DbStatus;
|
||||
|
||||
class CheckDbStatus implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
DbStatus::dispatchNow('db-ninja-01', 'db.status.db-ninja-01');
|
||||
DbStatus::dispatchNow('db-ninja-02', 'db.status.db-ninja-02');
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -37,7 +37,7 @@ class RefundCancelledAccount implements ShouldQueue
|
||||
if(Ninja::isSelfHost() || $this->account->isFreeHostedClient())
|
||||
return;
|
||||
|
||||
$plan_details = $account->getPlanDetails();
|
||||
$plan_details = $this->account->getPlanDetails();
|
||||
|
||||
/* Trial user cancelling early.... */
|
||||
if($plan_details['trial_active'])
|
||||
|
@ -43,7 +43,7 @@ class SubscriptionHandler implements ShouldQueue
|
||||
if(!$this->entity->company || $this->entity->company->company_users->first()->is_migrating)
|
||||
return true;
|
||||
|
||||
info("i got past the check");
|
||||
//info("i got past the check");
|
||||
|
||||
$subscriptions = Subscription::where('company_id', $this->entity->company_id)
|
||||
->where('event_id', $this->event_id)
|
||||
|
@ -47,6 +47,9 @@ class UploadAvatar implements ShouldQueue
|
||||
|
||||
$path = Storage::putFile('public/' . $this->directory, new File(sys_get_temp_dir().'/'.$tmp_file));
|
||||
|
||||
info($path);
|
||||
info($tmp_file);
|
||||
|
||||
$url = Storage::url($path);
|
||||
|
||||
//return file path
|
||||
|
@ -48,6 +48,6 @@ class CreateInvoiceActivity implements ShouldQueue
|
||||
$fields->company_id = $event->invoice->company_id;
|
||||
$fields->activity_type_id = Activity::CREATE_INVOICE;
|
||||
|
||||
$this->activity_repo->save($fields, $event->invoice);
|
||||
$this->activity_repo->save($fields, $event->invoice, $event->invoice->company->db);
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,10 @@ class CreateInvoicePdf implements ShouldQueue
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
PdfCreator::dispatch($event->invoice->invitations->first());
|
||||
$event->invoice->invitations->each(function ($invitation) {
|
||||
|
||||
PdfCreator::dispatch($invitation);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,8 @@ class UpdateInvoiceActivity implements ShouldQueue
|
||||
$fields->user_id = $event->invoice->user_id;
|
||||
$fields->company_id = $event->invoice->company_id;
|
||||
$fields->activity_type_id = Activity::UPDATE_INVOICE;
|
||||
|
||||
$fields->invoice_id = $event->invoice->id;
|
||||
|
||||
$this->activity_repo->save($fields, $event->invoice);
|
||||
}
|
||||
}
|
||||
|
@ -251,12 +251,12 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
* @param float $amount Adjustment amount
|
||||
* @return Client
|
||||
*/
|
||||
public function processUnappliedPayment($amount) :Client
|
||||
{
|
||||
return $this->service()->updatePaidToDate($amount)
|
||||
->adjustCreditBalance($amount)
|
||||
->save();
|
||||
}
|
||||
// public function processUnappliedPayment($amount) :Client
|
||||
// {
|
||||
// return $this->service()->updatePaidToDate($amount)
|
||||
// ->adjustCreditBalance($amount)
|
||||
// ->save();
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
@ -507,4 +507,9 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
public function payments()
|
||||
{
|
||||
return $this->hasMany(Payment::class);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ class ClientGatewayToken extends BaseModel
|
||||
'deleted_at' => 'timestamp',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'hashed_id',
|
||||
];
|
||||
public function getEntityType()
|
||||
{
|
||||
return ClientGatewayToken::class;
|
||||
|
@ -17,9 +17,12 @@ use App\Models\Gateway;
|
||||
use App\Models\GatewayType;
|
||||
use App\Utils\Number;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class CompanyGateway extends BaseModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $casts = [
|
||||
'fees_and_limits' => 'object',
|
||||
'updated_at' => 'timestamp',
|
||||
|
@ -70,6 +70,7 @@ class Credit extends BaseModel
|
||||
|
||||
protected $casts = [
|
||||
'line_items' => 'object',
|
||||
'backup' => 'object',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
|
@ -25,5 +25,7 @@ class Currency extends StaticModel
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
//'precision' => 'string',
|
||||
'precision' => 'integer',
|
||||
];
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Jobs\Client\UpdateClientBalance;
|
||||
use App\Jobs\Company\UpdateCompanyLedgerWithInvoice;
|
||||
use App\Jobs\Invoice\CreateInvoicePdf;
|
||||
use App\Models\Backup;
|
||||
use App\Models\CompanyLedger;
|
||||
use App\Models\Currency;
|
||||
use App\Models\Filterable;
|
||||
@ -98,10 +99,12 @@ class Invoice extends BaseModel
|
||||
'custom_surcharge_tax3',
|
||||
'custom_surcharge_tax4',
|
||||
'design_id',
|
||||
'assigned_user_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'line_items' => 'object',
|
||||
'backup' => 'object',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
@ -208,7 +211,7 @@ class Invoice extends BaseModel
|
||||
|
||||
public function history()
|
||||
{
|
||||
$this->activities->with('backup');
|
||||
return $this->hasManyThrough(Backup::class, Activity::class);
|
||||
}
|
||||
|
||||
// public function credits()
|
||||
|
@ -201,7 +201,9 @@ class Payment extends BaseModel
|
||||
|
||||
public function refund(array $data) :Payment
|
||||
{
|
||||
return $this->processRefund($data);
|
||||
return $this->service()->refundPayment($data);
|
||||
|
||||
//return $this->processRefund($data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,4 +25,14 @@ class ClientContactPresenter extends EntityPresenter
|
||||
{
|
||||
return $this->entity->first_name . ' ' . $this->entity->last_name;
|
||||
}
|
||||
|
||||
public function first_name()
|
||||
{
|
||||
return $this->entity->first_name ?: '';
|
||||
}
|
||||
|
||||
public function last_name()
|
||||
{
|
||||
return $this->entity->last_name ?: '';
|
||||
}
|
||||
}
|
||||
|
@ -25,15 +25,20 @@ class ClientPresenter extends EntityPresenter
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
if($this->entity->name)
|
||||
return $this->entity->name;
|
||||
|
||||
$contact = $this->entity->primary_contact->first();
|
||||
|
||||
$contact_name = 'No Contact Set';
|
||||
|
||||
if ($contact) {
|
||||
if ($contact && (strlen($contact->first_name) >=1 || strlen($contact->last_name) >=1)) {
|
||||
$contact_name = $contact->first_name. ' '. $contact->last_name;
|
||||
}
|
||||
elseif($contact && (strlen($contact->email)))
|
||||
$contact_name = $contact->email;
|
||||
|
||||
return $this->entity->name ?: $contact_name;
|
||||
return $contact_name;
|
||||
}
|
||||
|
||||
public function primary_contact_name()
|
||||
|
@ -38,7 +38,7 @@ class CompanyPresenter extends EntityPresenter
|
||||
$settings = $this->entity->settings;
|
||||
}
|
||||
|
||||
return iconv_strlen($settings->company_logo > 0) ? $settings->company_logo : 'https://www.invoiceninja.com/wp-content/uploads/2019/01/InvoiceNinja-Logo-Round-300x300.png';
|
||||
return (strlen($settings->company_logo) > 0) ? $settings->company_logo : 'https://www.invoiceninja.com/wp-content/uploads/2019/01/InvoiceNinja-Logo-Round-300x300.png';
|
||||
}
|
||||
|
||||
public function address($settings = null)
|
||||
|
@ -76,6 +76,7 @@ class Quote extends BaseModel
|
||||
'due_date' => 'date:Y-m-d',
|
||||
'partial_due_date' => 'date:Y-m-d',
|
||||
'line_items' => 'object',
|
||||
'backup' => 'object',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
@ -84,6 +85,7 @@ class Quote extends BaseModel
|
||||
const STATUS_DRAFT = 1;
|
||||
const STATUS_SENT = 2;
|
||||
const STATUS_APPROVED = 3;
|
||||
const STATUS_CONVERTED = 4;
|
||||
const STATUS_EXPIRED = -1;
|
||||
|
||||
public function getEntityType()
|
||||
|
@ -101,6 +101,7 @@ class RecurringInvoice extends BaseModel
|
||||
protected $casts = [
|
||||
'settings' => 'object',
|
||||
'line_items' => 'object',
|
||||
'backup' => 'object',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
|
@ -83,6 +83,7 @@ class RecurringQuote extends BaseModel
|
||||
|
||||
protected $casts = [
|
||||
'line_items' => 'object',
|
||||
'backup' => 'object',
|
||||
'settings' => 'object',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
|
@ -35,6 +35,8 @@ class SystemLog extends Model
|
||||
const TYPE_STRIPE = 301;
|
||||
const TYPE_LEDGER = 302;
|
||||
const TYPE_FAILURE = 303;
|
||||
const TYPE_CHECKOUT = 304;
|
||||
const TYPE_AUTHORIZE = 305;
|
||||
|
||||
const TYPE_QUOTA_EXCEEDED = 400;
|
||||
const TYPE_UPSTREAM_FAILURE = 401;
|
||||
|
25
app/PaymentDrivers/AbstractPaymentDriver.php
Normal file
25
app/PaymentDrivers/AbstractPaymentDriver.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
abstract class AbstractPaymentDriver
|
||||
{
|
||||
|
||||
abstract public function authorize($payment_method);
|
||||
|
||||
abstract public function purchase($amount, $return_client_response = false);
|
||||
|
||||
abstract public function refund($amount, $transaction_reference, $return_client_response = false);
|
||||
|
||||
abstract public function setPaymentMethod($payment_method_id);
|
||||
|
||||
}
|
89
app/PaymentDrivers/Authorize/AuthorizeCreateCustomer.php
Normal file
89
app/PaymentDrivers/Authorize/AuthorizeCreateCustomer.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Authorize;
|
||||
|
||||
use App\Exceptions\GenericPaymentDriverFailure;
|
||||
use App\Models\Client;
|
||||
use App\Models\GatewayType;
|
||||
use App\PaymentDrivers\AuthorizePaymentDriver;
|
||||
use net\authorize\api\constants\ANetEnvironment;
|
||||
use net\authorize\api\contract\v1\CreateCustomerProfileRequest;
|
||||
use net\authorize\api\contract\v1\CustomerAddressType;
|
||||
use net\authorize\api\contract\v1\CustomerPaymentProfileType;
|
||||
use net\authorize\api\contract\v1\CustomerProfileType;
|
||||
use net\authorize\api\controller\CreateCustomerProfileController;
|
||||
/**
|
||||
* Class BaseDriver
|
||||
* @package App\PaymentDrivers
|
||||
*
|
||||
*/
|
||||
class AuthorizeCreateCustomer
|
||||
{
|
||||
|
||||
public $authorize;
|
||||
|
||||
public $client;
|
||||
|
||||
public function __construct(AuthorizePaymentDriver $authorize, Client $client)
|
||||
{
|
||||
$this->authorize = $authorize;
|
||||
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
public function create($data = null)
|
||||
{
|
||||
error_reporting (E_ALL & ~E_DEPRECATED);
|
||||
|
||||
$this->authorize->init();
|
||||
// Create the Bill To info for new payment type
|
||||
|
||||
$contact = $this->client->primary_contact()->first();
|
||||
$refId = 'ref' . time();
|
||||
|
||||
// Create a new CustomerProfileType and add the payment profile object
|
||||
$customerProfile = new CustomerProfileType();
|
||||
$customerProfile->setDescription($this->client->present()->name());
|
||||
$customerProfile->setMerchantCustomerId("M_" . time());
|
||||
$customerProfile->setEmail($this->client->present()->email());
|
||||
|
||||
// Assemble the complete transaction request
|
||||
$request = new CreateCustomerProfileRequest();
|
||||
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
|
||||
$request->setRefId($refId);
|
||||
$request->setProfile($customerProfile);
|
||||
|
||||
// Create the controller and get the response
|
||||
$controller = new CreateCustomerProfileController($request);
|
||||
$response = $controller->executeWithApiResponse($this->authorize->mode());
|
||||
|
||||
if (($response != null) && ($response->getMessages()->getResultCode() == "Ok")) {
|
||||
|
||||
return $response->getCustomerProfileId();
|
||||
|
||||
} else {
|
||||
|
||||
$errorMessages = $response->getMessages()->getMessage();
|
||||
|
||||
$message = "Unable to add customer to Authorize.net gateway";
|
||||
|
||||
if(is_array($errorMessages))
|
||||
$message = $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText();
|
||||
|
||||
throw new GenericPaymentDriverFailure($message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
170
app/PaymentDrivers/Authorize/AuthorizeCreditCard.php
Normal file
170
app/PaymentDrivers/Authorize/AuthorizeCreditCard.php
Normal file
@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Authorize;
|
||||
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\AuthorizePaymentDriver;
|
||||
use App\PaymentDrivers\Authorize\AuthorizeCreateCustomer;
|
||||
use App\PaymentDrivers\Authorize\AuthorizePaymentMethod;
|
||||
use App\PaymentDrivers\Authorize\ChargePaymentProfile;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* Class AuthorizeCreditCard
|
||||
* @package App\PaymentDrivers\Authorize
|
||||
*
|
||||
*/
|
||||
class AuthorizeCreditCard
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public $authorize;
|
||||
|
||||
public function __construct(AuthorizePaymentDriver $authorize)
|
||||
{
|
||||
$this->authorize = $authorize;
|
||||
}
|
||||
|
||||
public function processPaymentView($data)
|
||||
{
|
||||
$tokens = ClientGatewayToken::where('client_id', $this->authorize->client->id)
|
||||
->where('company_gateway_id', $this->authorize->company_gateway->id)
|
||||
->where('gateway_type_id', GatewayType::CREDIT_CARD)
|
||||
->get();
|
||||
|
||||
$data['tokens'] = $tokens;
|
||||
$data['gateway'] = $this->authorize->company_gateway;
|
||||
$data['public_client_id'] = $this->authorize->init()->getPublicClientKey();
|
||||
$data['api_login_id'] = $this->authorize->company_gateway->getConfigField('apiLoginId');
|
||||
|
||||
return render('gateways.authorize.credit_card_payment', $data);
|
||||
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
if($request->token)
|
||||
return $this->processTokenPayment($request);
|
||||
|
||||
$data = $request->all();
|
||||
|
||||
$authorise_create_customer = new AuthorizeCreateCustomer($this->authorize, $this->authorize->client);
|
||||
|
||||
$gateway_customer_reference = $authorise_create_customer->create($data);
|
||||
|
||||
info($gateway_customer_reference);
|
||||
|
||||
$authorise_payment_method = new AuthorizePaymentMethod($this->authorize);
|
||||
|
||||
$payment_profile = $authorise_payment_method->addPaymentMethodToClient($gateway_customer_reference, $data);
|
||||
$payment_profile_id = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId();
|
||||
|
||||
info($request->input('store_card'));
|
||||
|
||||
if($request->has('store_card') && $request->input('store_card') === 'true'){
|
||||
$authorise_payment_method->payment_method = GatewayType::CREDIT_CARD;
|
||||
$client_gateway_token = $authorise_payment_method->createClientGatewayToken($payment_profile, $gateway_customer_reference);
|
||||
}
|
||||
|
||||
$data = (new ChargePaymentProfile($this->authorize))->chargeCustomerProfile($gateway_customer_reference, $payment_profile_id, $data['amount_with_fee']);
|
||||
|
||||
return $this->handleResponse($data, $request);
|
||||
|
||||
}
|
||||
|
||||
private function processTokenPayment($request)
|
||||
{
|
||||
$client_gateway_token = ClientGatewayToken::find($this->decodePrimaryKey($request->token));
|
||||
|
||||
$data = (new ChargePaymentProfile($this->authorize))->chargeCustomerProfile($client_gateway_token->gateway_customer_reference, $client_gateway_token->token, $request->input('amount_with_fee'));
|
||||
|
||||
return $this->handleResponse($data, $request);
|
||||
}
|
||||
|
||||
private function handleResponse($data, $request)
|
||||
{
|
||||
//info(print_r( $response->getTransactionResponse()->getMessages(),1));
|
||||
|
||||
$response = $data['response'];
|
||||
|
||||
if($response != null && $response->getMessages()->getResultCode() == "Ok")
|
||||
return $this->processSuccessfulResponse($data, $request);
|
||||
|
||||
return $this->processFailedResponse($data, $request);
|
||||
}
|
||||
|
||||
private function processSuccessfulResponse($data, $request)
|
||||
{
|
||||
$response = $data['response'];
|
||||
//create a payment record and fire notifications and then return
|
||||
|
||||
$payment = PaymentFactory::create($this->authorize->client->company_id, $this->authorize->client->user_id);
|
||||
$payment->client_id = $this->authorize->client->id;
|
||||
$payment->company_gateway_id = $this->authorize->company_gateway->id;
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->type_id = PaymentType::CREDIT_CARD_OTHER;
|
||||
$payment->currency_id = $this->authorize->client->getSetting('currency_id');
|
||||
$payment->date = Carbon::now();
|
||||
$payment->transaction_reference = $response->getTransactionResponse()->getTransId();
|
||||
$payment->amount = $request->input('amount_with_fee');
|
||||
$payment->currency_id = $this->authorize->client->getSetting('currency_id');
|
||||
$payment->client->getNextPaymentNumber($this->authorize->client);
|
||||
$payment->save();
|
||||
|
||||
|
||||
$this->authorize->attachInvoices($payment, $request->hashed_ids);
|
||||
|
||||
$payment->service()->updateInvoicePayment();
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company));
|
||||
|
||||
$logger_message = [
|
||||
'server_response' => $response->getTransactionResponse()->getTransId(),
|
||||
'data' => $this->formatGatewayResponse($data, $request)
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
|
||||
|
||||
}
|
||||
|
||||
private function processFailedResponse($data, $request)
|
||||
{ dd($data);
|
||||
info(print_r($data,1));
|
||||
}
|
||||
|
||||
private function formatGatewayResponse($data, $request)
|
||||
{
|
||||
$response = $data['response'];
|
||||
|
||||
return [
|
||||
'transaction_reference' => $response->getTransactionResponse()->getTransId(),
|
||||
'amount' => $request->input('amount'),
|
||||
'auth_code' => $response->getTransactionResponse()->getAuthCode(),
|
||||
'code' => $response->getTransactionResponse()->getMessages()[0]->getCode(),
|
||||
'description' => $response->getTransactionResponse()->getMessages()[0]->getDescription(),
|
||||
'invoices' => $request->hashed_ids,
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
}
|
267
app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php
Normal file
267
app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php
Normal file
@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Authorize;
|
||||
|
||||
use App\Exceptions\GenericPaymentDriverFailure;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\PaymentDrivers\AuthorizePaymentDriver;
|
||||
use App\PaymentDrivers\Authorize\AuthorizeCreateCustomer;
|
||||
use net\authorize\api\contract\v1\CreateCustomerPaymentProfileRequest;
|
||||
use net\authorize\api\contract\v1\CreateCustomerProfileRequest;
|
||||
use net\authorize\api\contract\v1\CustomerAddressType;
|
||||
use net\authorize\api\contract\v1\CustomerPaymentProfileType;
|
||||
use net\authorize\api\contract\v1\CustomerProfileType;
|
||||
use net\authorize\api\contract\v1\GetCustomerPaymentProfileRequest;
|
||||
use net\authorize\api\contract\v1\OpaqueDataType;
|
||||
use net\authorize\api\contract\v1\PaymentType;
|
||||
use net\authorize\api\controller\CreateCustomerPaymentProfileController;
|
||||
use net\authorize\api\controller\CreateCustomerProfileController;
|
||||
use net\authorize\api\controller\GetCustomerPaymentProfileController;
|
||||
|
||||
/**
|
||||
* Class AuthorizePaymentMethod
|
||||
* @package App\PaymentDrivers\AuthorizePaymentMethod
|
||||
*
|
||||
*/
|
||||
class AuthorizePaymentMethod
|
||||
{
|
||||
public $authorize;
|
||||
|
||||
public $payment_method;
|
||||
|
||||
public function __construct(AuthorizePaymentDriver $authorize)
|
||||
{
|
||||
$this->authorize = $authorize;
|
||||
}
|
||||
|
||||
public function authorizeView($payment_method)
|
||||
{
|
||||
$this->payment_method = $payment_method;
|
||||
|
||||
switch ($payment_method) {
|
||||
case GatewayType::CREDIT_CARD:
|
||||
return $this->authorizeCreditCard();
|
||||
break;
|
||||
case GatewayType::BANK_TRANSFER:
|
||||
return $this->authorizeBankTransfer();
|
||||
break;
|
||||
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function authorizeResponseView($payment_method, $data)
|
||||
{
|
||||
$this->payment_method = $payment_method;
|
||||
|
||||
switch ($payment_method) {
|
||||
case GatewayType::CREDIT_CARD:
|
||||
return $this->authorizeCreditCardResponse($data);
|
||||
break;
|
||||
case GatewayType::BANK_TRANSFER:
|
||||
return $this->authorizeBankTransferResponse($data);
|
||||
break;
|
||||
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function authorizeCreditCard()
|
||||
{
|
||||
$data['gateway'] = $this->authorize->company_gateway;
|
||||
$data['public_client_id'] = $this->authorize->init()->getPublicClientKey();
|
||||
$data['api_login_id'] = $this->authorize->company_gateway->getConfigField('apiLoginId');
|
||||
|
||||
return render('gateways.authorize.add_credit_card', $data);
|
||||
}
|
||||
|
||||
public function authorizeBankTransfer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function authorizeCreditCardResponse($data)
|
||||
{
|
||||
$client_profile_id = null;
|
||||
|
||||
if($client_gateway_token = $this->authorize->findClientGatewayRecord()){
|
||||
$payment_profile = $this->addPaymentMethodToClient($client_gateway_token->gateway_customer_reference, $data);
|
||||
}
|
||||
else{
|
||||
$gateway_customer_reference = (new AuthorizeCreateCustomer($this->authorize, $this->authorize->client))->create($data);
|
||||
$payment_profile = $this->addPaymentMethodToClient($gateway_customer_reference, $data);
|
||||
}
|
||||
|
||||
$this->createClientGatewayToken($payment_profile, $gateway_customer_reference);
|
||||
|
||||
return redirect()->route('client.payment_methods.index');
|
||||
|
||||
}
|
||||
|
||||
public function authorizeBankTransferResponse($data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function createClientGatewayToken($payment_profile, $gateway_customer_reference)
|
||||
{
|
||||
info(print_r($payment_profile,1));
|
||||
|
||||
$client_gateway_token = new ClientGatewayToken();
|
||||
$client_gateway_token->company_id = $this->authorize->client->company_id;
|
||||
$client_gateway_token->client_id = $this->authorize->client->id;
|
||||
$client_gateway_token->token = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId();
|
||||
$client_gateway_token->company_gateway_id = $this->authorize->company_gateway->id;
|
||||
$client_gateway_token->gateway_type_id = $this->payment_method;
|
||||
$client_gateway_token->gateway_customer_reference = $gateway_customer_reference;
|
||||
$client_gateway_token->meta = $this->buildPaymentMethod($payment_profile);
|
||||
$client_gateway_token->save();
|
||||
|
||||
return $client_gateway_token;
|
||||
}
|
||||
|
||||
public function buildPaymentMethod($payment_profile)
|
||||
{
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->exp_month = 'xx';
|
||||
$payment_meta->exp_year = 'xx';
|
||||
$payment_meta->brand = $payment_profile->getPaymentProfile()->getPayment()->getCreditCard()->getCardType();
|
||||
$payment_meta->last4 = $payment_profile->getPaymentProfile()->getPayment()->getCreditCard()->getCardNumber();
|
||||
$payment_meta->type = $this->payment_method;
|
||||
|
||||
return $payment_meta;
|
||||
}
|
||||
|
||||
public function addPaymentMethodToClient($gateway_customer_reference, $data)
|
||||
{
|
||||
|
||||
error_reporting (E_ALL & ~E_DEPRECATED);
|
||||
|
||||
$this->authorize->init();
|
||||
|
||||
// Set the transaction's refId
|
||||
$refId = 'ref' . time();
|
||||
|
||||
// Set the payment data for the payment profile to a token obtained from Accept.js
|
||||
$op = new OpaqueDataType();
|
||||
$op->setDataDescriptor($data['dataDescriptor']);
|
||||
$op->setDataValue($data['dataValue']);
|
||||
$paymentOne = new PaymentType();
|
||||
$paymentOne->setOpaqueData($op);
|
||||
|
||||
$contact = $this->authorize->client->primary_contact()->first();
|
||||
|
||||
if($contact){
|
||||
// Create the Bill To info for new payment type
|
||||
$billto = new CustomerAddressType();
|
||||
$billto->setFirstName($contact->present()->first_name());
|
||||
$billto->setLastName($contact->present()->last_name());
|
||||
$billto->setCompany($this->authorize->client->present()->name());
|
||||
$billto->setAddress($this->authorize->client->address1);
|
||||
$billto->setCity($this->authorize->client->city);
|
||||
$billto->setState($this->authorize->client->state);
|
||||
$billto->setZip($this->authorize->client->postal_code);
|
||||
|
||||
if($this->authorize->client->country_id)
|
||||
$billto->setCountry($this->authorize->client->country->name);
|
||||
|
||||
$billto->setPhoneNumber($this->authorize->client->phone);
|
||||
}
|
||||
|
||||
// Create a new Customer Payment Profile object
|
||||
$paymentprofile = new CustomerPaymentProfileType();
|
||||
$paymentprofile->setCustomerType('individual');
|
||||
|
||||
if($billto)
|
||||
$paymentprofile->setBillTo($billto);
|
||||
|
||||
$paymentprofile->setPayment($paymentOne);
|
||||
$paymentprofile->setDefaultPaymentProfile(true);
|
||||
$paymentprofiles[] = $paymentprofile;
|
||||
|
||||
// Assemble the complete transaction request
|
||||
$paymentprofilerequest = new CreateCustomerPaymentProfileRequest();
|
||||
$paymentprofilerequest->setMerchantAuthentication($this->authorize->merchant_authentication);
|
||||
|
||||
// Add an existing profile id to the request
|
||||
$paymentprofilerequest->setCustomerProfileId($gateway_customer_reference);
|
||||
$paymentprofilerequest->setPaymentProfile($paymentprofile);
|
||||
$paymentprofilerequest->setValidationMode("liveMode");
|
||||
|
||||
// Create the controller and get the response
|
||||
$controller = new CreateCustomerPaymentProfileController($paymentprofilerequest);
|
||||
$response = $controller->executeWithApiResponse($this->authorize->mode());
|
||||
|
||||
if (($response != null) && ($response->getMessages()->getResultCode() == "Ok") ) {
|
||||
return $this->getPaymentProfile($gateway_customer_reference, $response->getCustomerPaymentProfileId());
|
||||
} else {
|
||||
|
||||
$errorMessages = $response->getMessages()->getMessage();
|
||||
|
||||
$message = "Unable to add customer to Authorize.net gateway";
|
||||
|
||||
if(is_array($errorMessages))
|
||||
$message = $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText();
|
||||
|
||||
throw new GenericPaymentDriverFailure($message);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function getPaymentProfile($gateway_customer_reference, $payment_profile_id)
|
||||
{
|
||||
|
||||
error_reporting (E_ALL & ~E_DEPRECATED);
|
||||
|
||||
$this->authorize->init();
|
||||
|
||||
// Set the transaction's refId
|
||||
$refId = 'ref' . time();
|
||||
|
||||
//request requires customerProfileId and customerPaymentProfileId
|
||||
$request = new GetCustomerPaymentProfileRequest();
|
||||
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
|
||||
$request->setRefId($refId);
|
||||
$request->setCustomerProfileId($gateway_customer_reference);
|
||||
$request->setCustomerPaymentProfileId($payment_profile_id);
|
||||
|
||||
$controller = new GetCustomerPaymentProfileController($request);
|
||||
$response = $controller->executeWithApiResponse($this->authorize->mode());
|
||||
|
||||
if(($response != null) && ($response->getMessages()->getResultCode() == "Ok")) {
|
||||
return $response;
|
||||
}
|
||||
else if($response){
|
||||
|
||||
$errorMessages = $response->getMessages()->getMessage();
|
||||
|
||||
$message = "Unable to add payment method to Authorize.net gateway";
|
||||
|
||||
if(is_array($errorMessages))
|
||||
$message = $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText();
|
||||
|
||||
throw new GenericPaymentDriverFailure($message);
|
||||
}
|
||||
else
|
||||
throw new GenericPaymentDriverFailure("Error communicating with Authorize.net");
|
||||
}
|
||||
|
||||
}
|
66
app/PaymentDrivers/Authorize/AuthorizeTransactions.php
Normal file
66
app/PaymentDrivers/Authorize/AuthorizeTransactions.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Authorize;
|
||||
|
||||
use net\authorize\api\contract\v1\GetTransactionDetailsRequest;
|
||||
use net\authorize\api\controller\GetTransactionDetailsController;
|
||||
|
||||
|
||||
/**
|
||||
* Class AuthorizeTransactions
|
||||
* @package App\PaymentDrivers\Authorize
|
||||
*
|
||||
*/
|
||||
class AuthorizeTransactions
|
||||
{
|
||||
|
||||
public $authorize;
|
||||
|
||||
public function __construct(AuthorizePaymentDriver $authorize)
|
||||
{
|
||||
$this->authorize = $authorize;
|
||||
}
|
||||
|
||||
function getTransactionDetails($transactionId)
|
||||
{
|
||||
/* Create a merchantAuthenticationType object with authentication details
|
||||
retrieved from the constants file */
|
||||
$this->authorize->init();
|
||||
|
||||
// Set the transaction's refId
|
||||
$refId = 'ref' . time();
|
||||
|
||||
$request = new GetTransactionDetailsRequest();
|
||||
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
|
||||
$request->setTransId($transactionId);
|
||||
|
||||
$controller = new GetTransactionDetailsController($request);
|
||||
|
||||
$response = $controller->executeWithApiResponse($this->authorize->mode());
|
||||
|
||||
if (($response != null) && ($response->getMessages()->getResultCode() == "Ok"))
|
||||
{
|
||||
echo "SUCCESS: Transaction Status:" . $response->getTransaction()->getTransactionStatus() . "\n";
|
||||
echo " Auth Amount:" . $response->getTransaction()->getAuthAmount() . "\n";
|
||||
echo " Trans ID:" . $response->getTransaction()->getTransId() . "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "ERROR : Invalid response\n";
|
||||
$errorMessages = $response->getMessages()->getMessage();
|
||||
echo "Response : " . $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText() . "\n";
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
119
app/PaymentDrivers/Authorize/ChargePaymentProfile.php
Normal file
119
app/PaymentDrivers/Authorize/ChargePaymentProfile.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Authorize;
|
||||
|
||||
use App\PaymentDrivers\AuthorizePaymentDriver;
|
||||
use net\authorize\api\contract\v1\CreateTransactionRequest;
|
||||
use net\authorize\api\contract\v1\CustomerProfilePaymentType;
|
||||
use net\authorize\api\contract\v1\PaymentProfileType;
|
||||
use net\authorize\api\contract\v1\TransactionRequestType;
|
||||
use net\authorize\api\controller\CreateTransactionController;
|
||||
|
||||
/**
|
||||
* Class ChargePaymentProfile
|
||||
* @package App\PaymentDrivers\Authorize
|
||||
*
|
||||
*/
|
||||
class ChargePaymentProfile
|
||||
{
|
||||
|
||||
public function __construct(AuthorizePaymentDriver $authorize)
|
||||
{
|
||||
$this->authorize = $authorize;
|
||||
}
|
||||
|
||||
|
||||
function chargeCustomerProfile($profile_id, $payment_profile_id, $amount)
|
||||
{
|
||||
|
||||
$this->authorize->init();
|
||||
|
||||
// Set the transaction's refId
|
||||
$refId = 'ref' . time();
|
||||
|
||||
$profileToCharge = new CustomerProfilePaymentType();
|
||||
$profileToCharge->setCustomerProfileId($profile_id);
|
||||
$paymentProfile = new PaymentProfileType();
|
||||
$paymentProfile->setPaymentProfileId($payment_profile_id);
|
||||
$profileToCharge->setPaymentProfile($paymentProfile);
|
||||
|
||||
$transactionRequestType = new TransactionRequestType();
|
||||
$transactionRequestType->setTransactionType("authCaptureTransaction");
|
||||
$transactionRequestType->setAmount($amount);
|
||||
$transactionRequestType->setProfile($profileToCharge);
|
||||
$transactionRequestType->setCurrencyCode($this->authorize->client->currency()->code);
|
||||
|
||||
$request = new CreateTransactionRequest();
|
||||
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
|
||||
$request->setRefId( $refId);
|
||||
$request->setTransactionRequest( $transactionRequestType);
|
||||
$controller = new CreateTransactionController($request);
|
||||
$response = $controller->executeWithApiResponse($this->authorize->mode());
|
||||
|
||||
if($response != null && $response->getMessages()->getResultCode() == "Ok")
|
||||
{
|
||||
$tresponse = $response->getTransactionResponse();
|
||||
|
||||
if ($tresponse != null && $tresponse->getMessages() != null)
|
||||
{
|
||||
info(" Transaction Response code : " . $tresponse->getResponseCode() );
|
||||
info( "Charge Customer Profile APPROVED :" );
|
||||
info(" Charge Customer Profile AUTH CODE : " . $tresponse->getAuthCode() );
|
||||
info(" Charge Customer Profile TRANS ID : " . $tresponse->getTransId() );
|
||||
info(" Code : " . $tresponse->getMessages()[0]->getCode());
|
||||
info(" Description : " . $tresponse->getMessages()[0]->getDescription());
|
||||
//info(" Charge Customer Profile TRANS STATUS : " . $tresponse->getTransactionStatus() );
|
||||
//info(" Charge Customer Profile Amount : " . $tresponse->getAuthAmount());
|
||||
|
||||
info(" Code : " . $tresponse->getMessages()[0]->getCode() );
|
||||
info(" Description : " . $tresponse->getMessages()[0]->getDescription() );
|
||||
info(print_r($tresponse->getMessages()[0],1));
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Transaction Failed ");
|
||||
if($tresponse->getErrors() != null)
|
||||
{
|
||||
info(" Error code : " . $tresponse->getErrors()[0]->getErrorCode() );
|
||||
info(" Error message : " . $tresponse->getErrors()[0]->getErrorText() );
|
||||
info(print_r($tresponse->getErrors()[0],1));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Transaction Failed ");
|
||||
$tresponse = $response->getTransactionResponse();
|
||||
if($tresponse != null && $tresponse->getErrors() != null)
|
||||
{
|
||||
info(" Error code : " . $tresponse->getErrors()[0]->getErrorCode() );
|
||||
info(" Error message : " . $tresponse->getErrors()[0]->getErrorText() );
|
||||
info(print_r($tresponse->getErrors()[0],1));
|
||||
}
|
||||
else
|
||||
{
|
||||
info(" Error code : " . $response->getMessages()->getMessage()[0]->getCode() );
|
||||
info(" Error message : " . $response->getMessages()->getMessage()[0]->getText() );
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'response' => $response,
|
||||
'amount' => $amount,
|
||||
'profile_id' => $profile_id,
|
||||
'payment_profile_id' => $payment_profile_id
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
}
|
113
app/PaymentDrivers/Authorize/RefundTransaction.php
Normal file
113
app/PaymentDrivers/Authorize/RefundTransaction.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Authorize;
|
||||
|
||||
use App\PaymentDrivers\AuthorizePaymentDriver;
|
||||
use net\authorize\api\contract\v1\CreateTransactionRequest;
|
||||
use net\authorize\api\contract\v1\CustomerProfilePaymentType;
|
||||
use net\authorize\api\contract\v1\PaymentProfileType;
|
||||
use net\authorize\api\contract\v1\TransactionRequestType;
|
||||
use net\authorize\api\controller\CreateTransactionController;
|
||||
|
||||
/**
|
||||
* Class RefundTransaction
|
||||
* @package App\PaymentDrivers\Authorize
|
||||
*
|
||||
*/
|
||||
class RefundTransaction
|
||||
{
|
||||
|
||||
public function __construct(AuthorizePaymentDriver $authorize)
|
||||
{
|
||||
$this->authorize = $authorize;
|
||||
}
|
||||
|
||||
function refundTransaction($transaction_reference, $amount, $payment_profile_id, $profile_id)
|
||||
{
|
||||
|
||||
$this->authorize->init();
|
||||
|
||||
// Set the transaction's refId
|
||||
$refId = 'ref' . time();
|
||||
|
||||
$paymentProfile = new PaymentProfileType();
|
||||
$paymentProfile->setPaymentProfileId( $payment_profile_id );
|
||||
|
||||
// set customer profile
|
||||
$customerProfile = new CustomerProfilePaymentType();
|
||||
$customerProfile->setCustomerProfileId( $profile_id );
|
||||
$customerProfile->setPaymentProfile( $paymentProfile );
|
||||
|
||||
//create a transaction
|
||||
$transactionRequest = new TransactionRequestType();
|
||||
$transactionRequest->setTransactionType("refundTransaction");
|
||||
$transactionRequest->setAmount($amount);
|
||||
$transactionRequest->setProfile($customerProfile);
|
||||
$transactionRequest->setRefTransId($transaction_reference);
|
||||
|
||||
$request = new CreateTransactionRequest();
|
||||
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
|
||||
$request->setRefId($refId);
|
||||
$request->setTransactionRequest($transactionRequest);
|
||||
$controller = new CreateTransactionController($request);
|
||||
$response = $controller->executeWithApiResponse($this->authorize->mode());
|
||||
|
||||
if ($response != null)
|
||||
{
|
||||
if($response->getMessages()->getResultCode() == "Ok")
|
||||
{
|
||||
$tresponse = $response->getTransactionResponse();
|
||||
|
||||
if ($tresponse != null && $tresponse->getMessages() != null)
|
||||
{
|
||||
echo " Transaction Response code : " . $tresponse->getResponseCode() . "\n";
|
||||
echo "Refund SUCCESS: " . $tresponse->getTransId() . "\n";
|
||||
echo " Code : " . $tresponse->getMessages()[0]->getCode() . "\n";
|
||||
echo " Description : " . $tresponse->getMessages()[0]->getDescription() . "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Transaction Failed \n";
|
||||
if($tresponse->getErrors() != null)
|
||||
{
|
||||
echo " Error code : " . $tresponse->getErrors()[0]->getErrorCode() . "\n";
|
||||
echo " Error message : " . $tresponse->getErrors()[0]->getErrorText() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Transaction Failed \n";
|
||||
$tresponse = $response->getTransactionResponse();
|
||||
if($tresponse != null && $tresponse->getErrors() != null)
|
||||
{
|
||||
echo " Error code : " . $tresponse->getErrors()[0]->getErrorCode() . "\n";
|
||||
echo " Error message : " . $tresponse->getErrors()[0]->getErrorText() . "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo " Error code : " . $response->getMessages()->getMessage()[0]->getCode() . "\n";
|
||||
echo " Error message : " . $response->getMessages()->getMessage()[0]->getText() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No response returned \n";
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
}
|
141
app/PaymentDrivers/AuthorizePaymentDriver.php
Normal file
141
app/PaymentDrivers/AuthorizePaymentDriver.php
Normal file
@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\PaymentDrivers\Authorize\AuthorizeCreditCard;
|
||||
use App\PaymentDrivers\Authorize\AuthorizePaymentMethod;
|
||||
use net\authorize\api\constants\ANetEnvironment;
|
||||
use net\authorize\api\contract\v1\CreateTransactionRequest;
|
||||
use net\authorize\api\contract\v1\GetMerchantDetailsRequest;
|
||||
use net\authorize\api\contract\v1\MerchantAuthenticationType;
|
||||
use net\authorize\api\controller\CreateTransactionController;
|
||||
use net\authorize\api\controller\GetMerchantDetailsController;
|
||||
|
||||
/**
|
||||
* Class BaseDriver
|
||||
* @package App\PaymentDrivers
|
||||
*
|
||||
*/
|
||||
class AuthorizePaymentDriver extends BaseDriver
|
||||
{
|
||||
|
||||
public $merchant_authentication;
|
||||
|
||||
public static $methods = [
|
||||
GatewayType::CREDIT_CARD => AuthorizeCreditCard::class,
|
||||
];
|
||||
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
{
|
||||
|
||||
$class = self::$methods[$payment_method_id];
|
||||
|
||||
$this->payment_method = new $class($this);
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
/**
|
||||
* Returns the gateway types
|
||||
*/
|
||||
public function gatewayTypes() :array
|
||||
{
|
||||
$types = [
|
||||
GatewayType::CREDIT_CARD,
|
||||
];
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
error_reporting (E_ALL & ~E_DEPRECATED);
|
||||
|
||||
$this->merchant_authentication = new MerchantAuthenticationType();
|
||||
$this->merchant_authentication->setName($this->company_gateway->getConfigField('apiLoginId'));
|
||||
$this->merchant_authentication->setTransactionKey($this->company_gateway->getConfigField('transactionKey'));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPublicClientKey()
|
||||
{
|
||||
|
||||
$request = new GetMerchantDetailsRequest();
|
||||
$request->setMerchantAuthentication($this->merchant_authentication);
|
||||
|
||||
$controller = new GetMerchantDetailsController($request);
|
||||
$response = $controller->executeWithApiResponse($this->mode());
|
||||
|
||||
return $response->getPublicClientKey();
|
||||
|
||||
}
|
||||
|
||||
public function mode()
|
||||
{
|
||||
|
||||
if($this->company_gateway->getConfigField('testMode'))
|
||||
return ANetEnvironment::SANDBOX;
|
||||
|
||||
return $env = ANetEnvironment::PRODUCTION;
|
||||
|
||||
}
|
||||
|
||||
public function authorizeView($payment_method)
|
||||
{
|
||||
return (new AuthorizePaymentMethod($this))->authorizeView($payment_method);
|
||||
}
|
||||
|
||||
public function authorizeResponseView(array $data)
|
||||
{
|
||||
return (new AuthorizePaymentMethod($this))->authorizeResponseView($data['gateway_type_id'], $data);
|
||||
}
|
||||
|
||||
public function authorize($payment_method)
|
||||
{
|
||||
return $this->authorizeView($payment_method);
|
||||
}
|
||||
|
||||
public function processPaymentView($data)
|
||||
{
|
||||
|
||||
return $this->payment_method->processPaymentView($data);
|
||||
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
|
||||
return $this->payment_method->processPaymentResponse($request);
|
||||
|
||||
}
|
||||
|
||||
public function purchase($amount, $return_client_response = false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function refund($amount, $transaction_reference, $return_client_response = false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function findClientGatewayRecord() :?ClientGatewayToken
|
||||
{
|
||||
return ClientGatewayToken::where('client_id', $this->client->id)
|
||||
->where('company_gateway_id', $this->company_gateway->id)
|
||||
->first();
|
||||
}
|
||||
}
|
120
app/PaymentDrivers/BaseDriver.php
Normal file
120
app/PaymentDrivers/BaseDriver.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\PaymentDrivers\AbstractPaymentDriver;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SystemLogTrait;
|
||||
|
||||
/**
|
||||
* Class BaseDriver
|
||||
* @package App\PaymentDrivers
|
||||
*
|
||||
*/
|
||||
class BaseDriver extends AbstractPaymentDriver
|
||||
{
|
||||
use SystemLogTrait;
|
||||
use MakesHash;
|
||||
|
||||
/* The company gateway instance*/
|
||||
public $company_gateway;
|
||||
|
||||
/* The Invitation */
|
||||
public $invitation;
|
||||
|
||||
/* Gateway capabilities */
|
||||
public $refundable = false;
|
||||
|
||||
/* Token billing */
|
||||
public $token_billing = false;
|
||||
|
||||
/* Authorise payment methods */
|
||||
public $can_authorise_credit_card = false;
|
||||
|
||||
/* The client */
|
||||
public $client;
|
||||
|
||||
public $payment_method;
|
||||
|
||||
public static $methods = [];
|
||||
|
||||
public function __construct(CompanyGateway $company_gateway, Client $client = null, $invitation = false)
|
||||
{
|
||||
$this->company_gateway = $company_gateway;
|
||||
|
||||
$this->invitation = $invitation;
|
||||
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize a payment method.
|
||||
*
|
||||
* Returns a reusable token for storage for future payments
|
||||
* @param const $payment_method the GatewayType::constant
|
||||
* @return view Return a view for collecting payment method information
|
||||
*/
|
||||
public function authorize($payment_method) {}
|
||||
|
||||
/**
|
||||
* Executes purchase attempt for a given amount
|
||||
*
|
||||
* @param float $amount The amount to be collected
|
||||
* @param boolean $return_client_response Whether the method needs to return a response (otherwise we assume an unattended payment)
|
||||
* @return mixed
|
||||
*/
|
||||
public function purchase($amount, $return_client_response = false) {}
|
||||
|
||||
/**
|
||||
* Executes a refund attempt for a given amount with a transaction_reference
|
||||
*
|
||||
* @param float $amount The amount to be refunded
|
||||
* @param string $transaction_reference The transaction reference
|
||||
* @param boolean $return_client_response Whether the method needs to return a response (otherwise we assume an unattended payment)
|
||||
* @return mixed
|
||||
*/
|
||||
public function refund($amount, $transaction_reference, $return_client_response = false) {}
|
||||
|
||||
/**
|
||||
* Set the inbound request payment method type for access.
|
||||
*
|
||||
* @param int $payment_method_id The Payment Method ID
|
||||
*/
|
||||
public function setPaymentMethod($payment_method_id){}
|
||||
|
||||
/**
|
||||
* Helper method to attach invoices to a payment
|
||||
*
|
||||
* @param Payment $payment The payment
|
||||
* @param array $hashed_ids The array of invoice hashed_ids
|
||||
* @return Payment The payment object
|
||||
*/
|
||||
public function attachInvoices(Payment $payment, $hashed_ids): Payment
|
||||
{
|
||||
$transformed = $this->transformKeys($hashed_ids);
|
||||
$array = is_array($transformed) ? $transformed : [$transformed];
|
||||
|
||||
$invoices = Invoice::whereIn('id', $array)
|
||||
->whereClientId($this->client->id)
|
||||
->get();
|
||||
|
||||
$payment->invoices()->sync($invoices);
|
||||
$payment->save();
|
||||
|
||||
return $payment;
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ class BasePaymentDriver
|
||||
use MakesHash;
|
||||
|
||||
/* The company gateway instance*/
|
||||
protected $company_gateway;
|
||||
public $company_gateway;
|
||||
|
||||
/* The Omnipay payment driver instance*/
|
||||
protected $gateway;
|
||||
@ -259,12 +259,12 @@ class BasePaymentDriver
|
||||
->send();
|
||||
}
|
||||
|
||||
public function createPayment($data): Payment
|
||||
public function createPayment($data, $status = Payment::STATUS_COMPLETED): Payment
|
||||
{
|
||||
$payment = PaymentFactory::create($this->client->company->id, $this->client->user->id);
|
||||
$payment->client_id = $this->client->id;
|
||||
$payment->company_gateway_id = $this->company_gateway->id;
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->status_id = $status;
|
||||
$payment->currency_id = $this->client->getSetting('currency_id');
|
||||
$payment->date = Carbon::now();
|
||||
|
||||
|
42
app/PaymentDrivers/CheckoutCom/Utilities.php
Normal file
42
app/PaymentDrivers/CheckoutCom/Utilities.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CheckoutCom;
|
||||
|
||||
trait Utilities
|
||||
{
|
||||
public function getPublishableKey()
|
||||
{
|
||||
return $this->company_gateway->getConfigField('publicApiKey');
|
||||
}
|
||||
|
||||
public function convertToCheckoutAmount($amount, $currency)
|
||||
{
|
||||
$cases = [
|
||||
'option_1' => ['BIF', 'DJF', 'GNF', 'ISK', 'KMF', 'XAF', 'CLF', 'XPF', 'JPY', 'PYG', 'RWF', 'KRW', 'VUV', 'VND', 'XOF'],
|
||||
'option_2' => ['BHD', 'IQD', 'JOD', 'KWD', 'LYD', 'OMR', 'TND'],
|
||||
];
|
||||
|
||||
// https://docs.checkout.com/resources/calculating-the-value#Calculatingthevalue-Option1:Thefullvaluefullvalue
|
||||
if (in_array($currency, $cases['option_1'])) {
|
||||
return round($amount);
|
||||
}
|
||||
|
||||
// https://docs.checkout.com/resources/calculating-the-value#Calculatingthevalue-Option2:Thevaluedividedby1000valuediv1000
|
||||
if (in_array($currency, $cases['option_2'])) {
|
||||
return round($amount * 1000);
|
||||
}
|
||||
|
||||
// https://docs.checkout.com/resources/calculating-the-value#Calculatingthevalue-Option3:Thevaluedividedby100valuediv100
|
||||
return round($amount * 100);
|
||||
}
|
||||
}
|
276
app/PaymentDrivers/CheckoutComPaymentDriver.php
Normal file
276
app/PaymentDrivers/CheckoutComPaymentDriver.php
Normal file
@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\CheckoutCom\Utilities;
|
||||
use App\Utils\Traits\SystemLogTrait;
|
||||
use Checkout\CheckoutApi;
|
||||
use Checkout\Library\Exceptions\CheckoutHttpException;
|
||||
use Checkout\Models\Payments\IdSource;
|
||||
use Checkout\Models\Payments\Payment as CheckoutPayment;
|
||||
use Checkout\Models\Payments\TokenSource;
|
||||
|
||||
class CheckoutComPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
use SystemLogTrait, Utilities;
|
||||
|
||||
/* The company gateway instance*/
|
||||
public $company_gateway;
|
||||
|
||||
/* The Invitation */
|
||||
protected $invitation;
|
||||
|
||||
/* Gateway capabilities */
|
||||
protected $refundable = true;
|
||||
|
||||
/* Token billing */
|
||||
protected $token_billing = true;
|
||||
|
||||
/* Authorise payment methods */
|
||||
protected $can_authorise_credit_card = true;
|
||||
|
||||
/** Instance of \Checkout\CheckoutApi */
|
||||
public $gateway;
|
||||
|
||||
/** Since with Checkout.com we handle only credit cards, this method should be empty. */
|
||||
public function setPaymentMethod($string = null)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
$config = [
|
||||
'secret' => $this->company_gateway->getConfigField('secretApiKey'),
|
||||
'public' => $this->company_gateway->getConfigField('publicApiKey'),
|
||||
'sandbox' => $this->company_gateway->getConfigField('testMode'),
|
||||
];
|
||||
|
||||
$this->gateway = new CheckoutApi($config['secret'], $config['sandbox'], $config['public']);
|
||||
}
|
||||
|
||||
public function viewForType($gateway_type_id)
|
||||
{
|
||||
if ($gateway_type_id == GatewayType::CREDIT_CARD) {
|
||||
return 'gateways.checkout.credit_card';
|
||||
}
|
||||
|
||||
if ($gateway_type_id == GatewayType::TOKEN) {
|
||||
return 'gateways.checkout.credit_card';
|
||||
}
|
||||
}
|
||||
|
||||
public function processPaymentView(array $data)
|
||||
{
|
||||
$data['gateway'] = $this;
|
||||
$data['client'] = $this->client;
|
||||
$data['currency'] = $this->client->getCurrencyCode();
|
||||
$data['value'] = $this->convertToCheckoutAmount($data['amount_with_fee'], $this->client->getCurrencyCode());
|
||||
$data['raw_value'] = $data['amount_with_fee'];
|
||||
$data['customer_email'] = $this->client->present()->email;
|
||||
|
||||
return render($this->viewForType($data['payment_method_id']), $data);
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$state = [
|
||||
'server_response' => json_decode($request->gateway_response),
|
||||
'value' => $request->value,
|
||||
'raw_value' => $request->raw_value,
|
||||
'currency' => $request->currency,
|
||||
];
|
||||
|
||||
$state = array_merge($state, $request->all());
|
||||
$state['store_card'] = boolval($state['store_card']);
|
||||
|
||||
if ($request->has('token') && !is_null($request->token)) {
|
||||
$method = new IdSource($state['token']);
|
||||
$payment = new CheckoutPayment($method, $state['currency']);
|
||||
$payment->capture = false;
|
||||
$payment->amount = $state['value'];
|
||||
} else {
|
||||
$method = new TokenSource($state['server_response']->cardToken);
|
||||
$payment = new CheckoutPayment($method, $state['currency']);
|
||||
$payment->amount = $state['value'];
|
||||
|
||||
if ($this->client->currency()->code === 'EUR') {
|
||||
$payment->{"3ds"} = ['enabled' => true];
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->gateway->payments()->request($payment);
|
||||
$state['payment_response'] = $response;
|
||||
|
||||
if ($response->status === 'Authorized') {
|
||||
return $this->processSuccessfulPayment($state);
|
||||
}
|
||||
|
||||
if ($response->status === 'Pending') {
|
||||
return $this->processPendingPayment($state);
|
||||
}
|
||||
|
||||
if ($response->status === 'Declined') {
|
||||
return $this->processUnsuccessfulPayment($state);
|
||||
}
|
||||
} catch (CheckoutHttpException $e) {
|
||||
return $this->processInternallyFailedPayment($e, $state);
|
||||
}
|
||||
}
|
||||
|
||||
public function processSuccessfulPayment($state)
|
||||
{
|
||||
$state['charge_id'] = $state['payment_response']->id;
|
||||
|
||||
if (isset($state['store_card']) && $state['store_card']) {
|
||||
$this->saveCard($state);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'payment_method' => $state['charge_id'],
|
||||
'payment_type' => PaymentType::parseCardType($state['payment_response']->source['scheme']),
|
||||
'amount' => $state['raw_value'],
|
||||
];
|
||||
|
||||
$payment = $this->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
|
||||
$this->attachInvoices($payment, $state['hashed_ids']);
|
||||
|
||||
$payment->service()->updateInvoicePayment();
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company));
|
||||
|
||||
$logger_message = [
|
||||
'server_response' => $state['payment_response'],
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_CHECKOUT, $this->client);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
public function processPendingPayment($state)
|
||||
{
|
||||
$state['charge_id'] = $state['payment_response']->id;
|
||||
|
||||
if (isset($state['store_card']) && $state['store_card']) {
|
||||
$this->saveCard($state);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'payment_method' => $state['charge_id'],
|
||||
'payment_type' => PaymentType::parseCardType($state['payment_response']->source['scheme']),
|
||||
'amount' => $state['raw_value'],
|
||||
];
|
||||
|
||||
$payment = $this->createPayment($data, Payment::STATUS_PENDING);
|
||||
|
||||
$this->attachInvoices($payment, $state['hashed_ids']);
|
||||
|
||||
$payment->service()->updateInvoicePayment();
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company));
|
||||
|
||||
$logger_message = [
|
||||
'server_response' => $state['payment_response'],
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_CHECKOUT, $this->client);
|
||||
|
||||
try {
|
||||
return redirect($state['payment_response']->_links['redirect']['href']);
|
||||
} catch (\Exception $e) {
|
||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_CHECKOUT, $this->client);
|
||||
|
||||
throw new \Exception('Failed to process the payment.', 1);
|
||||
}
|
||||
}
|
||||
|
||||
public function processUnsuccessfulPayment($state)
|
||||
{
|
||||
PaymentFailureMailer::dispatch($this->client, $state['payment_response']->response_summary, $this->client->company, $state['payment_response']->amount);
|
||||
|
||||
$message = [
|
||||
'server_response' => $state['server_response'],
|
||||
'data' => $state,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_CHECKOUT, $this->client);
|
||||
|
||||
throw new \Exception('Failed to process the payment: ' . $state['payment_response']->response_summary, 1);
|
||||
}
|
||||
|
||||
public function processInternallyFailedPayment($e, $state)
|
||||
{
|
||||
$message = json_decode($e->getBody());
|
||||
|
||||
PaymentFailureMailer::dispatch($this->client, $message->error_type, $this->client->company, $state['value']);
|
||||
|
||||
$message = [
|
||||
'server_response' => $state['server_response'],
|
||||
'data' => $message,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_CHECKOUT, $this->client);
|
||||
|
||||
throw new \Exception('Failed to process the payment.', 1);
|
||||
}
|
||||
|
||||
public function createPayment($data, $status = Payment::STATUS_COMPLETED): Payment
|
||||
{
|
||||
$payment = parent::createPayment($data, $status);
|
||||
|
||||
$client_contact = $this->getContact();
|
||||
$client_contact_id = $client_contact ? $client_contact->id : null;
|
||||
|
||||
$payment->amount = $data['amount'];
|
||||
$payment->type_id = $data['payment_type'];
|
||||
$payment->transaction_reference = $data['payment_method'];
|
||||
$payment->client_contact_id = $client_contact_id;
|
||||
$payment->save();
|
||||
|
||||
return $payment;
|
||||
}
|
||||
|
||||
public function saveCard($state)
|
||||
{
|
||||
$company_gateway_token = new ClientGatewayToken();
|
||||
$company_gateway_token->company_id = $this->client->company->id;
|
||||
$company_gateway_token->client_id = $this->client->id;
|
||||
$company_gateway_token->token = $state['payment_response']->source['id'];
|
||||
$company_gateway_token->company_gateway_id = $this->company_gateway->id;
|
||||
$company_gateway_token->gateway_type_id = $state['payment_method_id'];
|
||||
$company_gateway_token->meta = $state['payment_response']->source;
|
||||
$company_gateway_token->save();
|
||||
|
||||
if ($this->client->gateway_tokens->count() == 1) {
|
||||
$this->client->gateway_tokens()->update(['is_default' => 0]);
|
||||
|
||||
$company_gateway_token->is_default = 1;
|
||||
$company_gateway_token->save();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Traits\SystemLogTrait;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Omnipay\Omnipay;
|
||||
|
||||
/**
|
||||
* Class BasePaymentDriver
|
||||
* @package App\PaymentDrivers
|
||||
*
|
||||
* Minimum dataset required for payment gateways
|
||||
*
|
||||
* $data = [
|
||||
'amount' => $invoice->getRequestedAmount(),
|
||||
'currency' => $invoice->getCurrencyCode(),
|
||||
'returnUrl' => $completeUrl,
|
||||
'cancelUrl' => $this->invitation->getLink(),
|
||||
'description' => trans('texts.' . $invoice->getEntityType()) . " {$invoice->number}",
|
||||
'transactionId' => $invoice->number,
|
||||
'transactionType' => 'Purchase',
|
||||
'clientIp' => Request::getClientIp(),
|
||||
];
|
||||
|
||||
*/
|
||||
class CheckoutPaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
use SystemLogTrait;
|
||||
|
||||
/* The company gateway instance*/
|
||||
protected $company_gateway;
|
||||
|
||||
/* The Omnipay payment driver instance*/
|
||||
protected $gateway;
|
||||
|
||||
/* The Invitation */
|
||||
protected $invitation;
|
||||
|
||||
/* Gateway capabilities */
|
||||
protected $refundable = true;
|
||||
|
||||
/* Token billing */
|
||||
protected $token_billing = true;
|
||||
|
||||
/* Authorise payment methods */
|
||||
protected $can_authorise_credit_card = true;
|
||||
|
||||
public function createTransactionToken($amount)
|
||||
{
|
||||
// if ($this->invoice()->getCurrencyCode() == 'BHD') {
|
||||
// $amount = $this->invoice()->getRequestedAmount() / 10;
|
||||
// } elseif ($this->invoice()->getCurrencyCode() == 'KWD') {
|
||||
// $amount = $this->invoice()->getRequestedAmount() * 10;
|
||||
// } elseif ($this->invoice()->getCurrencyCode() == 'OMR') {
|
||||
// $amount = $this->invoice()->getRequestedAmount();
|
||||
// } else
|
||||
// $amount = $this->invoice()->getRequestedAmount();
|
||||
|
||||
$response = $this->gateway()->purchase([
|
||||
'amount' => $amount,
|
||||
'currency' => $this->client->getCurrencyCode(),
|
||||
])->send();
|
||||
|
||||
if ($response->isRedirect()) {
|
||||
$token = $response->getTransactionReference();
|
||||
|
||||
session()->flash('transaction_reference', $token);
|
||||
|
||||
|
||||
// On each request, session()->flash() || sesion('', value) || session[name] ||session->flash(key, value)
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function viewForType($gateway_type_id)
|
||||
{
|
||||
switch ($gateway_type_id) {
|
||||
case GatewayType::CREDIT_CARD:
|
||||
return 'gateways.checkout.credit_card';
|
||||
break;
|
||||
case GatewayType::TOKEN:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* $data = [
|
||||
'invoices' => $invoices,
|
||||
'amount' => $amount,
|
||||
'fee' => $gateway->calcGatewayFee($amount),
|
||||
'amount_with_fee' => $amount + $gateway->calcGatewayFee($amount),
|
||||
'token' => auth()->user()->client->gateway_token($gateway->id, $payment_method_id),
|
||||
'payment_method_id' => $payment_method_id,
|
||||
'hashed_ids' => explode(",", request()->input('hashed_ids')),
|
||||
];
|
||||
*/
|
||||
public function processPaymentView(array $data)
|
||||
{
|
||||
$data['token'] = $this->createTransactionToken($data['amount']);
|
||||
$data['gateway'] = $this->gateway();
|
||||
|
||||
return render($this->viewForType($data['payment_method_id']), $data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
$data['token'] = session('transaction_reference');
|
||||
|
||||
$this->completeOffsitePurchase($data);
|
||||
|
||||
}
|
||||
}
|
@ -258,9 +258,9 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function createPayment($data): Payment
|
||||
public function createPayment($data, $status = Payment::STATUS_COMPLETED): Payment
|
||||
{
|
||||
$payment = parent::createPayment($data);
|
||||
$payment = parent::createPayment($data, $status);
|
||||
|
||||
$client_contact = $this->getContact();
|
||||
$client_contact_id = $client_contact ? $client_contact->id : null;
|
||||
|
221
app/PaymentDrivers/Stripe/ACH.php
Normal file
221
app/PaymentDrivers/Stripe/ACH.php
Normal file
@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Stripe;
|
||||
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\StripePaymentDriver;
|
||||
use Stripe\Exception\InvalidRequestException;
|
||||
|
||||
class ACH
|
||||
{
|
||||
/** @var StripePaymentDriver */
|
||||
public $stripe;
|
||||
|
||||
public function __construct(StripePaymentDriver $stripe)
|
||||
{
|
||||
$this->stripe = $stripe;
|
||||
}
|
||||
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
return render('gateways.stripe.ach.authorize', array_merge($data));
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
$state = [
|
||||
'server_response' => json_decode($request->gateway_response),
|
||||
'gateway_id' => $request->company_gateway_id,
|
||||
'gateway_type_id' => $request->gateway_type_id,
|
||||
'is_default' => $request->is_default,
|
||||
];
|
||||
|
||||
$customer = $this->stripe->findOrCreateCustomer();
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
$local_stripe = new \Stripe\StripeClient(
|
||||
$this->stripe->company_gateway->getConfigField('apiKey')
|
||||
);
|
||||
|
||||
try {
|
||||
$local_stripe->customers->createSource(
|
||||
$customer->id,
|
||||
['source' => $state['server_response']->token->id]
|
||||
);
|
||||
} catch (InvalidRequestException $e) {
|
||||
return back()->with('ach_error', $e->getMessage());
|
||||
}
|
||||
|
||||
$payment_meta = $state['server_response']->token->bank_account;
|
||||
$payment_meta->brand = ctrans('texts.ach');
|
||||
$payment_meta->type = ctrans('texts.bank_transfer');
|
||||
$payment_meta->verified_at = null;
|
||||
$payment_meta->token = $state['server_response']->token->id;
|
||||
|
||||
$client_gateway_token = new ClientGatewayToken();
|
||||
$client_gateway_token->company_id = $this->stripe->client->company->id;
|
||||
$client_gateway_token->client_id = $this->stripe->client->id;
|
||||
$client_gateway_token->token = $state['server_response']->token->bank_account->id;
|
||||
$client_gateway_token->company_gateway_id = $this->stripe->company_gateway->id;
|
||||
$client_gateway_token->gateway_type_id = $state['gateway_type_id'];
|
||||
$client_gateway_token->gateway_customer_reference = $customer->id;
|
||||
$client_gateway_token->meta = $payment_meta;
|
||||
$client_gateway_token->save();
|
||||
|
||||
if ($state['is_default'] == 'true' || $this->stripe->client->gateway_tokens->count() == 1) {
|
||||
$this->stripe->client->gateway_tokens()->update(['is_default' => 0]);
|
||||
|
||||
$client_gateway_token->is_default = 1;
|
||||
$client_gateway_token->save();
|
||||
}
|
||||
|
||||
return redirect()->route('client.payment_methods.verification', $client_gateway_token->hashed_id);
|
||||
}
|
||||
|
||||
public function verificationView(ClientGatewayToken $token)
|
||||
{
|
||||
return render('gateways.stripe.ach.verify', compact('token'));
|
||||
}
|
||||
|
||||
public function processVerification(ClientGatewayToken $token)
|
||||
{
|
||||
$this->stripe->init();
|
||||
|
||||
$bank_account = \Stripe\Customer::retrieveSource(
|
||||
request()->customer,
|
||||
request()->source,
|
||||
);
|
||||
|
||||
try {
|
||||
$status = $bank_account->verify(['amounts' => request()->transactions]);
|
||||
|
||||
$token->meta->verified_at = now();
|
||||
$token->save();
|
||||
|
||||
return redirect()
|
||||
->route('client.invoices.index')
|
||||
->with('success', __('texts.payment_method_verified'));
|
||||
} catch (\Stripe\Exception\CardException $e) {
|
||||
return back()->with('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
$state = [
|
||||
'amount' => $data['amount_with_fee'],
|
||||
'currency' => $this->stripe->client->getCurrencyCode(),
|
||||
'invoices' => $data['invoices'],
|
||||
'gateway' => $this->stripe,
|
||||
'payment_method_id' => GatewayType::BANK_TRANSFER,
|
||||
'token' => $data['token'],
|
||||
'customer' => $this->stripe->findOrCreateCustomer(),
|
||||
];
|
||||
|
||||
return render('gateways.stripe.ach.pay', $state);
|
||||
}
|
||||
|
||||
public function paymentResponse($request)
|
||||
{
|
||||
$state = [
|
||||
'payment_method' => $request->payment_method_id,
|
||||
'gateway_type_id' => $request->company_gateway_id,
|
||||
'hashed_ids' => $request->hashed_ids,
|
||||
'amount' => $this->stripe->convertToStripeAmount($request->amount, $this->stripe->client->currency()->precision),
|
||||
'currency' => $request->currency,
|
||||
'source' => $request->source,
|
||||
'customer' => $request->customer,
|
||||
];
|
||||
|
||||
if ($this->stripe->getContact()) {
|
||||
$state['client_contact'] = $this->stripe->getContact();
|
||||
} else {
|
||||
$state['client_contact'] = $state['invoices']->first()->invitations->first()->contact;
|
||||
}
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
try {
|
||||
$state['charge'] = \Stripe\Charge::create([
|
||||
'amount' => $state['amount'],
|
||||
'currency' => $state['currency'],
|
||||
'customer' => $state['customer'],
|
||||
'source' => $state['source'],
|
||||
]);
|
||||
|
||||
if ($state['charge']->status === 'pending' && is_null($state['charge']->failure_message)) {
|
||||
return $this->processPendingPayment($state);
|
||||
}
|
||||
|
||||
return $this->processUnsuccessfulPayment($state);
|
||||
} catch (\Exception $e) {
|
||||
if ($e instanceof \Stripe\Exception\CardException) {
|
||||
return redirect()->route('client.payment_methods.verification', ClientGatewayToken::first()->hashed_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function processPendingPayment($state)
|
||||
{
|
||||
$state['charge_id'] = $state['charge']->id;
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
$state['payment_type'] = PaymentType::ACH;
|
||||
|
||||
$data = [
|
||||
'payment_method' => $state['charge_id'],
|
||||
'payment_type' => $state['payment_type'],
|
||||
'amount' => $state['charge']->amount,
|
||||
];
|
||||
|
||||
$payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING);
|
||||
|
||||
$this->stripe->attachInvoices($payment, $state['hashed_ids']);
|
||||
|
||||
$payment->service()->updateInvoicePayment();
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company));
|
||||
|
||||
$logger_message = [
|
||||
'server_response' => $state['charge'],
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
public function processUnsuccessfulPayment($state)
|
||||
{
|
||||
PaymentFailureMailer::dispatch($this->stripe->client, $state['charge']->failure_message, $this->stripe->client->company, $state['amount']);
|
||||
|
||||
$message = [
|
||||
'server_response' => $state['charge'],
|
||||
'data' => $state,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client);
|
||||
|
||||
throw new \Exception('Failed to process the payment.', 1);
|
||||
}
|
||||
}
|
113
app/PaymentDrivers/Stripe/Alipay.php
Normal file
113
app/PaymentDrivers/Stripe/Alipay.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Stripe;
|
||||
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\StripePaymentDriver;
|
||||
|
||||
class Alipay
|
||||
{
|
||||
/** @var StripePaymentDriver */
|
||||
public $stripe;
|
||||
|
||||
public function __construct(StripePaymentDriver $stripe)
|
||||
{
|
||||
$this->stripe = $stripe;
|
||||
}
|
||||
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
$data['gateway'] = $this->stripe;
|
||||
$data['return_url'] = $this->buildReturnUrl($data);
|
||||
$data['currency'] = $this->stripe->client->getCurrencyCode();
|
||||
$data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['amount_with_fee'], $this->stripe->client->currency()->precision);
|
||||
|
||||
return render('gateways.stripe.alipay.pay', $data);
|
||||
}
|
||||
|
||||
private function buildReturnUrl($data): string
|
||||
{
|
||||
return route('client.payments.response', [
|
||||
'company_gateway_id' => $this->stripe->company_gateway->id,
|
||||
'gateway_type_id' => GatewayType::SOFORT,
|
||||
'hashed_ids' => implode(",", $data['hashed_ids']),
|
||||
'amount' => $data['amount'],
|
||||
'fee' => $data['fee'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function paymentResponse($request)
|
||||
{
|
||||
$state = array_merge($request->all(), []);
|
||||
$amount = $state['amount'] + $state['fee'];
|
||||
$state['amount'] = $this->stripe->convertToStripeAmount($amount, $this->stripe->client->currency()->precision);
|
||||
|
||||
if ($request->redirect_status == 'succeeded') {
|
||||
return $this->processSuccesfulRedirect($state);
|
||||
}
|
||||
|
||||
return $this->processUnsuccesfulRedirect($state);
|
||||
}
|
||||
|
||||
public function processSuccesfulRedirect($state)
|
||||
{
|
||||
$state['charge_id'] = $state['source'];
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
$state['payment_type'] = PaymentType::ALIPAY;
|
||||
|
||||
$data = [
|
||||
'payment_method' => $state['charge_id'],
|
||||
'payment_type' => $state['payment_type'],
|
||||
'amount' => $state['amount'],
|
||||
];
|
||||
|
||||
$payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING);
|
||||
|
||||
if (isset($state['hashed_ids'])) {
|
||||
$this->stripe->attachInvoices($payment, $state['hashed_ids']);
|
||||
}
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company));
|
||||
|
||||
$logger_message = [
|
||||
'server_response' => $state,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
public function processUnsuccesfulRedirect($state)
|
||||
{
|
||||
PaymentFailureMailer::dispatch($this->stripe->client, $state['charge']->failure_message, $this->stripe->client->company, $state['amount']);
|
||||
|
||||
$message = [
|
||||
'server_response' => $state['charge'],
|
||||
'data' => $state,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client);
|
||||
|
||||
throw new \Exception('Failed to process the payment.', 1);
|
||||
}
|
||||
}
|
233
app/PaymentDrivers/Stripe/CreditCard.php
Normal file
233
app/PaymentDrivers/Stripe/CreditCard.php
Normal file
@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Stripe;
|
||||
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\StripePaymentDriver;
|
||||
use Stripe\PaymentMethod;
|
||||
|
||||
class CreditCard
|
||||
{
|
||||
public $stripe;
|
||||
|
||||
public function __construct(StripePaymentDriver $stripe)
|
||||
{
|
||||
$this->stripe = $stripe;
|
||||
}
|
||||
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
$intent['intent'] = $this->stripe->getSetupIntent();
|
||||
|
||||
return render('gateways.stripe.add_credit_card', array_merge($data, $intent));
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
$server_response = json_decode($request->input('gateway_response'));
|
||||
|
||||
$gateway_id = $request->input('gateway_id');
|
||||
$gateway_type_id = $request->input('gateway_type_id');
|
||||
$is_default = $request->input('is_default');
|
||||
|
||||
$payment_method = $server_response->payment_method;
|
||||
|
||||
$customer = $this->stripe->findOrCreateCustomer();
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
$stripe_payment_method = \Stripe\PaymentMethod::retrieve($payment_method);
|
||||
$stripe_payment_method_obj = $stripe_payment_method->jsonSerialize();
|
||||
$stripe_payment_method->attach(['customer' => $customer->id]);
|
||||
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->exp_month = $stripe_payment_method_obj['card']['exp_month'];
|
||||
$payment_meta->exp_year = $stripe_payment_method_obj['card']['exp_year'];
|
||||
$payment_meta->brand = $stripe_payment_method_obj['card']['brand'];
|
||||
$payment_meta->last4 = $stripe_payment_method_obj['card']['last4'];
|
||||
$payment_meta->type = GatewayType::CREDIT_CARD;
|
||||
|
||||
$client_gateway_token = new ClientGatewayToken();
|
||||
$client_gateway_token->company_id = $this->stripe->client->company->id;
|
||||
$client_gateway_token->client_id = $this->stripe->client->id;
|
||||
$client_gateway_token->token = $payment_method;
|
||||
$client_gateway_token->company_gateway_id = $this->stripe->company_gateway->id;
|
||||
$client_gateway_token->gateway_type_id = $gateway_type_id;
|
||||
$client_gateway_token->gateway_customer_reference = $customer->id;
|
||||
$client_gateway_token->meta = $payment_meta;
|
||||
$client_gateway_token->save();
|
||||
|
||||
if ($is_default == 'true' || $this->stripe->client->gateway_tokens->count() == 1) {
|
||||
$this->stripe->client->gateway_tokens()->update(['is_default' => 0]);
|
||||
|
||||
$client_gateway_token->is_default = 1;
|
||||
$client_gateway_token->save();
|
||||
}
|
||||
|
||||
return redirect()->route('client.payment_methods.index');
|
||||
}
|
||||
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
$payment_intent_data = [
|
||||
'amount' => $this->stripe->convertToStripeAmount($data['amount_with_fee'], $this->stripe->client->currency()->precision),
|
||||
'currency' => $this->stripe->client->getCurrencyCode(),
|
||||
'customer' => $this->stripe->findOrCreateCustomer(),
|
||||
'description' => $data['invoices']->pluck('id'), //todo more meaningful description here:
|
||||
];
|
||||
|
||||
if ($data['token']) {
|
||||
$payment_intent_data['payment_method'] = $data['token']->token;
|
||||
} else {
|
||||
$payment_intent_data['setup_future_usage'] = 'off_session';
|
||||
// $payment_intent_data['save_payment_method'] = true;
|
||||
// $payment_intent_data['confirm'] = true;
|
||||
}
|
||||
|
||||
$data['intent'] = $this->stripe->createPaymentIntent($payment_intent_data);
|
||||
$data['gateway'] = $this->stripe;
|
||||
|
||||
return render('gateways.stripe.credit_card', $data);
|
||||
}
|
||||
|
||||
public function paymentResponse($request)
|
||||
{
|
||||
$server_response = json_decode($request->input('gateway_response'));
|
||||
|
||||
$state = [
|
||||
'payment_method' => $server_response->payment_method,
|
||||
'payment_status' => $server_response->status,
|
||||
'save_card' => $request->store_card,
|
||||
'gateway_type_id' => $request->payment_method_id,
|
||||
'hashed_ids' => $request->hashed_ids,
|
||||
'server_response' => $server_response,
|
||||
];
|
||||
|
||||
$invoices = Invoice::whereIn('id', $this->stripe->transformKeys($state['hashed_ids']))
|
||||
->whereClientId($this->stripe->client->id)
|
||||
->get();
|
||||
|
||||
if ($this->stripe->getContact()) {
|
||||
$client_contact = $this->stripe->getContact();
|
||||
} else {
|
||||
$client_contact = $invoices->first()->invitations->first()->contact;
|
||||
}
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
$state['payment_intent'] = \Stripe\PaymentIntent::retrieve($server_response->id);
|
||||
$state['customer'] = $state['payment_intent']->customer;
|
||||
|
||||
if ($state['payment_status'] == 'succeeded') {
|
||||
return $this->processSuccessfulPayment($state);
|
||||
}
|
||||
|
||||
return $this->processUnsuccessfulPayment($server_response);
|
||||
}
|
||||
|
||||
private function processSuccessfulPayment($state)
|
||||
{
|
||||
$state['charge_id'] = $state['payment_intent']->charges->data[0]->id;
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
$state['payment_method'] = PaymentMethod::retrieve($state['payment_method']);
|
||||
$payment_method_object = $state['payment_method']->jsonSerialize();
|
||||
|
||||
$state['payment_meta'] = [
|
||||
'exp_month' => $payment_method_object['card']['exp_month'],
|
||||
'exp_year' => $payment_method_object['card']['exp_year'],
|
||||
'brand' => $payment_method_object['card']['brand'],
|
||||
'last4' => $payment_method_object['card']['last4'],
|
||||
'type' => $payment_method_object['type'],
|
||||
];
|
||||
|
||||
$payment_type = PaymentType::parseCardType($payment_method_object['card']['brand']);
|
||||
|
||||
if ($state['save_card'] === true) {
|
||||
$this->saveCard($state);
|
||||
}
|
||||
|
||||
// Todo: Need to fix this to support payment types other than credit card.... sepa etc etc
|
||||
if (!isset($state['payment_type'])) {
|
||||
$state['payment_type'] = PaymentType::CREDIT_CARD_OTHER;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'payment_method' => $state['charge_id'],
|
||||
'payment_type' => $state['payment_type'],
|
||||
'amount' => $state['server_response']->amount,
|
||||
];
|
||||
|
||||
$payment = $this->stripe->createPayment($data, $status = Payment::STATUS_COMPLETED);
|
||||
|
||||
$this->stripe->attachInvoices($payment, $state['hashed_ids']);
|
||||
|
||||
$payment->service()->updateInvoicePayment();
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company));
|
||||
|
||||
$logger_message = [
|
||||
'server_response' => $state['payment_intent'],
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
private function processUnsuccessfulPayment($server_response)
|
||||
{
|
||||
PaymentFailureMailer::dispatch($this->stripe->client, $server_response->cancellation_reason, $this->stripe->client->company, $server_response->amount);
|
||||
|
||||
$message = [
|
||||
'server_response' => $server_response,
|
||||
'data' => [],
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client);
|
||||
|
||||
throw new \Exception('Failed to process the payment.', 1);
|
||||
}
|
||||
|
||||
private function saveCard($state)
|
||||
{
|
||||
$state['payment_method']->attach(['customer' => $state['customer']]);
|
||||
|
||||
$company_gateway_token = new ClientGatewayToken();
|
||||
$company_gateway_token->company_id = $this->stripe->client->company->id;
|
||||
$company_gateway_token->client_id = $this->stripe->client->id;
|
||||
$company_gateway_token->token = $state['payment_method'];
|
||||
$company_gateway_token->company_gateway_id = $this->stripe->company_gateway->id;
|
||||
$company_gateway_token->gateway_type_id = $state['gateway_type_id'];
|
||||
$company_gateway_token->gateway_customer_reference = $state['customer'];
|
||||
$company_gateway_token->meta = $state['payment_meta'];
|
||||
$company_gateway_token->save();
|
||||
|
||||
if ($this->stripe->client->gateway_tokens->count() == 1) {
|
||||
$this->stripe->client->gateway_tokens()->update(['is_default' => 0]);
|
||||
|
||||
$company_gateway_token->is_default = 1;
|
||||
$company_gateway_token->save();
|
||||
}
|
||||
}
|
||||
}
|
105
app/PaymentDrivers/Stripe/SOFORT.php
Normal file
105
app/PaymentDrivers/Stripe/SOFORT.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Stripe;
|
||||
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\StripePaymentDriver;
|
||||
|
||||
class SOFORT
|
||||
{
|
||||
/** @var StripePaymentDriver */
|
||||
public $stripe;
|
||||
|
||||
public function __construct(StripePaymentDriver $stripe)
|
||||
{
|
||||
$this->stripe = $stripe;
|
||||
}
|
||||
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
$data['gateway'] = $this->stripe;
|
||||
$data['return_url'] = $this->buildReturnUrl($data);
|
||||
$data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['amount_with_fee'], $this->stripe->client->currency()->precision);
|
||||
$data['client'] = $this->stripe->client;
|
||||
$data['country'] = $this->stripe->client->country->iso_3166_2;
|
||||
|
||||
return render('gateways.stripe.sofort.pay', $data);
|
||||
}
|
||||
|
||||
private function buildReturnUrl($data): string
|
||||
{
|
||||
return route('client.payments.response', [
|
||||
'company_gateway_id' => $this->stripe->company_gateway->id,
|
||||
'gateway_type_id' => GatewayType::SOFORT,
|
||||
'hashed_ids' => implode(",", $data['hashed_ids']),
|
||||
'amount' => $data['amount'],
|
||||
'fee' => $data['fee'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function paymentResponse($request)
|
||||
{
|
||||
$state = array_merge($request->all(), []);
|
||||
$amount = $state['amount'] + $state['fee'];
|
||||
$state['amount'] = $this->stripe->convertToStripeAmount($amount, $this->stripe->client->currency()->precision);
|
||||
|
||||
if ($request->redirect_status == 'succeeded') {
|
||||
return $this->processSuccessfulPayment($state);
|
||||
}
|
||||
|
||||
return $this->processUnsuccessfulPayment($state);
|
||||
}
|
||||
|
||||
public function processSuccessfulPayment($state)
|
||||
{
|
||||
$state['charge_id'] = $state['source'];
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
$state['payment_type'] = PaymentType::SOFORT;
|
||||
|
||||
$data = [
|
||||
'payment_method' => $state['charge_id'],
|
||||
'payment_type' => $state['payment_type'],
|
||||
'amount' => $state['amount'],
|
||||
];
|
||||
|
||||
$payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING);
|
||||
|
||||
/** @todo: https://github.com/invoiceninja/invoiceninja/pull/3789/files#r436175798 */
|
||||
if (isset($state['hashed_ids'])) {
|
||||
$this->stripe->attachInvoices($payment, $state['hashed_ids']);
|
||||
}
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company));
|
||||
|
||||
$logger_message = [
|
||||
'server_response' => $state,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
public function processUnsuccessfulPayment($state)
|
||||
{
|
||||
return redirect()->route('client.invoices.index')->with('warning', ctrans('texts.status_voided'));
|
||||
}
|
||||
}
|
26
app/PaymentDrivers/Stripe/Utilities.php
Normal file
26
app/PaymentDrivers/Stripe/Utilities.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Stripe;
|
||||
|
||||
trait Utilities
|
||||
{
|
||||
public function convertFromStripeAmount($amount, $precision)
|
||||
{
|
||||
return $amount / pow(10, $precision);
|
||||
}
|
||||
|
||||
public function convertToStripeAmount($amount, $precision)
|
||||
{
|
||||
return $amount * pow(10, $precision);
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\Stripe\Utilities;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
@ -30,7 +31,7 @@ use Stripe\Stripe;
|
||||
|
||||
class StripePaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
use MakesHash;
|
||||
use MakesHash, Utilities;
|
||||
|
||||
protected $refundable = true;
|
||||
|
||||
@ -40,6 +41,8 @@ class StripePaymentDriver extends BasePaymentDriver
|
||||
|
||||
protected $customer_reference = 'customerReferenceParam';
|
||||
|
||||
protected $payment_method;
|
||||
|
||||
/**
|
||||
* Methods in this class are divided into
|
||||
* two separate streams
|
||||
@ -62,6 +65,15 @@ class StripePaymentDriver extends BasePaymentDriver
|
||||
Stripe::setApiKey($this->company_gateway->getConfigField('apiKey'));
|
||||
}
|
||||
|
||||
public function setPaymentMethod(string $method)
|
||||
{
|
||||
// Example: setPaymentMethod('App\\PaymentDrivers\\Stripe\\CreditCard');
|
||||
|
||||
$this->payment_method = new $method($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the gateway types
|
||||
*/
|
||||
@ -128,102 +140,39 @@ class StripePaymentDriver extends BasePaymentDriver
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorises a credit card for future use.
|
||||
*
|
||||
* @param array $data Array of variables needed for the view
|
||||
* Proxy method to pass the data into payment method authorizeView().
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function authorizeCreditCardView(array $data)
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
$intent['intent'] = $this->getSetupIntent();
|
||||
|
||||
return render('gateways.stripe.add_credit_card', array_merge($data, $intent));
|
||||
return $this->payment_method->authorizeView($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the gateway response for credit card authorization.
|
||||
*
|
||||
* @param Request $request The returning request object
|
||||
* @return view Returns the user to payment methods screen.
|
||||
* @throws \Stripe\Exception\ApiErrorException
|
||||
* @param \Illuminate\Http\Request $request The returning request object
|
||||
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function authorizeCreditCardResponse($request)
|
||||
{
|
||||
$server_response = json_decode($request->input('gateway_response'));
|
||||
|
||||
$gateway_id = $request->input('gateway_id');
|
||||
$gateway_type_id = $request->input('gateway_type_id');
|
||||
$is_default = $request->input('is_default');
|
||||
|
||||
$payment_method = $server_response->payment_method;
|
||||
|
||||
$customer = $this->findOrCreateCustomer();
|
||||
|
||||
$this->init();
|
||||
$stripe_payment_method = \Stripe\PaymentMethod::retrieve($payment_method);
|
||||
$stripe_payment_method_obj = $stripe_payment_method->jsonSerialize();
|
||||
$stripe_payment_method->attach(['customer' => $customer->id]);
|
||||
|
||||
$payment_meta = new \stdClass;
|
||||
|
||||
if ($stripe_payment_method_obj['type'] == 'card') {
|
||||
$payment_meta->exp_month = $stripe_payment_method_obj['card']['exp_month'];
|
||||
$payment_meta->exp_year = $stripe_payment_method_obj['card']['exp_year'];
|
||||
$payment_meta->brand = $stripe_payment_method_obj['card']['brand'];
|
||||
$payment_meta->last4 = $stripe_payment_method_obj['card']['last4'];
|
||||
$payment_meta->type = GatewayType::CREDIT_CARD;
|
||||
}
|
||||
|
||||
$cgt = new ClientGatewayToken;
|
||||
$cgt->company_id = $this->client->company->id;
|
||||
$cgt->client_id = $this->client->id;
|
||||
$cgt->token = $payment_method;
|
||||
$cgt->company_gateway_id = $this->company_gateway->id;
|
||||
$cgt->gateway_type_id = $gateway_type_id;
|
||||
$cgt->gateway_customer_reference = $customer->id;
|
||||
$cgt->meta = $payment_meta;
|
||||
$cgt->save();
|
||||
|
||||
if ($is_default == 'true' || $this->client->gateway_tokens->count() == 1) {
|
||||
$this->client->gateway_tokens()->update(['is_default'=>0]);
|
||||
|
||||
$cgt->is_default = 1;
|
||||
$cgt->save();
|
||||
}
|
||||
|
||||
return redirect()->route('client.payment_methods.index');
|
||||
return $this->payment_method->authorizeResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the payment with gateway.
|
||||
*
|
||||
* @param array $data
|
||||
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function processPaymentView(array $data)
|
||||
{
|
||||
$payment_intent_data = [
|
||||
'amount' => $this->convertToStripeAmount($data['amount_with_fee'], $this->client->currency()->precision),
|
||||
'currency' => $this->client->getCurrencyCode(),
|
||||
'customer' => $this->findOrCreateCustomer(),
|
||||
'description' => $data['invoices']->pluck('id'), //todo more meaningful description here:
|
||||
];
|
||||
|
||||
if ($data['token']) {
|
||||
$payment_intent_data['payment_method'] = $data['token']->token;
|
||||
} else {
|
||||
$payment_intent_data['setup_future_usage'] = 'off_session';
|
||||
// $payment_intent_data['save_payment_method'] = true;
|
||||
// $payment_intent_data['confirm'] = true;
|
||||
}
|
||||
|
||||
|
||||
$data['intent'] = $this->createPaymentIntent($payment_intent_data);
|
||||
|
||||
$data['gateway'] = $this;
|
||||
|
||||
return render($this->viewForType($data['payment_method_id']), $data);
|
||||
return $this->payment_method->paymentView($data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -256,137 +205,12 @@ class StripePaymentDriver extends BasePaymentDriver
|
||||
*/
|
||||
public function processPaymentResponse($request) //We never have to worry about unsuccessful payments as failures are handled at the front end for this driver.
|
||||
{
|
||||
$server_response = json_decode($request->input('gateway_response'));
|
||||
|
||||
$payment_method = $server_response->payment_method;
|
||||
$payment_status = $server_response->status;
|
||||
$save_card = $request->input('store_card');
|
||||
|
||||
$gateway_type_id = $request->input('payment_method_id');
|
||||
$hashed_ids = $request->input('hashed_ids');
|
||||
$invoices = Invoice::whereIn('id', $this->transformKeys($hashed_ids))
|
||||
->whereClientId($this->client->id)
|
||||
->get();
|
||||
/**
|
||||
* Potential statuses that can be returned
|
||||
*
|
||||
* requires_action
|
||||
* processing
|
||||
* canceled
|
||||
* requires_action
|
||||
* requires_confirmation
|
||||
* requires_payment_method
|
||||
*
|
||||
*/
|
||||
|
||||
if ($this->getContact()) {
|
||||
$client_contact = $this->getContact();
|
||||
} else {
|
||||
$client_contact = $invoices->first()->invitations->first()->contact;
|
||||
}
|
||||
|
||||
$this->init();
|
||||
$payment_intent = \Stripe\PaymentIntent::retrieve($server_response->id);
|
||||
$customer = $payment_intent->customer;
|
||||
|
||||
if ($payment_status == 'succeeded') {
|
||||
|
||||
$charge_id = $payment_intent->charges->data[0]->id;
|
||||
|
||||
$this->init();
|
||||
$stripe_payment_method = \Stripe\PaymentMethod::retrieve($payment_method);
|
||||
$stripe_payment_method_obj = $stripe_payment_method->jsonSerialize();
|
||||
|
||||
$payment_meta = new \stdClass;
|
||||
|
||||
if ($stripe_payment_method_obj['type'] == 'card') {
|
||||
$payment_meta->exp_month = $stripe_payment_method_obj['card']['exp_month'];
|
||||
$payment_meta->exp_year = $stripe_payment_method_obj['card']['exp_year'];
|
||||
$payment_meta->brand = $stripe_payment_method_obj['card']['brand'];
|
||||
$payment_meta->last4 = $stripe_payment_method_obj['card']['last4'];
|
||||
$payment_meta->type = $stripe_payment_method_obj['type'];
|
||||
|
||||
$payment_type = PaymentType::parseCardType($stripe_payment_method_obj['card']['brand']);
|
||||
}
|
||||
|
||||
if ($save_card == 'true') {
|
||||
$stripe_payment_method->attach(['customer' => $customer]);
|
||||
|
||||
$cgt = new ClientGatewayToken;
|
||||
$cgt->company_id = $this->client->company->id;
|
||||
$cgt->client_id = $this->client->id;
|
||||
$cgt->token = $payment_method;
|
||||
$cgt->company_gateway_id = $this->company_gateway->id;
|
||||
$cgt->gateway_type_id = $gateway_type_id;
|
||||
$cgt->gateway_customer_reference = $customer;
|
||||
$cgt->meta = $payment_meta;
|
||||
$cgt->save();
|
||||
|
||||
if ($this->client->gateway_tokens->count() == 1) {
|
||||
$this->client->gateway_tokens()->update(['is_default'=>0]);
|
||||
|
||||
$cgt->is_default = 1;
|
||||
$cgt->save();
|
||||
}
|
||||
}
|
||||
|
||||
//todo need to fix this to support payment types other than credit card.... sepa etc etc
|
||||
if (!$payment_type) {
|
||||
$payment_type = PaymentType::CREDIT_CARD_OTHER;
|
||||
}
|
||||
|
||||
|
||||
$data = [
|
||||
'payment_method' => $charge_id,
|
||||
'payment_type' => $payment_type,
|
||||
'amount' => $server_response->amount,
|
||||
];
|
||||
|
||||
/* Create payment*/
|
||||
$payment = $this->createPayment($data);
|
||||
|
||||
/* Link invoices to payment*/
|
||||
$this->attachInvoices($payment, $hashed_ids);
|
||||
|
||||
$payment->service()->UpdateInvoicePayment();
|
||||
|
||||
event(new PaymentWasCreated($payment, $payment->company));
|
||||
|
||||
SystemLogger::dispatch(
|
||||
[
|
||||
'server_response' => $payment_intent,
|
||||
'data' => $data
|
||||
],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_STRIPE,
|
||||
$this->client
|
||||
);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
|
||||
} else {
|
||||
|
||||
PaymentFailureMailer::dispatch($this->client, $server_response->cancellation_reason, $this->client->company, $server_response->amount);
|
||||
|
||||
/*Fail and log*/
|
||||
SystemLogger::dispatch(
|
||||
[
|
||||
'server_response' => $server_response,
|
||||
'data' => $data
|
||||
],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_STRIPE,
|
||||
$this->client
|
||||
);
|
||||
|
||||
throw new \Exception("Failed to process payment", 1);
|
||||
}
|
||||
return $this->payment_method->paymentResponse($request);
|
||||
}
|
||||
|
||||
public function createPayment($data) :Payment
|
||||
public function createPayment($data, $status = Payment::STATUS_COMPLETED) :Payment
|
||||
{
|
||||
$payment = parent::createPayment($data);
|
||||
$payment = parent::createPayment($data, $status);
|
||||
|
||||
$client_contact = $this->getContact();
|
||||
$client_contact_id = $client_contact ? $client_contact->id : null;
|
||||
@ -400,15 +224,6 @@ class StripePaymentDriver extends BasePaymentDriver
|
||||
return $payment;
|
||||
}
|
||||
|
||||
private function convertFromStripeAmount($amount, $precision)
|
||||
{
|
||||
return $amount / pow(10, $precision);
|
||||
}
|
||||
|
||||
private function convertToStripeAmount($amount, $precision)
|
||||
{
|
||||
return $amount * pow(10, $precision);
|
||||
}
|
||||
/**
|
||||
* Creates a new String Payment Intent
|
||||
*
|
||||
@ -515,5 +330,15 @@ class StripePaymentDriver extends BasePaymentDriver
|
||||
return false;
|
||||
}
|
||||
|
||||
public function verificationView(ClientGatewayToken $payment_method)
|
||||
{
|
||||
return $this->payment_method->verificationView($payment_method);
|
||||
}
|
||||
|
||||
public function processVerification(ClientGatewayToken $payment_method)
|
||||
{
|
||||
return $this->payment_method->processVerification($payment_method);
|
||||
}
|
||||
|
||||
/************************************** Omnipay API methods **********************************************************/
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Client;
|
||||
@ -31,8 +32,11 @@ class ActivityRepository extends BaseRepository
|
||||
* @param stdClass $fields The fields
|
||||
* @param Collection $entity The entity that you wish to have backed up (typically Invoice, Quote etc etc rather than Payment)
|
||||
*/
|
||||
public function save($fields, $entity)
|
||||
public function save($fields, $entity, $db = null)
|
||||
{
|
||||
if($db)
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$activity = new Activity();
|
||||
|
||||
$activity->is_system = app()->runningInConsole();
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Events\Invoice\InvoiceWasUpdated;
|
||||
use App\Factory\InvoiceInvitationFactory;
|
||||
use App\Factory\QuoteInvitationFactory;
|
||||
use App\Jobs\Product\UpdateOrCreateProduct;
|
||||
@ -262,7 +263,7 @@ class BaseRepository
|
||||
//make sure we are creating an invite for a contact who belongs to the client only!
|
||||
$contact = ClientContact::find($invitation['client_contact_id']);
|
||||
|
||||
if ($model->client_id == $contact->client_id);
|
||||
if ($contact && $model->client_id == $contact->client_id);
|
||||
{
|
||||
$new_invitation = $invitation_factory_class::create($model->company_id, $model->user_id);
|
||||
$new_invitation->{$lcfirst_resource_id} = $model->id;
|
||||
@ -294,6 +295,9 @@ class BaseRepository
|
||||
}
|
||||
|
||||
$model = $model->calc()->getInvoice();
|
||||
|
||||
event(new InvoiceWasUpdated($model, $model->company));
|
||||
|
||||
}
|
||||
|
||||
if ($class->name == Credit::class) {
|
||||
|
@ -46,7 +46,6 @@ class PaymentRepository extends BaseRepository
|
||||
/**
|
||||
* Saves and updates a payment. //todo refactor to handle refunds and payments.
|
||||
*
|
||||
*
|
||||
* @param array $data the request object
|
||||
* @param Payment $payment The Payment object
|
||||
* @return Payment|null Payment $payment
|
||||
@ -57,61 +56,85 @@ class PaymentRepository extends BaseRepository
|
||||
return $this->applyPayment($data, $payment);
|
||||
}
|
||||
|
||||
return $this->refundPayment($data, $payment);
|
||||
return $payment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a positive payment request
|
||||
* @param array $data The data object
|
||||
* @param Payment $payment The $payment entity
|
||||
* @param array $data The data object
|
||||
* @param Payment $payment The $payment entity
|
||||
* @return Payment The updated/created payment object
|
||||
*/
|
||||
private function applyPayment(array $data, Payment $payment): ?Payment
|
||||
{
|
||||
|
||||
//check currencies here and fill the exchange rate data if necessary
|
||||
if (!$payment->id) {
|
||||
$this->processExchangeRates($data, $payment);
|
||||
|
||||
/*We only update the paid to date ONCE per payment*/
|
||||
if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) {
|
||||
|
||||
if($data['amount'] == '')
|
||||
$data['amount'] = array_sum(array_column($data['invoices'], 'amount'));
|
||||
|
||||
$client = Client::find($data['client_id']);
|
||||
$client->service()->updatePaidToDate($data['amount'])->save();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*Fill the payment*/
|
||||
$payment->fill($data);
|
||||
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
|
||||
$payment->save();
|
||||
|
||||
|
||||
/*Ensure payment number generated*/
|
||||
if (!$payment->number || strlen($payment->number) == 0) {
|
||||
$payment->number = $payment->client->getNextPaymentNumber($payment->client);
|
||||
}
|
||||
|
||||
$payment->client->service()->updatePaidToDate($payment->amount)->save();
|
||||
|
||||
$invoice_totals = 0;
|
||||
$credit_totals = 0;
|
||||
|
||||
if (array_key_exists('invoices', $data) && is_array($data['invoices'])) {
|
||||
/*Iterate through invoices and apply payments*/
|
||||
if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) {
|
||||
$invoice_totals = array_sum(array_column($data['invoices'], 'amount'));
|
||||
|
||||
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
|
||||
|
||||
$payment->invoices()->saveMany($invoices);
|
||||
|
||||
info("iterating through payment invoices");
|
||||
|
||||
foreach ($data['invoices'] as $paid_invoice) {
|
||||
$invoice = Invoice::whereId($paid_invoice['invoice_id'])->first();
|
||||
|
||||
$invoice = Invoice::whereId($paid_invoice['invoice_id'])->with('client')->first();
|
||||
|
||||
info("current client balance = {$invoice->client->balance}");
|
||||
|
||||
if ($invoice) {
|
||||
$invoice->service()->applyPayment($payment, $paid_invoice['amount'])->save();
|
||||
|
||||
info("apply payment amount {$paid_invoice['amount']}");
|
||||
|
||||
$invoice = $invoice->service()->markSent()->applyPayment($payment, $paid_invoice['amount'])->save();
|
||||
|
||||
info("after processing invoice the client balance is now {$invoice->client->balance}");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
//payment is made, but not to any invoice, therefore we are applying the payment to the clients credit
|
||||
$payment->client->processUnappliedPayment($payment->amount);
|
||||
//payment is made, but not to any invoice, therefore we are applying the payment to the clients paid_to_date only
|
||||
$payment->client->service()->updatePaidToDate($payment->amount)->save();
|
||||
}
|
||||
|
||||
if (array_key_exists('credits', $data) && is_array($data['credits'])) {
|
||||
$credit_totals = array_sum(array_column($data['credits'], 'amount'));
|
||||
|
||||
$credits = Credit::whereIn('id', $this->transformKeys(array_column($data['credits'], 'credit_id')))->get();
|
||||
|
||||
$payment->credits()->saveMany($credits);
|
||||
|
||||
foreach ($data['credits'] as $paid_credit) {
|
||||
@ -128,8 +151,9 @@ class PaymentRepository extends BaseRepository
|
||||
$invoice_totals -= $credit_totals;
|
||||
|
||||
//$payment->amount = $invoice_totals; //creates problems when setting amount like this.
|
||||
|
||||
if ($invoice_totals == $payment->amount) {
|
||||
if($credit_totals == $payment->amount){
|
||||
$payment->applied += $credit_totals;
|
||||
} elseif ($invoice_totals == $payment->amount) {
|
||||
$payment->applied += $payment->amount;
|
||||
} elseif ($invoice_totals < $payment->amount) {
|
||||
$payment->applied += $invoice_totals;
|
||||
@ -140,53 +164,6 @@ class PaymentRepository extends BaseRepository
|
||||
return $payment->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Refundable trait replaces this.
|
||||
*/
|
||||
private function refundPayment(array $data, Payment $payment): string
|
||||
{
|
||||
// //temp variable to sum the total refund/credit amount
|
||||
// $invoice_total_adjustment = 0;
|
||||
|
||||
// if (array_key_exists('invoices', $data) && is_array($data['invoices'])) {
|
||||
|
||||
// foreach ($data['invoices'] as $adjusted_invoice) {
|
||||
|
||||
// $invoice = Invoice::whereId($adjusted_invoice['invoice_id'])->first();
|
||||
|
||||
// $invoice_total_adjustment += $adjusted_invoice['amount'];
|
||||
|
||||
// if (array_key_exists('credits', $adjusted_invoice)) {
|
||||
|
||||
// //process and insert credit notes
|
||||
// foreach ($adjusted_invoice['credits'] as $credit) {
|
||||
|
||||
// $credit = $this->credit_repo->save($credit, CreditFactory::create(auth()->user()->id, auth()->user()->id), $invoice);
|
||||
|
||||
// }
|
||||
|
||||
// } else {
|
||||
// //todo - generate Credit Note for $amount on $invoice - the assumption here is that it is a FULL refund
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// if (array_key_exists('amount', $data) && $data['amount'] != $invoice_total_adjustment)
|
||||
// return 'Amount must equal the sum of invoice adjustments';
|
||||
// }
|
||||
|
||||
|
||||
// //adjust applied amount
|
||||
// $payment->applied += $invoice_total_adjustment;
|
||||
|
||||
// //adjust clients paid to date
|
||||
// $client = $payment->client;
|
||||
// $client->paid_to_date += $invoice_total_adjustment;
|
||||
|
||||
// $payment->save();
|
||||
// $client->save();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the client is paying in a currency other than
|
||||
|
@ -37,7 +37,14 @@ class ApplyPayment extends AbstractService
|
||||
->ledger()
|
||||
->updatePaymentBalance($this->payment_amount*-1);
|
||||
|
||||
$this->payment->client->service()->updateBalance($this->payment_amount*-1)->save();
|
||||
info("apply payment method - current client balance = {$this->payment->client->balance}");
|
||||
|
||||
info("reducing client balance by payment amount {$this->payment_amount}");
|
||||
|
||||
$this->invoice->client->service()->updateBalance($this->payment_amount*-1)->save();
|
||||
// $this->invoice->client->service()->updateBalance($this->payment_amount*-1)->updatePaidToDate($this->payment_amount)->save();
|
||||
|
||||
info("post client balance = {$this->invoice->client->balance}");
|
||||
|
||||
/* Update Pivot Record amount */
|
||||
$this->payment->invoices->each(function ($inv) {
|
||||
@ -47,6 +54,10 @@ class ApplyPayment extends AbstractService
|
||||
}
|
||||
});
|
||||
|
||||
$this->invoice->fresh('client');
|
||||
|
||||
info("1 end of apply payment method the client balance = {$this->invoice->client->balance}");
|
||||
|
||||
if ($this->invoice->hasPartial()) {
|
||||
//is partial and amount is exactly the partial amount
|
||||
if ($this->invoice->partial == $this->payment_amount) {
|
||||
@ -61,9 +72,11 @@ class ApplyPayment extends AbstractService
|
||||
} elseif ($this->payment_amount < $this->invoice->balance) { //partial invoice payment made
|
||||
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($this->payment_amount*-1);
|
||||
}
|
||||
info("2 end of apply payment method the client balnace = {$this->invoice->client->balance}");
|
||||
|
||||
$this->invoice->service()->applyNumber()->save();
|
||||
|
||||
info("3 end of apply payment method the client balnace = {$this->invoice->client->balance}");
|
||||
return $this->invoice;
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,9 @@ class HandleCancellation extends AbstractService
|
||||
}
|
||||
|
||||
$adjustment = $this->invoice->balance*-1;
|
||||
|
||||
$this->backupCancellation($adjustment);
|
||||
|
||||
//set invoice balance to 0
|
||||
$this->invoice->ledger()->updateInvoiceBalance($adjustment, "Invoice cancellation");
|
||||
|
||||
@ -56,6 +59,58 @@ class HandleCancellation extends AbstractService
|
||||
|
||||
event(new InvoiceWasCancelled($this->invoice));
|
||||
|
||||
|
||||
return $this->invoice;
|
||||
}
|
||||
|
||||
public function reverse()
|
||||
{
|
||||
|
||||
$cancellation = $this->invoice->backup->cancellation;
|
||||
|
||||
$adjustment = $cancellation->adjustment*-1;
|
||||
|
||||
$this->invoice->ledger()->updateInvoiceBalance($adjustment, "Invoice cancellation REVERSAL");
|
||||
|
||||
/* Reverse the invoice status and balance */
|
||||
$this->invoice->balance += $adjustment;
|
||||
$this->invoice->status_id = $cancellation->status_id;
|
||||
|
||||
$this->invoice->client->service()->updateBalance($adjustment)->save();
|
||||
|
||||
/* Pop the cancellation out of the backup*/
|
||||
$backup = $this->invoice->backup;
|
||||
unset($backup->cancellation);
|
||||
$this->invoice->backup = $backup;
|
||||
$this->invoice->save();
|
||||
|
||||
return $this->invoice;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Backup the cancellation in case we ever need to reverse it.
|
||||
*
|
||||
* @param float $adjustment The amount the balance has been reduced by to cancel the invoice
|
||||
* @return void
|
||||
*/
|
||||
private function backupCancellation($adjustment)
|
||||
{
|
||||
|
||||
if(!is_object($this->invoice->backup)){
|
||||
$backup = new \stdClass;
|
||||
$this->invoice->backup = $backup;
|
||||
}
|
||||
|
||||
$cancellation = new \stdClass;
|
||||
$cancellation->adjustment = $adjustment;
|
||||
$cancellation->status_id = $this->invoice->status_id;
|
||||
|
||||
$invoice_backup = $this->invoice->backup;
|
||||
$invoice_backup->cancellation = $cancellation;
|
||||
|
||||
$this->invoice->backup = $invoice_backup;
|
||||
$this->invoice->save();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -44,12 +44,14 @@ class HandleReversal extends AbstractService
|
||||
return $this->invoice;
|
||||
}
|
||||
|
||||
if($this->invoice->status_id == Invoice::STATUS_CANCELLED)
|
||||
$this->invoice = $this->invoice->service()->reverseCancellation()->save();
|
||||
|
||||
$balance_remaining = $this->invoice->balance;
|
||||
|
||||
$total_paid = $this->invoice->amount - $this->invoice->balance;
|
||||
|
||||
/*Adjust payment applied and the paymentables to the correct amount */
|
||||
|
||||
$paymentables = Paymentable::wherePaymentableType(Invoice::class)
|
||||
->wherePaymentableId($this->invoice->id)
|
||||
->get();
|
||||
@ -69,7 +71,8 @@ class HandleReversal extends AbstractService
|
||||
if ($total_paid > 0) {
|
||||
$credit = CreditFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||
$credit->client_id = $this->invoice->client_id;
|
||||
|
||||
$credit->invoice_id = $this->invoice->id;
|
||||
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
$item->cost = (float)$total_paid;
|
||||
@ -88,10 +91,12 @@ class HandleReversal extends AbstractService
|
||||
|
||||
$credit->service()->markSent()->save();
|
||||
}
|
||||
/* Set invoice balance to 0 */
|
||||
$this->invoice->ledger()->updateInvoiceBalance($balance_remaining*-1, $notes)->save();
|
||||
|
||||
$this->invoice->balance= 0;
|
||||
/* Set invoice balance to 0 */
|
||||
if($this->invoice->balance != 0)
|
||||
$this->invoice->ledger()->updateInvoiceBalance($balance_remaining*-1, $notes)->save();
|
||||
|
||||
$this->invoice->balance=0;
|
||||
|
||||
/* Set invoice status to reversed... somehow*/
|
||||
$this->invoice->service()->setStatus(Invoice::STATUS_REVERSED)->save();
|
||||
@ -109,6 +114,42 @@ class HandleReversal extends AbstractService
|
||||
return $this->invoice;
|
||||
//create a ledger row for this with the resulting Credit ( also include an explanation in the notes section )
|
||||
}
|
||||
|
||||
|
||||
// public function run2()
|
||||
// {
|
||||
|
||||
// /* Check again!! */
|
||||
// if (!$this->invoice->invoiceReversable($this->invoice)) {
|
||||
// return $this->invoice;
|
||||
// }
|
||||
|
||||
// if($this->invoice->status_id == Invoice::STATUS_CANCELLED)
|
||||
// $this->invoice = $this->invoice->service()->reverseCancellation()->save();
|
||||
|
||||
// //$balance_remaining = $this->invoice->balance;
|
||||
|
||||
// //$total_paid = $this->invoice->amount - $this->invoice->balance;
|
||||
|
||||
// /*Adjust payment applied and the paymentables to the correct amount */
|
||||
// $paymentables = Paymentable::wherePaymentableType(Invoice::class)
|
||||
// ->wherePaymentableId($this->invoice->id)
|
||||
// ->get();
|
||||
|
||||
// $total_paid = 0;
|
||||
|
||||
// $paymentables->each(function ($paymentable) use ($total_paid) {
|
||||
|
||||
// $reversable_amount = $paymentable->amount - $paymentable->refunded;
|
||||
// $total_paid -= $reversable_amount;
|
||||
// $paymentable->amount = $paymentable->refunded;
|
||||
// $paymentable->save();
|
||||
// });
|
||||
|
||||
// //Unwinding any payments made to this invoice
|
||||
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
// The client paid to date amount is reduced by the calculated amount of (invoice balance - invoice amount).
|
||||
|
@ -129,6 +129,13 @@ class InvoiceService
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function reverseCancellation()
|
||||
{
|
||||
$this->invoice = (new HandleCancellation($this->invoice))->reverse();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function markViewed()
|
||||
{
|
||||
$this->invoice->last_viewed = Carbon::now()->format('Y-m-d H:i');
|
||||
|
@ -48,10 +48,14 @@ class MarkSent extends AbstractService
|
||||
->setDueDate()
|
||||
->save();
|
||||
|
||||
info("marking invoice sent currently client balance = {$this->client->balance}");
|
||||
|
||||
$this->client->service()->updateBalance($this->invoice->balance)->save();
|
||||
|
||||
info("after marking invoice sent currently client balance = {$this->client->balance}");
|
||||
|
||||
$this->invoice->ledger()->updateInvoiceBalance($this->invoice->balance);
|
||||
|
||||
return $this->invoice;
|
||||
return $this->invoice->fresh();
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user