mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Merge pull request #6953 from beganovich/gocardless-instant-bank-pay
GoCardless: Instant Bank Pay
This commit is contained in:
commit
019e3c0068
29
app/Http/Controllers/Gateways/GoCardlessController.php
Normal file
29
app/Http/Controllers/Gateways/GoCardlessController.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Gateways;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Gateways\GoCardless\IbpRequest;
|
||||
use App\Models\GatewayType;
|
||||
|
||||
class GoCardlessController extends Controller
|
||||
{
|
||||
public function ibpRedirect(IbpRequest $request)
|
||||
{
|
||||
return $request
|
||||
->getCompanyGateway()
|
||||
->driver($request->getClient())
|
||||
->setPaymentMethod(GatewayType::INSTANT_BANK_PAY)
|
||||
->processPaymentResponse($request);
|
||||
}
|
||||
}
|
67
app/Http/Requests/Gateways/GoCardless/IbpRequest.php
Normal file
67
app/Http/Requests/Gateways/GoCardless/IbpRequest.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Gateways\GoCardless;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class IbpRequest extends FormRequest
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public function getCompany(): ?Company
|
||||
{
|
||||
return Company::where('company_key', $this->company_key)->first();
|
||||
}
|
||||
|
||||
public function getCompanyGateway(): ?CompanyGateway
|
||||
{
|
||||
return CompanyGateway::find($this->decodePrimaryKey($this->company_gateway_id));
|
||||
}
|
||||
|
||||
public function getPaymentHash(): ?PaymentHash
|
||||
{
|
||||
return PaymentHash::where('hash', $this->hash)->firstOrFail();
|
||||
}
|
||||
|
||||
public function getClient(): ?Client
|
||||
{
|
||||
return Client::find($this->getPaymentHash()->data->client_id);
|
||||
}
|
||||
}
|
@ -159,7 +159,8 @@ class Gateway extends StaticModel
|
||||
return [
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']], // GoCardless
|
||||
GatewayType::DIRECT_DEBIT => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']],
|
||||
GatewayType::SEPA => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']]
|
||||
GatewayType::SEPA => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']],
|
||||
GatewayType::INSTANT_BANK_PAY => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']],
|
||||
];
|
||||
break;
|
||||
case 58:
|
||||
|
@ -37,6 +37,7 @@ class GatewayType extends StaticModel
|
||||
const DIRECT_DEBIT = 18;
|
||||
const ACSS = 19;
|
||||
const BECS = 20;
|
||||
const INSTANT_BANK_PAY = 21;
|
||||
|
||||
public function gateway()
|
||||
{
|
||||
@ -89,6 +90,8 @@ class GatewayType extends StaticModel
|
||||
return ctrans('texts.acss');
|
||||
case self::DIRECT_DEBIT:
|
||||
return ctrans('texts.payment_type_direct_debit');
|
||||
case self::INSTANT_BANK_PAY:
|
||||
return ctrans('texts.payment_type_instant_bank_pay');
|
||||
default:
|
||||
return 'Undefined.';
|
||||
break;
|
||||
|
@ -53,6 +53,7 @@ class PaymentType extends StaticModel
|
||||
const DIRECT_DEBIT = 42;
|
||||
const BECS = 43;
|
||||
const ACSS = 44;
|
||||
const INSTANT_BANK_PAY = 45;
|
||||
|
||||
public static function parseCardType($cardName)
|
||||
{
|
||||
|
182
app/PaymentDrivers/GoCardless/InstantBankPay.php
Normal file
182
app/PaymentDrivers/GoCardless/InstantBankPay.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace App\PaymentDrivers\GoCardless;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
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\Common\MethodInterface;
|
||||
use App\PaymentDrivers\GoCardlessPaymentDriver;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class InstantBankPay implements MethodInterface
|
||||
{
|
||||
protected GoCardlessPaymentDriver $go_cardless;
|
||||
|
||||
public function __construct(GoCardlessPaymentDriver $go_cardless)
|
||||
{
|
||||
$this->go_cardless = $go_cardless;
|
||||
|
||||
$this->go_cardless->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorization page for Instant Bank Pay.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RedirectResponse
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function authorizeView(array $data): RedirectResponse
|
||||
{
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle authorization for Instant Bank Pay.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RedirectResponse
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function authorizeResponse(Request $request): RedirectResponse
|
||||
{
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
try {
|
||||
$billing_request = $this->go_cardless->gateway->billingRequests()->create([
|
||||
'params' => [
|
||||
'payment_request' => [
|
||||
'description' => ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number'),
|
||||
'amount' => (string) $data['amount_with_fee'] * 100,
|
||||
'currency' => $this->go_cardless->client->getCurrencyCode(),
|
||||
],
|
||||
]
|
||||
]);
|
||||
|
||||
$billing_request_flow = $this->go_cardless->gateway->billingRequestFlows()->create([
|
||||
'params' => [
|
||||
'redirect_uri' => route('gocardless.ibp_redirect', [
|
||||
'company_key' => $this->go_cardless->company_gateway->company->company_key,
|
||||
'company_gateway_id' => $this->go_cardless->company_gateway->hashed_id,
|
||||
'hash' => $this->go_cardless->payment_hash->hash,
|
||||
]),
|
||||
'links' => [
|
||||
'billing_request' => $billing_request->id,
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$this->go_cardless->payment_hash
|
||||
->withData('client_id', $this->go_cardless->client->id)
|
||||
->withData('billing_request', $billing_request->id)
|
||||
->withData('billing_request_flow', $billing_request_flow->id);
|
||||
|
||||
return redirect(
|
||||
$billing_request_flow->authorisation_url
|
||||
);
|
||||
} catch (\Exception $exception) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
public function paymentResponse($request)
|
||||
{
|
||||
$this->go_cardless->setPaymentHash(
|
||||
$request->getPaymentHash()
|
||||
);
|
||||
|
||||
try {
|
||||
$billing_request = $this->go_cardless->gateway->billingRequests()->get(
|
||||
$this->go_cardless->payment_hash->data->billing_request
|
||||
);
|
||||
|
||||
$payment = $this->go_cardless->gateway->payments()->get(
|
||||
$billing_request->payment_request->links->payment
|
||||
);
|
||||
|
||||
if ($billing_request->status === 'fulfilled') {
|
||||
return $this->processSuccessfulPayment($payment);
|
||||
}
|
||||
|
||||
return $this->processUnsuccessfulPayment($payment);
|
||||
} catch (\Exception $exception) {
|
||||
throw new PaymentFailed(
|
||||
$exception->getMessage(),
|
||||
$exception->getCode()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle pending payments for Instant Bank Transfer.
|
||||
*
|
||||
* @param ResourcesPayment $payment
|
||||
* @param array $data
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function processSuccessfulPayment(\GoCardlessPro\Resources\Payment $payment, array $data = [])
|
||||
{
|
||||
$data = [
|
||||
'payment_method' => $payment->links->mandate,
|
||||
'payment_type' => PaymentType::INSTANT_BANK_PAY,
|
||||
'amount' => $this->go_cardless->payment_hash->data->amount_with_fee,
|
||||
'transaction_reference' => $payment->id,
|
||||
'gateway_type_id' => GatewayType::INSTANT_BANK_PAY,
|
||||
];
|
||||
|
||||
$payment = $this->go_cardless->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $payment, 'data' => $data],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_GOCARDLESS,
|
||||
$this->go_cardless->client,
|
||||
$this->go_cardless->client->company,
|
||||
);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->go_cardless->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process unsuccessful payments for Direct Debit.
|
||||
*
|
||||
* @param ResourcesPayment $payment
|
||||
* @return never
|
||||
*/
|
||||
public function processUnsuccessfulPayment(\GoCardlessPro\Resources\Payment $payment)
|
||||
{
|
||||
PaymentFailureMailer::dispatch($this->go_cardless->client, $payment->status, $this->go_cardless->client->company, $this->go_cardless->payment_hash->data->amount_with_fee);
|
||||
|
||||
PaymentFailureMailer::dispatch(
|
||||
$this->go_cardless->client,
|
||||
$payment,
|
||||
$this->go_cardless->client->company,
|
||||
$payment->amount
|
||||
);
|
||||
|
||||
$message = [
|
||||
'server_response' => $payment,
|
||||
'data' => $this->go_cardless->payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_GOCARDLESS,
|
||||
$this->go_cardless->client,
|
||||
$this->go_cardless->client->company,
|
||||
);
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ class GoCardlessPaymentDriver extends BaseDriver
|
||||
GatewayType::BANK_TRANSFER => \App\PaymentDrivers\GoCardless\ACH::class,
|
||||
GatewayType::DIRECT_DEBIT => \App\PaymentDrivers\GoCardless\DirectDebit::class,
|
||||
GatewayType::SEPA => \App\PaymentDrivers\GoCardless\SEPA::class,
|
||||
GatewayType::INSTANT_BANK_PAY => \App\PaymentDrivers\GoCardless\InstantBankPay::class,
|
||||
];
|
||||
|
||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_GOCARDLESS;
|
||||
@ -77,6 +78,10 @@ class GoCardlessPaymentDriver extends BaseDriver
|
||||
$types[] = GatewayType::SEPA;
|
||||
}
|
||||
|
||||
if ($this->client->currency()->code === 'GBP') {
|
||||
$types[] = GatewayType::INSTANT_BANK_PAY;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentType;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddInstantBankTransfer extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
$type = new PaymentType();
|
||||
|
||||
$type->id = PaymentType::INSTANT_BANK_PAY;
|
||||
$type->name = 'Instant Bank Pay';
|
||||
$type->gateway_type_id = GatewayType::INSTANT_BANK_PAY;
|
||||
|
||||
$type->save();
|
||||
}
|
||||
}
|
@ -4339,6 +4339,7 @@ $LANG = array(
|
||||
'browser_pay' => 'Google Pay, Apple Pay, Microsoft Pay',
|
||||
'no_available_methods' => 'We can\'t find any credit cards on your device. <a href="https://invoiceninja.github.io/docs/payments#apple-pay-google-pay-microsoft-pay" target="_blank" class="underline">Read more about this.</a>',
|
||||
'gocardless_mandate_not_ready' => 'Payment mandate is not ready. Please try again later.',
|
||||
'payment_type_instant_bank_pay' => 'Instant Bank Pay',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -43,3 +43,4 @@ Route::get('stripe/completed', 'StripeConnectController@completed')->name('strip
|
||||
|
||||
Route::get('checkout/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', 'Gateways\Checkout3dsController@index')->name('checkout.3ds_redirect');
|
||||
Route::get('mollie/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', 'Gateways\Mollie3dsController@index')->name('mollie.3ds_redirect');
|
||||
Route::get('gocardless/ibp_redirect/{company_key}/{company_gateway_id}/{hash}', 'Gateways\GoCardlessController@ibpRedirect')->name('gocardless.ibp_redirect');
|
||||
|
Loading…
x
Reference in New Issue
Block a user