Working on invoice payments with gateway fees

This commit is contained in:
David Bomba 2020-08-31 12:00:43 +10:00
parent ac08a146a1
commit eb867522df
8 changed files with 127 additions and 143 deletions

View File

@ -136,7 +136,21 @@ class PaymentController extends Controller
$payment_method_id = request()->input('payment_method_id');
$invoice_totals = array_sum(array_column($payable_invoices,'amount'));
$fee_totals = round($gateway->calcGatewayFee($invoice_totals), $invoices->first()->client->currency()->precision);
$first_invoice = $invoices->first();
$fee_totals = round($gateway->calcGatewayFee($invoice_totals, true), $first_invoice->client->currency()->precision);
if(!$first_invoice->uses_inclusive_taxes) {
$fee_tax = 0;
$fee_tax += round(($first_invoice->tax_rate1/100)*$fee_totals, $first_invoice->client->currency()->precision);
$fee_tax += round(($first_invoice->tax_rate2/100)*$fee_totals, $first_invoice->client->currency()->precision);
$fee_tax += round(($first_invoice->tax_rate3/100)*$fee_totals, $first_invoice->client->currency()->precision);
$fee_totals += $fee_tax;
}
$first_invoice->service()->addGatewayFee($gateway, $invoice_totals)->save();
$payment_hash = new PaymentHash;
$payment_hash->hash = Str::random(128);

View File

@ -267,7 +267,7 @@ class CompanyGateway extends BaseModel
return $label;
}
public function calcGatewayFee($amount)
public function calcGatewayFee($amount, $include_taxes = false)
{
$fees_and_limits = $this->getFeesAndLimits();
@ -286,29 +286,32 @@ class CompanyGateway extends BaseModel
$fee += $amount * $fees_and_limits->fee_percent / 100;
info("fee after adding fee percent = {$fee}");
}
$pre_tax_fee = $fee;
//we shouldn't calculate the taxes - they'll be done when we re-process the invoice
// if ($fees_and_limits->fee_tax_rate1) {
// $fee += $pre_tax_fee * $fees_and_limits->fee_tax_rate1 / 100;
// info("fee after adding fee tax 1 = {$fee}");
// }
// if ($fees_and_limits->fee_tax_rate2) {
// $fee += $pre_tax_fee * $fees_and_limits->fee_tax_rate2 / 100;
// info("fee after adding fee tax 2 = {$fee}");
// }
// if ($fees_and_limits->fee_tax_rate3) {
// $fee += $pre_tax_fee * $fees_and_limits->fee_tax_rate3 / 100;
// info("fee after adding fee tax 3 = {$fee}");
// }
/* Cap fee if we have to here. */
if($fees_and_limits->fee_cap > 0 && ($fee > $fees_and_limits->fee_cap))
$fee = $fees_and_limits->fee_cap;
$pre_tax_fee = $fee;
/**/
if($include_taxes)
{
if ($fees_and_limits->fee_tax_rate1) {
$fee += $pre_tax_fee * $fees_and_limits->fee_tax_rate1 / 100;
info("fee after adding fee tax 1 = {$fee}");
}
if ($fees_and_limits->fee_tax_rate2) {
$fee += $pre_tax_fee * $fees_and_limits->fee_tax_rate2 / 100;
info("fee after adding fee tax 2 = {$fee}");
}
if ($fees_and_limits->fee_tax_rate3) {
$fee += $pre_tax_fee * $fees_and_limits->fee_tax_rate3 / 100;
info("fee after adding fee tax 3 = {$fee}");
}
}
return $fee;
}

View File

@ -167,7 +167,7 @@ class BaseDriver extends AbstractPaymentDriver
* @param PaymentResponseRequest $request The incoming payment request
* @return void Success/Failure
*/
public function appendGatewayFeeToInvoice(PaymentResponseRequest $request) :void
public function confirmGatewayFee(PaymentResponseRequest $request) :void
{
/*Payment meta data*/
$payment_hash = $request->getPaymentHash();
@ -175,22 +175,24 @@ class BaseDriver extends AbstractPaymentDriver
/*Payment invoices*/
$payment_invoices = $payment_hash->invoices();
/*Fee charged at gateway*/
// /*Fee charged at gateway*/
$fee_total = $payment_hash->fee_total;
/*Sum of invoice amounts*/
$invoice_totals = array_sum(array_column($payment_invoices,'amount'));
// Sum of invoice amounts
// $invoice_totals = array_sum(array_column($payment_invoices,'amount'));
/*Hydrate invoices*/
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_invoices, 'invoice_id')))->get();
/*Append gateway fee to invoice line item of first invoice*/
if($fee_total != 0){
$invoices->first()->service()->addGatewayFee($this->company_gateway, $invoice_totals)->save();
$invoices->each(function($invoice) use($fee_total){
//We need to update invoice balance / client balance at this point so
//that payment record sanity is preserved //todo
}
if(collect($invoice->line_items)->contains('type_id', '3')){
$invoice->service()->toggleFeesPaid()->save();
$invoice->client->service()->updateBalance($fee_total)->save();
$invoice->ledger()->updateInvoiceBalance($fee_total, $notes = 'Gateway fee adjustment');
}
});
}
}

