Paypal Standard Payment Gateways App

This commit is contained in:
cuneytsenturk 2017-11-20 17:28:15 +03:00
parent 10024b92f8
commit ed392c74a6
34 changed files with 598 additions and 3 deletions

View File

@ -0,0 +1,36 @@
<?php
namespace App\Listeners\Updates;
use App\Events\UpdateFinished;
use App\Models\Company\Company;
use Artisan;
class Version109 extends Listener
{
const ALIAS = 'core';
const VERSION = '1.0.9';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(UpdateFinished $event)
{
// Check if should listen
if (!$this->check($event)) {
return;
}
// Create new bill statuses
$companies = Company::all();
foreach ($companies as $company) {
Artisan::call('module:install', ['alias' => 'offlinepayment', 'company_id' => $company->id]);
Artisan::call('module:install', ['alias' => 'paypalstandard', 'company_id' => $company->id]);
}
}
}

View File

@ -17,6 +17,7 @@ class EventServiceProvider extends ServiceProvider
'App\Listeners\Updates\Version106',
'App\Listeners\Updates\Version107',
'App\Listeners\Updates\Version108',
'App\Listeners\Updates\Version109',
],
'Illuminate\Auth\Events\Login' => [
'App\Listeners\Auth\Login',

View File

@ -16,6 +16,7 @@ class CompanySeeder extends Seeder
$this->call(Database\Seeds\Categories::class);
$this->call(Database\Seeds\Currencies::class);
$this->call(Database\Seeds\InvoiceStatuses::class);
$this->call(Database\Seeds\Modules::class);
$this->call(Database\Seeds\Settings::class);
$this->call(Database\Seeds\Taxes::class);
}

View File

@ -0,0 +1,33 @@
<?php
namespace Database\Seeds;
use App\Models\Model;
use Artisan;
use Illuminate\Database\Seeder;
class Modules extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
$this->create();
Model::reguard();
}
private function create()
{
$company_id = $this->command->argument('company');
Artisan::call('module:install', ['alias' => 'offlinepayment', 'company_id' => $company_id]);
Artisan::call('module:install', ['alias' => 'paypalstandard', 'company_id' => $company_id]);
}
}

View File

View File

View File

@ -0,0 +1,7 @@
<?php
return [
'name' => 'PaypalStandard',
];

View File

View File

@ -0,0 +1,21 @@
<?php
namespace Modules\PaypalStandard\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class PaypalStandardDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
// $this->call("OthersTableSeeder");
}
}

View File

View File

View File

@ -0,0 +1,23 @@
<?php
namespace Modules\PaypalStandard\Events\Handlers;
use App\Events\PaymentGatewayListing;
class PaypalStandardGateway
{
/**
* Handle the event.
*
* @param PaymentGatewayListing $event
* @return void
*/
public function handle(PaymentGatewayListing $event)
{
$setting = setting('paypalstandard');
$setting['code'] = 'paypalstandard';
return [$setting];
}
}

View File

