diff --git a/app/PaymentDrivers/Razorpay/Hosted.php b/app/PaymentDrivers/Razorpay/Hosted.php index 147085e7bb45..736a31c4e762 100644 --- a/app/PaymentDrivers/Razorpay/Hosted.php +++ b/app/PaymentDrivers/Razorpay/Hosted.php @@ -13,12 +13,21 @@ namespace App\PaymentDrivers\Razorpay; +use App\Exceptions\PaymentFailed; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use App\Http\Requests\Request; +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\RazorpayPaymentDriver; +use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Http\RedirectResponse; use Illuminate\View\View; +use Razorpay\Api\Errors\SignatureVerificationError; class Hosted implements MethodInterface { @@ -53,11 +62,123 @@ class Hosted implements MethodInterface return redirect()->route('client.payment_methods.index'); } - public function paymentView(array $data) + /** + * Payment view for the Razorpay. + * + * @param array $data + * @return View + */ + public function paymentView(array $data): View { + $order = $this->razorpay->gateway->order->create([ + 'currency' => $this->razorpay->client->currency()->code, + 'amount' => $this->razorpay->convertToRazorpayAmount((float) $this->razorpay->payment_hash->data->amount_with_fee), + ]); + + $this->razorpay->payment_hash->withData('order_id', $order->id); + $this->razorpay->payment_hash->withData('order_amount', $order->amount); + + $data['gateway'] = $this->razorpay; + + $data['options'] = [ + 'key' => $this->razorpay->company_gateway->getConfigField('apiKey'), + 'amount' => $this->razorpay->convertToRazorpayAmount((float) $this->razorpay->payment_hash->data->amount_with_fee), + 'currency' => $this->razorpay->client->currency()->code, + 'name' => $this->razorpay->company_gateway->company->present()->name(), + 'order_id' => $order->id, + ]; + + return render('gateways.razorpay.hosted.pay', $data); } + /** + * Handle payments page for Razorpay. + * + * @param PaymentResponseRequest $request + * @return void + */ public function paymentResponse(PaymentResponseRequest $request) { + $request->validate([ + 'payment_hash' => ['required'], + 'razorpay_payment_id' => ['required'], + 'razorpay_signature' => ['required'], + ]); + + if (! property_exists($this->razorpay->payment_hash->data, 'order_id')) { + throw new PaymentFailed('Missing [order_id] property. Please contact the administrator. Reference: ' . $this->razorpay->payment_hash->hash); + } + + try { + $attributes = [ + 'razorpay_order_id' => $this->razorpay->payment_hash->data->order_id, + 'razorpay_payment_id' => $request->razorpay_payment_id, + 'razorpay_signature' => $request->razorpay_signature, + ]; + + $this->razorpay->gateway->utility->verifyPaymentSignature($attributes); + + return $this->processSuccessfulPayment($request->razorpay_payment_id); + } + catch (SignatureVerificationError $exception) { + return $this->processUnsuccessfulPayment($exception); + } + } + + /** + * Handle the successful payment for Razorpay. + * + * @param string $payment_id + * @return RedirectResponse + */ + public function processSuccessfulPayment(string $payment_id): RedirectResponse + { + $data = [ + 'gateway_type_id' => GatewayType::HOSTED_PAGE, + 'amount' => array_sum(array_column($this->razorpay->payment_hash->invoices(), 'amount')) + $this->razorpay->payment_hash->fee_total, + 'payment_type' => PaymentType::HOSTED_PAGE, + 'transaction_reference' => $payment_id, + ]; + + $payment_record = $this->razorpay->createPayment($data, Payment::STATUS_COMPLETED); + + SystemLogger::dispatch( + ['response' => $payment_id, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_RAZORPAY, + $this->razorpay->client, + $this->razorpay->client->company, + ); + + return redirect()->route('client.payments.show', ['payment' => $this->razorpay->encodePrimaryKey($payment_record->id)]); + } + + /** + * Handle unsuccessful payment for Razorpay. + * + * @param Exception $exception + * @throws PaymentFailed + * @return void + */ + public function processUnsuccessfulPayment(\Exception $exception): void + { + PaymentFailureMailer::dispatch( + $this->razorpay->client, + $exception->getMessage(), + $this->razorpay->client->company, + $this->razorpay->payment_hash->data->amount_with_fee + ); + + SystemLogger::dispatch( + $exception->getMessage(), + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_RAZORPAY, + $this->razorpay->client, + $this->razorpay->client->company, + ); + + throw new PaymentFailed($exception->getMessage(), $exception->getCode()); } } diff --git a/resources/views/portal/ninja2020/gateways/razorpay/hosted/pay.blade.php b/resources/views/portal/ninja2020/gateways/razorpay/hosted/pay.blade.php new file mode 100644 index 000000000000..2c3e341ecb78 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/razorpay/hosted/pay.blade.php @@ -0,0 +1,55 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.aio_checkout'), 'card_title' => +ctrans('texts.aio_checkout')]) + +@section('gateway_head') + +@endsection + +@section('gateway_content') +
+ @csrf + + + + + + + + + +
+ + + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')]) + {{ ctrans('texts.aio_checkout') }} + @endcomponent + + @include('portal.ninja2020.gateways.includes.payment_details') + + @include('portal.ninja2020.gateways.includes.pay_now') +@endsection + +@section('gateway_footer') + + + +@endsection