View File

@ -295,7 +295,7 @@ class BasePaymentDriver
* @param PaymentResponseRequest $request The incoming payment request
* @return void Success/Failure
*/
public function appendGatewayFeeToInvoice(PaymentResponseRequest $request) :void
public function confirmGatewayFee(PaymentResponseRequest $request) :void
{
/*Payment meta data*/
$payment_hash = $request->getPaymentHash();
@ -303,22 +303,24 @@ class BasePaymentDriver
/*Payment invoices*/
$payment_invoices = $payment_hash->invoices();
/*Fee charged at gateway*/
// /*Fee charged at gateway*/
$fee_total = $payment_hash->fee_total;
/*Sum of invoice amounts*/
$invoice_totals = array_sum(array_column($payment_invoices,'amount'));
// Sum of invoice amounts
// $invoice_totals = array_sum(array_column($payment_invoices,'amount'));
/*Hydrate invoices*/
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_invoices, 'invoice_id')))->get();
/*Append gateway fee to invoice line item of first invoice*/
if($fee_total != 0){
$invoices->first()->service()->addGatewayFee($this->company_gateway, $invoice_totals)->save();
$invoices->each(function($invoice) use($fee_total){
//We need to update invoice balance / client balance at this point so
//that payment record sanity is preserved //todo
}
if(collect($invoice->line_items)->contains('type_id', '3')){
$invoice->service()->toggleFeesPaid()->save();
$invoice->client->service()->updateBalance($fee_total)->save();
$invoice->ledger()->updateInvoiceBalance($fee_total, $notes = 'Gateway fee adjustment');
}
});
}
}

View File

@ -123,6 +123,7 @@ class CreditCard
'gateway_type_id' => $request->payment_method_id,
'hashed_ids' => $request->hashed_ids,
'server_response' => $server_response,
'payment_hash' => $payment_hash,
];
$invoices = Invoice::whereIn('id', $this->stripe->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))
@ -143,7 +144,7 @@ class CreditCard
if ($state['payment_status'] == 'succeeded') {
/* Add gateway fees if needed! */
$this->stripe->appendGatewayFeeToInvoice($request);
$this->stripe->confirmGatewayFee($request);
return $this->processSuccessfulPayment($state);
}
@ -190,7 +191,7 @@ class CreditCard
$this->stripe->attachInvoices($payment, $state['hashed_ids']);
$payment->service()->updateInvoicePayment();
$payment->service()->updateInvoicePayment($state['payment_hash']);
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));

View File

@ -60,7 +60,7 @@ class AddGatewayFee extends AbstractService
$invoice_items = $this->invoice->line_items;
$invoice_items = collect($invoice_items)->filter(function ($item){
return $item->type_id != 3;
return $item->type_id != '3';
});
$this->invoice->line_items = $invoice_items;
@ -71,7 +71,7 @@ class AddGatewayFee extends AbstractService
private function processGatewayFee($gateway_fee)
{
$invoice_item = new InvoiceItem;
$invoice_item->type_id = '4';
$invoice_item->type_id = '3';
$invoice_item->product_key = ctrans('texts.surcharge');
$invoice_item->notes = ctrans('texts.online_payment_surcharge');
$invoice_item->quantity = 1;
@ -92,10 +92,9 @@ class AddGatewayFee extends AbstractService
/**Refresh Invoice values*/
$this->invoice = $this->invoice->calc()->getInvoice();
/*Update client balance*/
$this->invoice->client->service()->updateBalance($gateway_fee)->save();
$this->invoice->ledger()->updateInvoiceBalance($gateway_fee, $notes = 'Gateway fee adjustment');
/*Update client balance*/ // don't increment until we have process the payment!
//$this->invoice->client->service()->updateBalance($gateway_fee)->save();
//$this->invoice->ledger()->updateInvoiceBalance($gateway_fee, $notes = 'Gateway fee adjustment');
return $this->invoice;
@ -104,7 +103,7 @@ class AddGatewayFee extends AbstractService
private function processGatewayDiscount($gateway_fee)
{
$invoice_item = new InvoiceItem;
$invoice_item->type_id = 3;
$invoice_item->type_id = '3';
$invoice_item->product_key = ctrans('texts.discount');
$invoice_item->notes = ctrans('texts.online_payment_discount');
$invoice_item->quantity = 1;
@ -124,9 +123,9 @@ class AddGatewayFee extends AbstractService
$this->invoice = $this->invoice->calc()->getInvoice();
$this->invoice->client->service()->updateBalance($gateway_fee)->save();
// $this->invoice->client->service()->updateBalance($gateway_fee)->save();
$this->invoice->ledger()->updateInvoiceBalance($gateway_fee, $notes = 'Discount fee adjustment');
// $this->invoice->ledger()->updateInvoiceBalance($gateway_fee, $notes = 'Discount fee adjustment');
return $this->invoice;
}

View File

@ -84,9 +84,9 @@ class PaymentService
return (new DeletePayment($this->payment))->run();
}
public function updateInvoicePayment() :?Payment
public function updateInvoicePayment($payment_hash = null) :?Payment
{
return ((new UpdateInvoicePayment($this->payment)))->run();
return ((new UpdateInvoicePayment($this->payment, $payment_hash)))->run();
}
public function applyNumber()