@ -0,0 +1,164 @@
<?php
namespace Modules\PaypalStandard\Http\Controllers;
use App\Events\InvoicePaid;
use Illuminate\Http\Response;
use Illuminate\Routing\Controller;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
use App\Http\Requests\Customer\InvoicePayment as PaymentRequest;
use App\Models\Income\Invoice;
class PaypalStandard extends Controller
{
/**
* Show the form for editing the specified resource.
* @param Invoice
* @param PaymentRequest
* @return Response
*/
public function show(Invoice $invoice, PaymentRequest $request)
{
$gateway = setting('paypalstandard');
$gateway['action'] = 'https://www.paypal.com/cgi-bin/webscr';
if ($gateway['mode'] == 'sandbox') {
$gateway['action'] = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
}
$customer = explode(" ", $invoice->customer_name);
$last_name = array_pop($customer);
$first_name = implode(" ", $customer);
$invoice->first_name = $first_name;
$invoice->last_name = $last_name;
$gateway['language'] = \App::getLocale();
$html = view('paypalstandard::show', compact('gateway', 'invoice'))->render();
return response()->json([
'code' => 'paypalstandard',
'name' => $gateway['name'],
'description' => trans('paypalstandard::paypalstandard.description'),
'redirect' => false,
'html' => $html,
]);
}
public function result(Invoice $invoice, Request $request)
{
$success = true;
switch ($request['payment_status']) {
case 'Completed':
$message = trans('messages.success.added', ['type' => trans_choice('general.customers', 1)]);
break;
case 'Canceled_Reversal':
case 'Denied':
case 'Expired':
case 'Failed':
case 'Pending':
case 'Processed':
case 'Refunded':
case 'Reversed':
case 'Voided':
$message = trans('messages.error.added', ['type' => trans_choice('general.customers', 1)]);
$success = false;
break;
}
if ($success) {
flash($message)->success();
} else {
flash($message)->warning();
}
$redirect = url('customers/invoices/' . $invoice->id);
return redirect($redirect);
}
public function callback(Invoice $invoice, Request $request)
{
$gateway = setting('paypalstandard');
$paypal_log = new Logger('Paypal');
$paypal_log->pushHandler(new StreamHandler(storage_path('logs/paypal.log')), Logger::INFO);
if ($invoice) {
$url = 'https://ipnpb.paypal.com/cgi-bin/webscr';
if ($gateway['mode'] == 'sandbox') {
$url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
}
$client = new Client(['verify' => false]);
$paypal_request['cmd'] = '_notify-validate';
foreach ($request->toArray() as $key => $value) {
$paypal_request[$key] = urlencode(html_entity_decode($value, ENT_QUOTES, 'UTF-8'));
}
$result = $client->post($url, $paypal_request);
if ($result->getStatusCode() != 200) {
$paypal_log->info('PAYPAL_STANDARD :: CURL failed ', $result->getBody()->getContents());
} else {
$result = $result->getBody()->getContents();
}
if ($gateway['debug']) {
$paypal_log->info('PAYPAL_STANDARD :: IPN REQUEST: ', $request->toArray());
//$paypal_log->info('PAYPAL_STANDARD :: IPN RESULT: ', $result);
}
if ((strcmp($result, 'VERIFIED') == 0 || strcmp($result, 'UNVERIFIED') == 0) || true) {
switch ($request['payment_status']) {
case 'Completed':
$receiver_match = (strtolower($request['receiver_email']) == strtolower($gateway['email']));
$total_paid_match = ((float)$request['mc_gross'] == $invoice->amount);
if ($receiver_match && $total_paid_match) {
event(new InvoicePaid($invoice, $request->toArray()));
}
if (!$receiver_match) {
$paypal_log->info('PAYPAL_STANDARD :: RECEIVER EMAIL MISMATCH! ' . strtolower($request['receiver_email']));
}
if (!$total_paid_match) {
$paypal_log->info('PAYPAL_STANDARD :: TOTAL PAID MISMATCH! ' . $request['mc_gross']);
}
break;
case 'Canceled_Reversal':
case 'Denied':
case 'Expired':
case 'Failed':
case 'Pending':
case 'Processed':
case 'Refunded':
case 'Reversed':
case 'Voided':
$paypal_log->info('PAYPAL_STANDARD :: NOT COMPLETED ' . $request->toArray());
break;
}
} else {
$paypal_log->info('PAYPAL_STANDARD :: VERIFIED != 0 || UNVERIFIED != 0 ' . $request->toArray());
}
}
}
}

View File

@ -0,0 +1,10 @@
<?php
Route::group(['middleware' => ['web', 'auth', 'language', 'customermenu', 'permission:read-customer-panel'], 'prefix' => 'customers', 'namespace' => 'Modules\PaypalStandard\Http\Controllers'], function () {
Route::get('invoices/{invoice}/paypalstandard', 'PaypalStandard@show');
});
Route::group(['prefix' => 'customers', 'namespace' => 'Modules\PaypalStandard\Http\Controllers'], function () {
Route::post('invoices/{invoice}/paypalstandard/result', 'PaypalStandard@result');
Route::post('invoices/{invoice}/paypalstandard/callback', 'PaypalStandard@callback');
});

View File

View File

View File

