From 12466fe3546586049b7068568a00e9c33df1cb82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:21:49 +0200 Subject: [PATCH 01/20] Add HOSTED_PAGE to GatewayType --- app/Models/GatewayType.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index e0b0fbfdcd35..9a8fa92b7cc8 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -28,6 +28,7 @@ class GatewayType extends StaticModel const KBC = 11; const BANCONTACT = 12; const IDEAL = 13; + const HOSTED_PAGE = 14; // For gateways that contain multiple methods. public function gateway() { From 6f136132c42c4e6a19b057a2f0ec55269cc600d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:22:16 +0200 Subject: [PATCH 02/20] Migration --- .../migrations/2021_10_07_141737_razorpay.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 database/migrations/2021_10_07_141737_razorpay.php diff --git a/database/migrations/2021_10_07_141737_razorpay.php b/database/migrations/2021_10_07_141737_razorpay.php new file mode 100644 index 000000000000..7c397489153e --- /dev/null +++ b/database/migrations/2021_10_07_141737_razorpay.php @@ -0,0 +1,28 @@ +name = 'Razorpay'; + $gateway->key = Str::lower(Str::random(32)); + $gateway->provider = 'Razorpay'; + $gateway->is_offsite = false; + $gateway->fields = new \stdClass; + $gateway->visible = true; + $gateway->site_url = 'https://razorpay.com'; + $gateway->default_gateway_type_id = GatewayType::HOSTED_PAGE; + $gateway->save(); + } +} From a08a003f898e4ae678cc8107340a6b1adfd02e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:23:34 +0200 Subject: [PATCH 03/20] Hardcore ID for gateway record --- database/migrations/2021_10_07_141737_razorpay.php | 1 + 1 file changed, 1 insertion(+) diff --git a/database/migrations/2021_10_07_141737_razorpay.php b/database/migrations/2021_10_07_141737_razorpay.php index 7c397489153e..efbbff3cf7dd 100644 --- a/database/migrations/2021_10_07_141737_razorpay.php +++ b/database/migrations/2021_10_07_141737_razorpay.php @@ -15,6 +15,7 @@ class Razorpay extends Migration public function up() { $gateway = new Gateway(); + $gateway->id = 58; $gateway->name = 'Razorpay'; $gateway->key = Str::lower(Str::random(32)); $gateway->provider = 'Razorpay'; From 63ef35dcb924efee52633effe6a503d7d4435268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:24:40 +0200 Subject: [PATCH 04/20] Add Razorpay to Gateway class --- app/Models/Gateway.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index 6b6399dc6e20..5d5a5578b920 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -137,6 +137,11 @@ class Gateway extends StaticModel GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true] // GoCardless ]; break; + case 58: + return [ + GatewayType::HOSTED_PAGE => ['refund' => false, 'token_billing' => false] // Razorpay + ]; + break; default: return []; break; From 647455f86ff74475ee4ac87d587a37a732874f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:33:26 +0200 Subject: [PATCH 05/20] Scaffold Razorpay driver class --- app/PaymentDrivers/RazorpayPaymentDriver.php | 79 ++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 app/PaymentDrivers/RazorpayPaymentDriver.php diff --git a/app/PaymentDrivers/RazorpayPaymentDriver.php b/app/PaymentDrivers/RazorpayPaymentDriver.php new file mode 100644 index 000000000000..4fa2eca1495b --- /dev/null +++ b/app/PaymentDrivers/RazorpayPaymentDriver.php @@ -0,0 +1,79 @@ + Hosted::class, + ]; + + const SYSTEM_LOG_TYPE = SystemLog::TYPE_RAZORPAY; + + public function setPaymentMethod($payment_method_id) + { + $class = self::$methods[$payment_method_id]; + + $this->payment_method = new $class($this); + + return $this; + } + + public function authorizeView(array $data) + { + return $this->payment_method->authorizeView($data); + } + + public function authorizeResponse($request) + { + return $this->payment_method->authorizeResponse($request); + } + + public function processPaymentView(array $data) + { + return $this->payment_method->paymentView($data); + } + + public function processPaymentResponse($request) + { + return $this->payment_method->paymentResponse($request); + } + + public function refund(Payment $payment, $amount, $return_client_response = false) + { + return $this->payment_method->yourRefundImplementationHere(); + } + + public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash) + { + return $this->payment_method->yourTokenBillingImplmentation(); + } +} From 9b388c9c5fc56b23ddd3722c6e57eab9bb9fad04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:33:38 +0200 Subject: [PATCH 06/20] Add TYPE_RAZORPAY SystemLog constant --- app/Models/SystemLog.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php index 316ada10063b..2565c4d003b4 100644 --- a/app/Models/SystemLog.php +++ b/app/Models/SystemLog.php @@ -74,6 +74,7 @@ class SystemLog extends Model const TYPE_EWAY = 313; const TYPE_SQUARE = 320; const TYPE_GOCARDLESS = 321; + const TYPE_RAZORPAY = 322; const TYPE_QUOTA_EXCEEDED = 400; const TYPE_UPSTREAM_FAILURE = 401; From 59e563a1ad7a3900e2a4a65a7c4306dbddc53fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:35:03 +0200 Subject: [PATCH 07/20] Return supported gateway types --- app/PaymentDrivers/RazorpayPaymentDriver.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/PaymentDrivers/RazorpayPaymentDriver.php b/app/PaymentDrivers/RazorpayPaymentDriver.php index 4fa2eca1495b..0d8fb13dda3c 100644 --- a/app/PaymentDrivers/RazorpayPaymentDriver.php +++ b/app/PaymentDrivers/RazorpayPaymentDriver.php @@ -18,7 +18,7 @@ use App\Models\PaymentHash; use App\Models\SystemLog; use App\Utils\Traits\MakesHash; -class DriverTemplate extends BaseDriver +class RazorpayPaymentDriver extends BaseDriver { use MakesHash; @@ -38,12 +38,19 @@ class DriverTemplate extends BaseDriver const SYSTEM_LOG_TYPE = SystemLog::TYPE_RAZORPAY; + public function gatewayTypes(): array + { + return [ + GatewayType::HOSTED_PAGE, + ]; + } + public function setPaymentMethod($payment_method_id) { $class = self::$methods[$payment_method_id]; $this->payment_method = new $class($this); - + return $this; } From 3fa811f679b02388881a6c0241ba3e4f59add87f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:35:58 +0200 Subject: [PATCH 08/20] Scaffold Hosted class --- app/PaymentDrivers/Razorpay/Hosted.php | 29 ++++++++++++++++++++ app/PaymentDrivers/RazorpayPaymentDriver.php | 1 + 2 files changed, 30 insertions(+) create mode 100644 app/PaymentDrivers/Razorpay/Hosted.php diff --git a/app/PaymentDrivers/Razorpay/Hosted.php b/app/PaymentDrivers/Razorpay/Hosted.php new file mode 100644 index 000000000000..659b0f06fbc0 --- /dev/null +++ b/app/PaymentDrivers/Razorpay/Hosted.php @@ -0,0 +1,29 @@ + Date: Thu, 7 Oct 2021 16:37:43 +0200 Subject: [PATCH 09/20] Add translation for hosted pages --- app/Models/GatewayType.php | 2 ++ resources/lang/en/texts.php | 1 + 2 files changed, 3 insertions(+) diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 9a8fa92b7cc8..7a77a56e5142 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -67,6 +67,8 @@ class GatewayType extends StaticModel return ctrans('texts.bancontact'); case self::IDEAL: return ctrans('texts.ideal'); + case self::HOSTED_PAGE: + return ctrans('texts.aio_checkout'); default: return 'Undefined.'; break; diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 2bffc05fda02..a79ec3208054 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -4317,6 +4317,7 @@ $LANG = array( 'kbc_cbc' => 'KBC/CBC', 'bancontact' => 'Bancontact', 'ideal' => 'iDEAL', + 'aio_checkout' => 'All-in-one checkout', ); return $LANG; From 0ae52537f0da7c34494818664c0b44b17cb8eb74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:38:32 +0200 Subject: [PATCH 10/20] Install razorpay/razorpay SDK --- composer.json | 1 + composer.lock | 126 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 9e3eee567db6..f981ecbe61c4 100644 --- a/composer.json +++ b/composer.json @@ -72,6 +72,7 @@ "payfast/payfast-php-sdk": "^1.1", "pragmarx/google2fa": "^8.0", "predis/predis": "^1.1", + "razorpay/razorpay": "2.*", "sentry/sentry-laravel": "^2", "square/square": "13.0.0.20210721", "stripe/stripe-php": "^7.50", diff --git a/composer.lock b/composer.lock index cd3141490d06..1f7570d4f7e8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "96908a391244cbc96eefbb130bd7bed9", + "content-hash": "dc4f3d21b0f54361b6d4b85674fc900e", "packages": [ { "name": "apimatic/jsonmapper", @@ -7789,6 +7789,68 @@ ], "time": "2021-09-25T23:10:38+00:00" }, + { + "name": "razorpay/razorpay", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/razorpay/razorpay-php.git", + "reference": "f562c919d153c343428c9a4e8d4e0848f334aef4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/razorpay/razorpay-php/zipball/f562c919d153c343428c9a4e8d4e0848f334aef4", + "reference": "f562c919d153c343428c9a4e8d4e0848f334aef4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=5.3.0", + "rmccue/requests": "v1.8.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "raveren/kint": "1.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Razorpay\\Api\\": "src/", + "Razorpay\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Abhay Rana", + "email": "nemo@razorpay.com", + "homepage": "https://captnemo.in", + "role": "Developer" + }, + { + "name": "Shashank Kumar", + "email": "shashank@razorpay.com", + "role": "Developer" + } + ], + "description": "Razorpay PHP Client Library", + "homepage": "https://docs.razorpay.com", + "keywords": [ + "api", + "client", + "php", + "razorpay" + ], + "support": { + "email": "contact@razorpay.com", + "issues": "https://github.com/Razorpay/razorpay-php/issues", + "source": "https://github.com/Razorpay/razorpay-php" + }, + "time": "2021-09-16T06:18:12+00:00" + }, { "name": "react/promise", "version": "v2.8.0", @@ -7839,6 +7901,66 @@ }, "time": "2020-05-12T15:16:56+00:00" }, + { + "name": "rmccue/requests", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/WordPress/Requests.git", + "reference": "afbe4790e4def03581c4a0963a1e8aa01f6030f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WordPress/Requests/zipball/afbe4790e4def03581c4a0963a1e8aa01f6030f1", + "reference": "afbe4790e4def03581c4a0963a1e8aa01f6030f1", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7", + "php-parallel-lint/php-console-highlighter": "^0.5.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcompatibility/php-compatibility": "^9.0", + "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5", + "requests/test-server": "dev-master", + "squizlabs/php_codesniffer": "^3.5", + "wp-coding-standards/wpcs": "^2.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Requests": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Ryan McCue", + "homepage": "http://ryanmccue.info" + } + ], + "description": "A HTTP library written in PHP, for human beings.", + "homepage": "http://github.com/WordPress/Requests", + "keywords": [ + "curl", + "fsockopen", + "http", + "idna", + "ipv6", + "iri", + "sockets" + ], + "support": { + "issues": "https://github.com/WordPress/Requests/issues", + "source": "https://github.com/WordPress/Requests/tree/v1.8.0" + }, + "time": "2021-04-27T11:05:25+00:00" + }, { "name": "sabre/uri", "version": "2.2.1", @@ -15962,5 +16084,5 @@ "platform-dev": { "php": "^7.3|^7.4|^8.0" }, - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } From 4c1dd8e03a746d8930897f9bcf99a581d8fef3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:51:08 +0200 Subject: [PATCH 11/20] Add Razorpay to PaymentLibrariesSeeder & update migration --- database/migrations/2021_10_07_141737_razorpay.php | 10 +++++++--- database/seeders/PaymentLibrariesSeeder.php | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/database/migrations/2021_10_07_141737_razorpay.php b/database/migrations/2021_10_07_141737_razorpay.php index efbbff3cf7dd..6dcacd66d63e 100644 --- a/database/migrations/2021_10_07_141737_razorpay.php +++ b/database/migrations/2021_10_07_141737_razorpay.php @@ -3,7 +3,6 @@ use App\Models\Gateway; use App\Models\GatewayType; use Illuminate\Database\Migrations\Migration; -use Illuminate\Support\Str; class Razorpay extends Migration { @@ -17,10 +16,15 @@ class Razorpay extends Migration $gateway = new Gateway(); $gateway->id = 58; $gateway->name = 'Razorpay'; - $gateway->key = Str::lower(Str::random(32)); + $gateway->key = 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9'; $gateway->provider = 'Razorpay'; $gateway->is_offsite = false; - $gateway->fields = new \stdClass; + + $configuration = new \stdClass; + $configuration->apiKey = ''; + $configuration->apiSecret = ''; + + $gateway->fields = \json_encode($configuration); $gateway->visible = true; $gateway->site_url = 'https://razorpay.com'; $gateway->default_gateway_type_id = GatewayType::HOSTED_PAGE; diff --git a/database/seeders/PaymentLibrariesSeeder.php b/database/seeders/PaymentLibrariesSeeder.php index d512c31b7350..ab9431bbf0e5 100644 --- a/database/seeders/PaymentLibrariesSeeder.php +++ b/database/seeders/PaymentLibrariesSeeder.php @@ -81,6 +81,7 @@ class PaymentLibrariesSeeder extends Seeder ['id' => 54, 'name' => 'PAYMILL', 'provider' => 'Paymill', 'key' => 'ca52f618a39367a4c944098ebf977e1c', 'fields' => '{"apiKey":""}'], ['id' => 55, 'name' => 'Custom', 'provider' => 'Custom', 'is_offsite' => true, 'sort_order' => 21, 'key' => '54faab2ab6e3223dbe848b1686490baa', 'fields' => '{"name":"","text":""}'], ['id' => 57, 'name' => 'Square', 'provider' => 'Square', 'is_offsite' => false, 'sort_order' => 21, 'key' => '65faab2ab6e3223dbe848b1686490baz', 'fields' => '{"accessToken":"","applicationId":"","locationId":"","testMode":false}'], + ['id' => 58, 'name' => 'Razorpay', 'provider' => 'Razorpay', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9', 'fields' => '{"apiKey":"","apiSecret":""}'], ]; foreach ($gateways as $gateway) { @@ -97,7 +98,7 @@ class PaymentLibrariesSeeder extends Seeder Gateway::query()->update(['visible' => 0]); - Gateway::whereIn('id', [1,7,11,15,20,39,46,55,50,57,52])->update(['visible' => 1]); + Gateway::whereIn('id', [1,7,11,15,20,39,46,55,50,57,52,58])->update(['visible' => 1]); if (Ninja::isHosted()) { Gateway::whereIn('id', [20])->update(['visible' => 0]); From eaa94bdebef281dbecb5010caf720e7e2196fd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:51:27 +0200 Subject: [PATCH 12/20] Initialize \Razorpay\Api\Api client --- app/PaymentDrivers/RazorpayPaymentDriver.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/PaymentDrivers/RazorpayPaymentDriver.php b/app/PaymentDrivers/RazorpayPaymentDriver.php index 6436a0130551..54f1ee0c2c67 100644 --- a/app/PaymentDrivers/RazorpayPaymentDriver.php +++ b/app/PaymentDrivers/RazorpayPaymentDriver.php @@ -29,7 +29,7 @@ class RazorpayPaymentDriver extends BaseDriver public $can_authorise_credit_card = false; - public $gateway; + public \Razorpay\Api\Api $gateway; public $payment_method; @@ -39,6 +39,16 @@ class RazorpayPaymentDriver extends BaseDriver const SYSTEM_LOG_TYPE = SystemLog::TYPE_RAZORPAY; + public function init(): self + { + $this->gateway = new \Razorpay\Api\Api( + $this->company_gateway->getConfigField('apiKey'), + $this->company_gateway->getConfigField('apiSecret'), + ); + + return $this; + } + public function gatewayTypes(): array { return [ From 2e0ccb6f7f565f797030bf83758de94ea587020e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:52:41 +0200 Subject: [PATCH 13/20] Initialize Razorpay driver --- app/PaymentDrivers/Razorpay/Hosted.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/PaymentDrivers/Razorpay/Hosted.php b/app/PaymentDrivers/Razorpay/Hosted.php index 659b0f06fbc0..28f9be178172 100644 --- a/app/PaymentDrivers/Razorpay/Hosted.php +++ b/app/PaymentDrivers/Razorpay/Hosted.php @@ -16,9 +16,19 @@ namespace App\PaymentDrivers\Razorpay; use App\Http\Requests\Request; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use App\PaymentDrivers\Common\MethodInterface; +use App\PaymentDrivers\RazorpayPaymentDriver; class Hosted implements MethodInterface { + protected RazorpayPaymentDriver $razorpay; + + public function __construct(RazorpayPaymentDriver $razorpay) + { + $this->razorpay = $razorpay; + + $this->razorpay->init(); + } + public function authorizeView(array $data) { } public function authorizeResponse(Request $request) { } From f28d1f2780f32c4ad15980dd88e9be4989c95b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 16:54:56 +0200 Subject: [PATCH 14/20] Authorization --- app/PaymentDrivers/Razorpay/Hosted.php | 34 ++++++++++++++++--- .../razorpay/hosted/authorize.blade.php | 8 +++++ 2 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 resources/views/portal/ninja2020/gateways/razorpay/hosted/authorize.blade.php diff --git a/app/PaymentDrivers/Razorpay/Hosted.php b/app/PaymentDrivers/Razorpay/Hosted.php index 28f9be178172..147085e7bb45 100644 --- a/app/PaymentDrivers/Razorpay/Hosted.php +++ b/app/PaymentDrivers/Razorpay/Hosted.php @@ -13,10 +13,12 @@ namespace App\PaymentDrivers\Razorpay; -use App\Http\Requests\Request; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; +use App\Http\Requests\Request; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\RazorpayPaymentDriver; +use Illuminate\Http\RedirectResponse; +use Illuminate\View\View; class Hosted implements MethodInterface { @@ -29,11 +31,33 @@ class Hosted implements MethodInterface $this->razorpay->init(); } - public function authorizeView(array $data) { } + /** + * Show the authorization page for Razorpay. + * + * @param array $data + * @return View + */ + public function authorizeView(array $data): View + { + return render('gateways.razorpay.hosted.authorize', $data); + } - public function authorizeResponse(Request $request) { } + /** + * Handle the authorization page for Razorpay. + * + * @param Request $request + * @return RedirectResponse + */ + public function authorizeResponse(Request $request): RedirectResponse + { + return redirect()->route('client.payment_methods.index'); + } - public function paymentView(array $data) { } + public function paymentView(array $data) + { + } - public function paymentResponse(PaymentResponseRequest $request) { } + public function paymentResponse(PaymentResponseRequest $request) + { + } } diff --git a/resources/views/portal/ninja2020/gateways/razorpay/hosted/authorize.blade.php b/resources/views/portal/ninja2020/gateways/razorpay/hosted/authorize.blade.php new file mode 100644 index 000000000000..4f78e4bb4fd4 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/razorpay/hosted/authorize.blade.php @@ -0,0 +1,8 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.aio_checkout'), 'card_title' => +ctrans('texts.aio_checkout')]) + +@section('gateway_content') + @component('portal.ninja2020.components.general.card-element-single') + {{ __('texts.payment_method_cannot_be_preauthorized') }} + @endcomponent +@endsection \ No newline at end of file From 87c6241947f0782de40c9002755b301de978390d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 18:01:14 +0200 Subject: [PATCH 15/20] Add new payment type --- app/Models/PaymentType.php | 1 + ...55410_add_hosted_page_to_payment_types.php | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 database/migrations/2021_10_07_155410_add_hosted_page_to_payment_types.php diff --git a/app/Models/PaymentType.php b/app/Models/PaymentType.php index ff396181578e..faaeb9052f0a 100644 --- a/app/Models/PaymentType.php +++ b/app/Models/PaymentType.php @@ -46,6 +46,7 @@ class PaymentType extends StaticModel const KBC = 35; const BANCONTACT = 36; const IDEAL = 37; + const HOSTED_PAGE = 38; public static function parseCardType($cardName) { diff --git a/database/migrations/2021_10_07_155410_add_hosted_page_to_payment_types.php b/database/migrations/2021_10_07_155410_add_hosted_page_to_payment_types.php new file mode 100644 index 000000000000..8aefe97af473 --- /dev/null +++ b/database/migrations/2021_10_07_155410_add_hosted_page_to_payment_types.php @@ -0,0 +1,24 @@ +id = 35; + $type->name = 'Hosted Page'; + $type->gateway_type_id = GatewayType::HOSTED_PAGE; + + $type->save(); + } +} From ea823182189d3d6a041bb3455239a94935b991ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 18:01:25 +0200 Subject: [PATCH 16/20] Method to convert to Razorpay specific-amount --- app/PaymentDrivers/RazorpayPaymentDriver.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/PaymentDrivers/RazorpayPaymentDriver.php b/app/PaymentDrivers/RazorpayPaymentDriver.php index 54f1ee0c2c67..73e0827a1ed4 100644 --- a/app/PaymentDrivers/RazorpayPaymentDriver.php +++ b/app/PaymentDrivers/RazorpayPaymentDriver.php @@ -94,4 +94,15 @@ class RazorpayPaymentDriver extends BaseDriver { return $this->payment_method->yourTokenBillingImplmentation(); } + + /** + * Convert the amount to the format that Razorpay supports. + * + * @param mixed|float $amount + * @return int + */ + public function convertToRazorpayAmount($amount): int + { + return \number_format((float) $amount * 100, 0, '.', ''); + } } From 60594f18024bb3fe5683eb0d27072875f9884322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 18:01:36 +0200 Subject: [PATCH 17/20] Payments --- app/PaymentDrivers/Razorpay/Hosted.php | 123 +++++++++++++++++- .../gateways/razorpay/hosted/pay.blade.php | 55 ++++++++ 2 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 resources/views/portal/ninja2020/gateways/razorpay/hosted/pay.blade.php 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 From 2cfdb56c136b9dacb925a913905ce4ac7933c445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 18:04:07 +0200 Subject: [PATCH 18/20] Extract scripts into separate file --- resources/js/clients/payments/razorpay-aio.js | 29 +++++++++++++++++++ .../gateways/razorpay/hosted/pay.blade.php | 21 +------------- 2 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 resources/js/clients/payments/razorpay-aio.js diff --git a/resources/js/clients/payments/razorpay-aio.js b/resources/js/clients/payments/razorpay-aio.js new file mode 100644 index 000000000000..2ae159a3e7f6 --- /dev/null +++ b/resources/js/clients/payments/razorpay-aio.js @@ -0,0 +1,29 @@ +/** + * 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://opensource.org/licenses/AAL + */ + +let options = JSON.parse( + document.querySelector('meta[name=razorpay-options]')?.content +); + +options.handler = function(response) { + document.getElementById('razorpay_payment_id').value = + response.razorpay_payment_id; + document.getElementById('razorpay_signature').value = + response.razorpay_signature; + document.getElementById('server-response').submit(); +}; + +let razorpay = new Razorpay(options); + +document.getElementById('pay-now').onclick = function(event) { + event.target.parentElement.disabled = true; + + razorpay.open(); +}; diff --git a/resources/views/portal/ninja2020/gateways/razorpay/hosted/pay.blade.php b/resources/views/portal/ninja2020/gateways/razorpay/hosted/pay.blade.php index 2c3e341ecb78..43a76890f809 100644 --- a/resources/views/portal/ninja2020/gateways/razorpay/hosted/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/razorpay/hosted/pay.blade.php @@ -32,24 +32,5 @@ ctrans('texts.aio_checkout')]) @section('gateway_footer') - - + @endsection From 167c42e5b15a62db30e00c508aa2fbe2f5f7cf16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 18:04:14 +0200 Subject: [PATCH 19/20] Assets production build --- public/js/clients/payments/razorpay-aio.js | 2 ++ public/js/clients/payments/razorpay-aio.js.LICENSE.txt | 9 +++++++++ public/mix-manifest.json | 1 + webpack.mix.js | 4 ++++ 4 files changed, 16 insertions(+) create mode 100644 public/js/clients/payments/razorpay-aio.js create mode 100644 public/js/clients/payments/razorpay-aio.js.LICENSE.txt diff --git a/public/js/clients/payments/razorpay-aio.js b/public/js/clients/payments/razorpay-aio.js new file mode 100644 index 000000000000..dbf83802dd09 --- /dev/null +++ b/public/js/clients/payments/razorpay-aio.js @@ -0,0 +1,2 @@ +/*! For license information please see razorpay-aio.js.LICENSE.txt */ +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=27)}({27:function(e,t,n){e.exports=n("tIvh")},tIvh:function(e,t){var n,r=JSON.parse(null===(n=document.querySelector("meta[name=razorpay-options]"))||void 0===n?void 0:n.content);r.handler=function(e){document.getElementById("razorpay_payment_id").value=e.razorpay_payment_id,document.getElementById("razorpay_signature").value=e.razorpay_signature,document.getElementById("server-response").submit()};var o=new Razorpay(r);document.getElementById("pay-now").onclick=function(e){e.target.parentElement.disabled=!0,o.open()}}}); \ No newline at end of file diff --git a/public/js/clients/payments/razorpay-aio.js.LICENSE.txt b/public/js/clients/payments/razorpay-aio.js.LICENSE.txt new file mode 100644 index 000000000000..c357ff50e6ab --- /dev/null +++ b/public/js/clients/payments/razorpay-aio.js.LICENSE.txt @@ -0,0 +1,9 @@ +/** + * 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://opensource.org/licenses/AAL + */ diff --git a/public/mix-manifest.json b/public/mix-manifest.json index e52f5e227431..d6a844a098c8 100755 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -15,6 +15,7 @@ "/js/clients/payments/eway-credit-card.js": "/js/clients/payments/eway-credit-card.js?id=08ea84e9451abd434cff", "/js/clients/payments/mollie-credit-card.js": "/js/clients/payments/mollie-credit-card.js?id=73b66e88e2daabcd6549", "/js/clients/payments/paytrace-credit-card.js": "/js/clients/payments/paytrace-credit-card.js?id=c2b5f7831e1a46dd5fb2", + "/js/clients/payments/razorpay-aio.js": "/js/clients/payments/razorpay-aio.js?id=817ab3b2b94ee37b14eb", "/js/clients/payments/square-credit-card.js": "/js/clients/payments/square-credit-card.js?id=070c86b293b532c5a56c", "/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=81c2623fc1e5769b51c7", "/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=665ddf663500767f1a17", diff --git a/webpack.mix.js b/webpack.mix.js index 01fcbf04df88..906679251a59 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -106,6 +106,10 @@ mix.js("resources/js/app.js", "public/js") "resources/js/clients/statements/view.js", "public/js/clients/statements/view.js", ) + .js( + "resources/js/clients/payments/razorpay-aio.js", + "public/js/clients/payments/razorpay-aio.js" + ) mix.copyDirectory('node_modules/card-js/card-js.min.css', 'public/css/card-js.min.css'); From e592b56e0372e84ca3fbde32ec099d66ba65b18c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 7 Oct 2021 21:16:21 +0200 Subject: [PATCH 20/20] Remove token billing / refund placeholders --- app/PaymentDrivers/RazorpayPaymentDriver.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/app/PaymentDrivers/RazorpayPaymentDriver.php b/app/PaymentDrivers/RazorpayPaymentDriver.php index 73e0827a1ed4..927ca27805d4 100644 --- a/app/PaymentDrivers/RazorpayPaymentDriver.php +++ b/app/PaymentDrivers/RazorpayPaymentDriver.php @@ -85,15 +85,9 @@ class RazorpayPaymentDriver extends BaseDriver return $this->payment_method->paymentResponse($request); } - public function refund(Payment $payment, $amount, $return_client_response = false) - { - return $this->payment_method->yourRefundImplementationHere(); - } + public function refund(Payment $payment, $amount, $return_client_response = false) {} - public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash) - { - return $this->payment_method->yourTokenBillingImplmentation(); - } + public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash) {} /** * Convert the amount to the format that Razorpay supports.