1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-05 18:52:44 +01:00

Payment refunds (#3687)

* Fix whereClientId when starting payment

* Refunding using Paypal

* Refunding engine

* Cleanup and making refund method work

* Remove "refund" method from BasePaymentController

* Add "refund" to PaypalExpressPaymentDriver

* Extract refunding into own classes

* Apply php-cs-fixer to PaypalExpress

* Refunding with stripe
This commit is contained in:
Benjamin Beganović 2020-05-14 03:04:23 +02:00 committed by GitHub
parent 14577fdfd0
commit a613cfed7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 146 additions and 30 deletions

View File

@ -0,0 +1,31 @@
<?php
namespace App\Exceptions;
use Exception;
class PaymentRefundFailed extends Exception
{
/**
* Report the exception.
*
* @return void
*/
public function report()
{
//
}
/**
* Render the exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
return response()->json([
'message' => 'Unable to refund the transaction'
], 401);
}
}

View File

@ -72,7 +72,7 @@ class PaymentController extends Controller
public function process()
{
$invoices = Invoice::whereIn('id', $this->transformKeys(request()->invoices))
->whereClientId(auth('contact')->user()->company()->id)
->where('company_id', auth('contact')->user()->company->id)
->get();
$amount = $invoices->sum('balance');

View File

@ -1,4 +1,5 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
@ -20,7 +21,6 @@ use App\Models\Payment;
use App\Models\PaymentType;
use App\Models\SystemLog;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Omnipay\Common\Item;
/**
@ -61,7 +61,7 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
{
use MakesHash;
protected $refundable = false;
protected $refundable = true;
protected $token_billing = false;
@ -107,9 +107,9 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
SystemLogger::dispatch(
[
'server_response' => $response->getData(),
'data' => $data
],
'server_response' => $response->getData(),
'data' => $data
],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_PAYPAL,
@ -131,20 +131,20 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
} elseif ($response->isSuccessful()) {
SystemLogger::dispatch(
[
'server_response' => $response->getData(),
'data' => $request->all()
],
'server_response' => $response->getData(),
'data' => $request->all()
],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_PAYPAL,
$this->client
);
} elseif (! $response->isSuccessful()) {
} elseif (!$response->isSuccessful()) {
SystemLogger::dispatch(
[
'data' => $request->all(),
'server_response' => $response->getData()
],
'data' => $request->all(),
'server_response' => $response->getData()
],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_PAYPAL,
@ -159,13 +159,13 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
$this->attachInvoices($payment, $request->input('hashed_ids'));
$payment->service()->UpdateInvoicePayment();
event(new PaymentWasCreated($payment, $payment->company));
return redirect()->route('client.payments.show', ['payment'=>$this->encodePrimaryKey($payment->id)]);
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
}
protected function paymentDetails($input) :array
protected function paymentDetails($input): array
{
$data = parent::paymentDetails($input);
@ -182,41 +182,41 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
return $data;
}
private function buildReturnUrl($input) : string
private function buildReturnUrl($input): string
{
$url = $this->client->company->domain() . "/client/payments/process/response";
$url .= "?company_gateway_id={$this->company_gateway->id}&gateway_type_id=".GatewayType::PAYPAL;
$url .= "?company_gateway_id={$this->company_gateway->id}&gateway_type_id=" . GatewayType::PAYPAL;
$url .= "&hashed_ids=" . implode(",", $input['hashed_ids']);
$url .= "&amount=".$input['amount'];
$url .= "&fee=".$input['fee'];
$url .= "&amount=" . $input['amount'];
$url .= "&fee=" . $input['fee'];
return $url;
}
private function buildCancelUrl($input) : string
private function buildCancelUrl($input): string
{
$url = $this->client->company->domain() . '/client/invoices';
return $url;
}
private function buildDescription($input) : string
private function buildDescription($input): string
{
$invoice_numbers = "";
foreach ($input['invoices'] as $invoice) {
$invoice_numbers .= $invoice->number." ";
$invoice_numbers .= $invoice->number . " ";
}
return ctrans('texts.invoice_number'). ": {$invoice_numbers}";
return ctrans('texts.invoice_number') . ": {$invoice_numbers}";
}
private function buildTransactionId($input) : string
private function buildTransactionId($input): string
{
return implode(",", $input['hashed_ids']);
}
private function paymentItems($input) : array
private function paymentItems($input): array
{
$items = [];
$total = 0;
@ -255,7 +255,7 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
return $items;
}
public function createPayment($data) : Payment
public function createPayment($data): Payment
{
$payment = parent::createPayment($data);
@ -270,4 +270,41 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
return $payment;
}
public function refund(Payment $payment, $amount = null)
{
$this->gateway();
$response = $this->gateway
->refund(['transactionReference' => $payment->transaction_reference, 'amount' => $amount ?? $payment->amount])
->send();
if ($response->isSuccessful()) {
SystemLogger::dispatch(
[
'server_response' => $response->getMessage(),
'data' => request()->all(),
],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_PAYPAL,
$this->client
);
return true;
}
SystemLogger::dispatch(
[
'server_response' => $response->getMessage(),
'data' => request()->all(),
],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_PAYPAL,
$this->client
);
return false;
}
}

View File

@ -471,6 +471,42 @@ class StripePaymentDriver extends BasePaymentDriver
return $customer;
}
public function refund(Payment $payment, $amount = null)
{
$this->gateway();
$response = $this->gateway
->refund(['transactionReference' => $payment->transaction_reference, 'amount' => $amount ?? $payment->amount])
->send();
if ($response->isSuccessful()) {
SystemLogger::dispatch(
[
'server_response' => $response->getMessage(),
'data' => request()->all(),
],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_PAYPAL,
$this->client
);
return true;
}
SystemLogger::dispatch(
[
'server_response' => $response->getMessage(),
'data' => request()->all(),
],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_PAYPAL,
$this->client
);
return false;
}
/************************************** Omnipay API methods **********************************************************/
}

View File

@ -11,9 +11,11 @@
namespace App\Utils\Traits\Payment;
use App\Exceptions\PaymentRefundFailed;
use App\Factory\CreditFactory;
use App\Factory\InvoiceItemFactory;
use App\Models\Activity;
use App\Models\CompanyGateway;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
@ -163,11 +165,18 @@ trait Refundable
$credit_note->number = $this->client->getNextCreditNumber($this->client);
$credit_note->save();
//determine if we need to refund via gateway
if ($data['gateway_refund'] !== false) {
//todo process gateway refund, on success, reduce the credit note balance to 0
}
$gateway = CompanyGateway::find($this->company_gateway_id);
if ($gateway) {
$amount = request()->has('amount') ? request()->amount : null;
$response = $gateway->driver($this->client)->refund($this, $amount);
if (!$response) {
throw new PaymentRefundFailed();
}
}
}
if ($total_refund > 0) {
$this->refunded += $total_refund;

View File

@ -78,6 +78,7 @@
href="{{ route('client.invoice.show', ['invoice' => $invoice->hashed_id])}}">
{{ $invoice->number }}
</a>
<span>Payment: {{ $payment->hashed_id }} Invoice: {{ $invoice->hashed_id }} Amount: {{ $payment->amount }}</span>
</div>
</div>
@endforeach

View File

@ -3,6 +3,8 @@
* Signup Routes
*/
use Omnipay\Omnipay;
Route::get('/', 'BaseController@flutterRoute')->middleware('guest');
Route::get('setup', 'SetupController@index')->middleware('guest');
Route::post('setup/check_db', 'SetupController@checkDB')->middleware('guest');