@ -0,0 +1,119 @@
<?php
namespace Modules\PaypalStandard\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Factory;
use App\Events\PaymentGatewayListing;
use Modules\PaypalStandard\Events\Handlers\PaypalStandardGateway;
class PaypalStandardServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Boot the application events.
*
* @return void
*/
public function boot()
{
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->registerFactories();
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
$this->app['events']->listen(PaymentGatewayListing::class, PaypalStandardGateway::class);
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
/**
* Register config.
*
* @return void
*/
protected function registerConfig()
{
$this->publishes([
__DIR__.'/../Config/config.php' => config_path('paypalstandard.php'),
], 'config');
$this->mergeConfigFrom(
__DIR__.'/../Config/config.php', 'paypalstandard'
);
}
/**
* Register views.
*
* @return void
*/
public function registerViews()
{
$viewPath = resource_path('views/modules/paypalstandard');
$sourcePath = __DIR__.'/../Resources/views';
$this->publishes([
$sourcePath => $viewPath
]);
$this->loadViewsFrom(array_merge(array_map(function ($path) {
return $path . '/modules/paypalstandard';
}, \Config::get('view.paths')), [$sourcePath]), 'paypalstandard');
}
/**
* Register translations.
*
* @return void
*/
public function registerTranslations()
{
$langPath = resource_path('lang/modules/paypalstandard');
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, 'paypalstandard');
} else {
$this->loadTranslationsFrom(__DIR__ .'/../Resources/lang', 'paypalstandard');
}
}
/**
* Register an additional directory of factories.
* @source https://github.com/sebastiaanluca/laravel-resource-flow/blob/develop/src/Modules/ModuleServiceProvider.php#L66
*/
public function registerFactories()
{
if (! app()->environment('production')) {
app(Factory::class)->load(__DIR__ . '/Database/factories');
}
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [];
}
}

View File

@ -0,0 +1,17 @@
<?php
return [
'paypalstandard' => 'Paypal Standard',
'email' => 'Email',
'mode' => 'Mode',
'debug' => 'Debug',
'transaction' => 'Transaction',
'customer' => 'Show to Customer',
'order' => 'Order',
'test_mode' => 'Warning: The payment gateway is in \'Sandbox Mode\'. Your account will not be charged.',
'description' => 'Pay with PAYPAL',
'confirm' => 'Confirm',
];

View File

@ -0,0 +1,48 @@
<h2>{{ $gateway['name'] }}</h2>
@if($gateway['mode'] == 'sandbox')
<div class="alert alert-danger"><i class="fa fa-exclamation-circle"></i> {{ trans('paypalstandard::paypalstandard.test_mode') }}</div>
@endif
<div class="well well-sm">
{{ trans('paypalstandard::paypalstandard.description') }}
</div>
<form action="{{ $gateway['action'] }}" method="post">
<input type="hidden" name="cmd" value="_cart" />
<input type="hidden" name="upload" value="1" />
<input type="hidden" name="business" value="{{ $gateway['email'] }}" />
<?php $i = 1; ?>
@foreach ($invoice->items as $item)
<input type="hidden" name="item_name_{{ $i }}" value="{{ $item->name }}" />
@if($item->sku)
<input type="hidden" name="item_number_{{ $i }}" value="{{ $item->sku }}" />
@endif
<input type="hidden" name="amount_{{ $i }}" value="{{ $item->price }}" />
<input type="hidden" name="quantity_{{ $i }}" value="{{ $item->quantity }}" />
<?php $i++; ?>
@endforeach
<input type="hidden" name="currency_code" value="{{ $invoice->currency_code}}" />
<input type="hidden" name="first_name" value="{{ $invoice->first_name }}" />
<input type="hidden" name="last_name" value="{{ $invoice->last_name }}" />
<input type="hidden" name="address1" value="{{ $invoice->customer_address }}" />
<input type="hidden" name="address_override" value="0" />
<input type="hidden" name="email" value="{{ $invoice->customer_email }}" />
<input type="hidden" name="invoice" value="{{ $invoice->id . '-' . $invoice->customer_name }}" />
<input type="hidden" name="lc" value="{{ $gateway['language'] }}" />
<input type="hidden" name="rm" value="2" />
<input type="hidden" name="no_note" value="1" />
<input type="hidden" name="no_shipping" value="1" />
<input type="hidden" name="charset" value="utf-8" />
<input type="hidden" name="return" value="{{ url('customers/invoices/' . $invoice->id . '/paypalstandard/result') }}" />
<input type="hidden" name="notify_url" value="{{ url('customers/invoices/' . $invoice->id . '/paypalstandard/callback') }}" />
<input type="hidden" name="cancel_return" value="{{ url('customers/invoices/' . $invoice->id) }}" />
<input type="hidden" name="paymentaction" value="{{ $gateway['transaction'] }}" />
<input type="hidden" name="custom" value="{{ $invoice->id }}" />
<input type="hidden" name="bn" value="Akaunting_1.0_WPS" />
<div class="buttons">
<div class="pull-right">
<input type="submit" value="{{ trans('paypalstandard::paypalstandard.confirm') }}" class="btn btn-success" />
</div>
</div>
</form>