View File

@ -17,119 +17,82 @@ use App\Jobs\Payment\EmailPayment;
use App\Jobs\Util\SystemLogger;
use App\Models\Invoice;
use App\Models\SystemLog;
use App\Utils\Traits\MakesHash;
class UpdateInvoicePayment
{
use MakesHash;
/**
* @deprecated This is bad logic, assumes too much.
*/
public $payment;
public function __construct($payment)
public $payment_hash;
public function __construct($payment, $payment_hash)
{
$this->payment = $payment;
$this->payment_hash = $payment_hash;
}
public function run()
{
$invoices = $this->payment->invoices()->get();
// $invoices = $this->payment->invoices()->get();
// $invoices_total = $invoices->sum('balance');
$invoices_total = $invoices->sum('balance');
$paid_invoices = $this->payment_hash->invoices();
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($paid_invoices, 'invoice_id')))->get();
/* Simplest scenario - All invoices are paid in full*/
if (strval($invoices_total) === strval($this->payment->amount)) {
$invoices->each(function ($invoice) {
$this->payment
->ledger()
->updatePaymentBalance($invoice->balance*-1);
$this->payment->client
->service()
->updateBalance($invoice->balance*-1)
->updatePaidToDate($invoice->balance)
->save();
$invoice->pivot->amount = $invoice->balance;
collect($paid_invoices)->each(function ($paid_invoice) use($invoices) {
$invoice = $invoices->first(function ($inv) use($paid_invoice) {
return $paid_invoice['invoice_id'] == $inv->hashed_id;
});
$this->payment
->ledger()
->updatePaymentBalance($paid_invoice->amount*-1);
$this->payment
->client
->service()
->updateBalance($paid_invoice->amount*-1)
->updatePaidToDate($paid_invoice->amount)
->save();
$invoice->pivot->amount = $paid_invoice->amount;
$invoice->pivot->save();
$invoice->service()
$invoice->service() //caution what if we amount paid was less than partial - we wipe it!
->clearPartial()
->updateBalance($invoice->balance*-1)
->updateBalance($paid_invoice->amount*-1)
->save();
});
}
/*Combination of partials and full invoices are being paid*/
else {
$total = 0;
/* Calculate the grand total of the invoices*/
foreach ($invoices as $invoice) {
if ($invoice->hasPartial()) {
$total += $invoice->partial;
} else {
$total += $invoice->balance;
}
}
});
/*Test if there is a batch of partial invoices that have been paid */
if ($this->payment->amount == $total) {
$invoices->each(function ($invoice) {
if ($invoice->hasPartial()) {
$this->payment
->ledger()
->updatePaymentBalance($invoice->partial*-1);
// } else {
// SystemLogger::dispatch(
// [
// 'payment' => $this->payment,
// 'invoices' => $invoices,
// 'invoices_total' => $invoices_total,
// 'payment_amount' => $this->payment->amount,
// 'partial_check_amount' => $total,
// ],
// SystemLog::CATEGORY_GATEWAY_RESPONSE,
// SystemLog::EVENT_PAYMENT_RECONCILIATION_FAILURE,
// SystemLog::TYPE_LEDGER,
// $this->payment->client
// );
$this->payment->client->service()
->updateBalance($invoice->partial*-1)
->updatePaidToDate($invoice->partial)
->save();
// throw new \Exception("payment amount {$this->payment->amount} does not match invoice totals {$invoices_total} reversing payment");
$invoice->pivot->amount = $invoice->partial;
$invoice->pivot->save();
$invoice->service()->updateBalance($invoice->partial*-1)
->clearPartial()
->setDueDate()
->setStatus(Invoice::STATUS_PARTIAL)
->save();
} else {
$this->payment
->ledger()
->updatePaymentBalance($invoice->balance*-1);
$this->payment->client->service()
->updateBalance($invoice->balance*-1)
->updatePaidToDate($invoice->balance)
->save();
$invoice->pivot->amount = $invoice->balance;
$invoice->pivot->save();
$invoice->service()->clearPartial()->updateBalance($invoice->balance*-1)->save();
}
});
} else {
SystemLogger::dispatch(
[
'payment' => $this->payment,
'invoices' => $invoices,
'invoices_total' => $invoices_total,
'payment_amount' => $this->payment->amount,
'partial_check_amount' => $total,
],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_PAYMENT_RECONCILIATION_FAILURE,
SystemLog::TYPE_LEDGER,
$this->payment->client
);
throw new \Exception("payment amount {$this->payment->amount} does not match invoice totals {$invoices_total} reversing payment");
$this->payment->invoice()->delete();
$this->payment->is_deleted=true;
$this->payment->save();
$this->payment->delete();
}
// $this->payment->invoice()->delete();
// $this->payment->is_deleted=true;
// $this->payment->save();
// $this->payment->delete();
// }
}
return $this->payment;