View File

View File

@ -0,0 +1,15 @@
{
"name": "akaunting/paypalstandard",
"description": "",
"authors": [
{
"name": "Akaunting",
"email": "info@akaunting.com"
}
],
"autoload": {
"psr-4": {
"Modules\\PaypalStandard\\": ""
}
}
}

View File

@ -0,0 +1,85 @@
{
"name": "PaypalStandard",
"alias": "paypalstandard",
"description": "",
"version": "1.0.0",
"category": "payment-gateways",
"keywords": [],
"active": 1,
"order": 0,
"providers": [
"Modules\\PaypalStandard\\Providers\\PaypalStandardServiceProvider"
],
"aliases": {},
"files": [
"start.php"
],
"requires": [],
"settings": [
{
"type": "textGroup",
"name": "name",
"title": "general.name",
"icon": "id-card-o",
"attributes": {
"required": "required"
}
},
{
"type": "textGroup",
"name": "email",
"title": "paypalstandard::paypalstandard.email",
"icon": "envelope-o",
"attributes": {
"required": "required"
}
},
{
"type": "selectGroup",
"name": "mode",
"title": "paypalstandard::paypalstandard.mode",
"icon": "plane",
"values": {
"live": "Live",
"sandbox": "Sandbox"
},
"selected": null,
"attributes": {}
},
{
"type": "selectGroup",
"name": "transaction",
"title": "paypalstandard::paypalstandard.transaction",
"icon": "exchange",
"values": {
"authorization": "Authorization",
"sale": "Sale"
},
"selected": null,
"attributes": {}
},
{
"type": "radioGroup",
"name": "customer",
"title": "paypalstandard::paypalstandard.customer",
"enable": "general.yes",
"disable": "general.no",
"attributes": {}
},
{
"type": "radioGroup",
"name": "debug",
"title": "paypalstandard::paypalstandard.debug",
"enable": "general.yes",
"disable": "general.no",
"attributes": {}
},
{
"type": "textGroup",
"name": "order",
"title": "paypalstandard::paypalstandard.order",
"icon": "sort",
"attributes": {}
}
]
}

View File

@ -0,0 +1,17 @@
<?php
/*
|--------------------------------------------------------------------------
| Register Namespaces And Routes
|--------------------------------------------------------------------------
|
| When a module starting, this file will executed automatically. This helps
| to register some namespaces like translator or view. Also this file
| will load the routes file for each module. You may also modify
| this file as you want.
|
*/
if (!app()->routesAreCached()) {
require __DIR__ . '/Http/routes.php';
}

View File

@ -142,16 +142,14 @@
</div>
<div class="box-footer row no-print">
<div class="col-md-10">
<div class="col-md-12">
<a href="{{ url('incomes/invoices/' . $invoice->id . '/print') }}" target="_blank" class="btn btn-default">
<i class="fa fa-print"></i>&nbsp; {{ trans('general.print') }}
</a>
<a href="{{ url('incomes/invoices/' . $invoice->id . '/pdf') }}" class="btn btn-default" data-toggle="tooltip" title="{{ trans('invoices.download_pdf') }}">
<i class="fa fa-file-pdf-o"></i>&nbsp; {{ trans('general.download') }}
</a>
</div>
<div class="col-md-2">
@if($invoice->invoice_status_code != 'paid')
@if ($payment_methods)
{!! Form::select('payment_method', $payment_methods, null, array_merge(['id' => 'payment-method', 'class' => 'form-control', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.payment_methods', 1)])])) !!}