I have decided to upgrade the Laravel framework version to resolve the critical security flaws. I have successfully upgraded from 5.8 to 6 and then to 6.20 (exploit fixed).
But unfortunately I have encountered an error starting from Laravel framework 6.19.0 which throws an exception in my class (BuyWidget.php) which is related to BoundMethod.php. I have checked through the internet and found out they implemented check for non specified types in BoundMethod.php (Illuminate\Container\BoundMethod).
I tried to change all the functions in BuyWidget.php accordingly: public function run($listing) to public function run(Listing $listing) etc.
BuyWidget.php
<?php
namespace App\Widgets\Order;
use Arrilot\Widgets\AbstractWidget;
use App\Models\Listing;
class BuyWidget extends AbstractWidget
{
protected $config = [];
public function calculate_price($listing, $params) {
$fee_percentage = setting('marketplace_percentage_fee');
$fee_transaction = setting('marketplace_transaction_fee');
$quantity = isset($params['quantity'])?$params['quantity']:1;
$variants = isset($params['variant'])?$params['variant']:null;
$shipping = isset($params['shipping_option'])?$params['shipping_option']:null;
$additional_options = isset($params['additional_option'])?$params['additional_option']:[];
$additional_options_meta = isset($params['additional_options_meta'])?$params['additional_options_meta']:[];
$listing_price = $listing->price;
#calculate additional variant cost
$selected_variant = null;
$error = false;
$user_choice = [];
$user_choice[] = ['group' => 'general', 'name' => 'quantity', 'value' => $quantity];
if($variants) {
$variant_pricing = $listing->variants;
foreach($variants as $k => $v) {
$variant_pricing = $variant_pricing->where("meta.$k", $v);
$user_choice[] = ['group' => 'variant', 'name' => $k, 'value' => $v];
}
if($variant_pricing->count() == 1) {
$selected_variant = $variant_pricing->first();
$listing_price += $selected_variant->price;
if($quantity > $selected_variant->stock) {
$error = __('Insufficient stock. Please lower the quantity.');
}
if($selected_variant->stock < 1) {
$error = __('Out of Stock');
}
}
}
#calculate shipping cost
$selected_shipping_price = null;
if(!is_null($shipping)) {
$selected_shipping_method = $listing->shipping_options->firstWhere('id', $shipping)?:null;
if($selected_shipping_method) {
$selected_shipping_price = $selected_shipping_method->price;
}
$user_choice[] = ['group' => 'shipping', 'name' => 'Shipping', 'value' => $selected_shipping_method->name, 'price' => $selected_shipping_method->price];
}
#additional pricing
$additional_options_price = $listing->additional_options->reduce(function ($carry, $item) use($additional_options, $additional_options_meta) {
if(in_array($item->id, array_keys($additional_options))) {
$price = $item->price;
$quantity = 1;
if(in_array($item->id, array_keys($additional_options_meta)) && isset($additional_options_meta[$item->id]['quantity'])) {
$quantity = (int) $additional_options_meta[$item->id]['quantity'];
}
return $carry + ($price*$quantity);
}
return $carry;
}, 0);
$number = 0;
foreach($listing->additional_options as $k => $item) {
if(in_array($item->id, array_keys($additional_options))) {
$number++;
$user_choice[] = ['group' => 'additional_options', 'name' => 'Option '.($k+1), 'value' => $item->name, 'price' => $item->price];
}
}
//date, time, qty
$subtotal = ($quantity * $listing_price) + $additional_options_price;
$service_fee_percentage = $subtotal * ($fee_percentage/100);
$service_fee = (float) $service_fee_percentage + (float) $fee_transaction;
$total = $subtotal + $service_fee + $selected_shipping_price;
if($quantity > $listing->stock) {
$error = __('Insufficient stock. Please lower the quantity.');
}
if($listing->stock < 1) {
$error = __('Out of Stock');
}
//now check if we have any slots left for this time
$price_items = [
[
'key' => 'price',
'label' => __(':price x :quantity :unit_label', ['price' => format_money($listing_price, $listing->currency), 'quantity' => $quantity, 'unit_label' => $listing->unit]),
'price' => ($quantity * $listing_price)
]
];
if($selected_shipping_price) {
$price_items[] = [
'key' => 'service',
'label' => __('Shipping'),
'price' => $selected_shipping_price,
];
}
if($additional_options_price) {
$price_items[] = [
'key' => 'additional',
'label' => __('Additional options'),
'price' => $additional_options_price,
];
}
if($service_fee > 0) {
$price_items[] = [
'key' => 'service',
'label' => __('Service fee'),
'price' => $service_fee,
'notice' => __('This fee helps cover the costs of operating the website'),
];
}
return [
'user_choice' => $user_choice,
'error' => $error,
'total' => $total,
'service_fee' => $service_fee,
'price_items' => $price_items,
];
}
public function decrease_stock($order, $listing)
{
$quantity = $order->listing_options['quantity'];
$listing->decrement( 'stock', $quantity );
if(isset($order->listing_options['variant'])) {
$variants = $order->listing_options['variant'];
$listing_variants = $listing->variants;
foreach($variants as $k => $v) {
$listing_variants = $listing_variants->where("meta.$k", $v);
}
if($listing_variants->count() == 1) {
$listing_variant = $listing_variants->first();
$listing_variant->decrement( 'stock', $quantity );
}
}
}
public function validate_payment($listing, $request)
{
$result = $this->calculate_price($listing, request()->all());
return $result;
}
/**
* Treat this method as a controller action.
* Return view() or other content to display.
*/
public function run($listing)
{
//
$total = 0;
$quantity = request('quantity', 1);
$result = $this->calculate_price($listing, request()->all());
return view('listing.widgets.buy_widget', [
'config' => $this->config,
'listing' => $listing,
'qs' => http_build_query(request()->all()),
'error' => $result['error'],
'total' => $result['total'],
'service_fee' => $result['service_fee'],
'price_items' => $result['price_items'],
]);
}
}
However after applying these changes the app does not work properly, the parameters are always empty and the product is shown as Out Of Stock.
The initial exception has been thrown at sidebar.twig file which builds up the widget based on the listing type.
"An exception has been thrown during the rendering of a template ("Unable to resolve dependency [Parameter #0 [ $listing ]] ")
sidebar.twig
{% if listing.pricing_model %}
{{ Widget.run('Order.'~(listing.pricing_model.widget)~'Widget', {}, listing) | raw }}
{% endif %}
Code works flawlessly on any other versions before Laravel 6.19.0. If I manually revert the changes from file BoundMethod.php the code works as expected even on never vesions. I would like to find another solution to avoid modifying vendor files, since it's not the best approach.
web.php
include "admin.php";
include "payments.php";
Route::get('/cp', function () {
if(env('DEMO_PANEL')) {
\Auth::loginUsingId(1, true);
return redirect("/panel");
}
});
Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => 'jailBanned'], function()
{
Auth::routes();
Route::get('email-verification', 'Auth\EmailVerificationController#sendEmailVerification')->name('email-verification.send');
Route::get('email-verification/error', 'Auth\EmailVerificationController#getVerificationError')->name('email-verification.error');
Route::get('email-verification/check/{token}', 'Auth\EmailVerificationController#getVerification')->name('email-verification.check');
Route::get('/', 'HomeController#index')->name('home');
Route::get('/browse', 'BrowseController#listings')->name('browse');
Route::get('/categories', 'BrowseController#categories')->name('categories');
Route::get('/pages/{slug?}', 'PageController#index')->name('page');
Route::get('/contact', 'ContactController#index')->name('contact');
Route::post('/contact', 'ContactController#postIndex')->name('contact.post');
Route::get('/profile/{user}', 'ProfileController#index')->name('profile'); //PROFILE
Route::get('/profile/{user}/follow', 'ProfileController#follow')->name('profile.follow'); //PROFILE
//LISTINGS
Route::group(['prefix' => 'listing'], function()
{
Route::get('/{listing}/{slug}', 'ListingController#index')->name('listing');
Route::get('/{listing}/{slug}/card', 'ListingController#card')->name('listing.card');
Route::get('/{listing}/{slug}/spotlight', 'ListingController#spotlight')->middleware('auth.ajax')->name('listing.spotlight');
Route::get('/{listing}/{slug}/verify', 'ListingController#verify')->middleware('auth.ajax')->name('listing.verify');
Route::get('/{listing}/{slug}/star', 'ListingController#star')->middleware('auth.ajax')->name('listing.star');
Route::get('/{listing}/{slug}/edit', 'ListingController#edit')->name('listing.edit');
#Route::get('/{listing}/{slug}/availability', 'AvailabilityController#availability')->name('listing.availability');
Route::any('/{id}/update', 'ListingController#update')->name('listing.update');
});
//ACCOUNT
Route::group(['middleware' => ['auth', 'isVerified'], 'prefix' => 'account', 'as' => 'account.', 'namespace' => 'Account'], function()
{
Route::get('/', function () {
return redirect(route('account.edit_profile.index'));
});
Route::resource('change_password', 'PasswordController');
Route::resource('edit_profile', 'ProfileController');
Route::resource('purchase-history', 'PurchaseHistoryController');
Route::resource('favorites', 'FavoritesController');
Route::resource('listings', 'ListingsController');
Route::resource('orders', 'OrdersController');
Route::get('payments/{id}/unlink', 'BankAccountController#unlink')->name('payments.unlink');
Route::resource('bank-account', 'BankAccountController');
Route::get('paypal/connect', 'PayPalController#connect')->name('paypal.connect');
Route::get('paypal/callback', 'PayPalController#callback')->name('paypal.callback');
});
//REQUIRES AUTHENTICATION
Route::group(['middleware' => ['auth', 'isVerified']], function () {
//INBOX
Route::resource('inbox', 'InboxController')->middleware('talk'); //Inbox
Route::get('/inbox/messages/{id}', 'InboxController#messages')->name('inbox.messages');
//CREATE LISTING
Route::resource('create', 'CreateController');
Route::any('/create/{listing}/session', 'CreateController#session')->name('create.session');
Route::get('/create/{listing}/images', 'CreateController#images')->name('create.images');
Route::get('/create/{listing}/additional', 'CreateController#additional')->name('create.additional');
Route::get('/create/{listing}/pricing', 'CreateController#pricing')->name('create.pricing');
Route::get('/create/{listing}/times', 'CreateController#getTimes')->name('create.times');
Route::post('/create/{listing}/times', 'CreateController#postTimes')->name('create.times');
Route::get('/create/{listing}/boost', 'CreateController#boost')->name('create.boost');
Route::post('/create/{listing}/uploads', 'CreateController#upload')->name('create.upload');
Route::delete('/create/{listing}/image/{uuid?}', 'CreateController#deleteUpload')->name('create.delete-image');
//CHECKOUT
Route::get('/checkout/error', 'CheckoutController#error_page')->name('checkout.error');
Route::get('/checkout/{listing}', 'CheckoutController#index')->name('checkout');
Route::post('/checkout/{listing}', 'CheckoutController#store')->name('checkout.store');
Route::get('/checkout/{session}/callback', 'CheckoutController#callback')->name('checkout.callback');
Route::any('/checkout/process/{listing}', 'CheckoutController#process')->name('checkout.process');
#Route::any('/checkout/test', 'CheckoutController#test')->name('checkout.test');
#Route::resource('stripe', 'StripeController');
#Route::any('/stripe/connect', 'StripeController#connect')->name('stripe.connect');
Route::any('/paypal/{listing}/start', 'PaypalController#start')->name('paypal.start');
Route::any('/paypal/cancel', 'PaypalController#cancel')->name('paypal.cancel');
Route::any('/paypal/callback', 'PaypalController#callback')->name('paypal.callback');
Route::any('/paypal/confirm', 'PaypalController#confirm')->name('paypal.confirm');
#Route::any('/paypal/create_agreement', 'PaypalController#create_agreement')->name('paypal.create_agreement');
});
//REQUIRES AUTHENTICATION
Route::group(['middleware' => ['auth']], function () {
Route::get('email-verification', 'Auth\EmailVerificationController#index')->name('email-verification.index');
Route::get('resend-verification', 'Auth\EmailVerificationController#resend')->name('email-verification.resend');
Route::get('email-verified', 'Auth\EmailVerificationController#verified')->name('email-verification.verified');
});
Route::get('login/facebook', 'Auth\LoginController#redirectToProvider');
Route::get('login/facebook/callback', 'Auth\LoginController#handleProviderCallback');
});
#errors
Route::get('/suspended',function(){
return 'Sorry something went wrong.';
})->name('error.suspended');
public function run(Listing $listing)
And you included a class in that namespace that matches Listing? Or is it available through service providers?
Remove parameter from function:
public function run();
and add the following line to the function:
$listing = func_get_arg(0);
I'm building an online courses website, where students can buy a course via PayPal. The payment process is working successfully, But I'm confused on where or how to store course and user data after PayPal Payment Approval.
I have a users table, courses table and pivot table: course_students where I store the id of the course and the id of the student:
-------------
course_students
--------------
student_id course_id
1 2
----------
This is PayPalService Class:
class PayPalService
{
use ConsumesExternalServices;
protected $baseUri;
protected $clientId;
protected $clientSecret;
public function __construct()
{
$this->baseUri = config('services.paypal.base_uri');
$this->clientId = config('services.paypal.client_id');
$this->clientSecret = config('services.paypal.client_secret');
}
public function resolveAuthorization(&$queryParams, &$formParams, &$headers)
{
$headers['Authorization'] = $this->resolveAccessToken();
}
public function decodeResponse($response)
{
return json_decode($response);
}
public function resolveAccessToken()
{
$credentials = base64_encode("{$this->clientId}:{$this->clientSecret}");
return "Basic {$credentials}";
}
public function handlePayment(Request $request)
{
$order = $this->createOrder($request->value, $request->currency);
$orderLinks = collect($order->links);
$approve = $orderLinks->where('rel', 'approve')->first();
session()->put('approvalId', $order->id);
return redirect($approve->href);
}
public function handleApproval()
{
if (session()->has('approvalId')) {
$approvalId = session()->get('approvalId');
$payment = $this->capturePayment($approvalId);
$name = $payment->payer->name->given_name;
$payment = $payment->purchase_units[0]->payments->captures[0]->amount;
$amount = $payment->value;
$currency = $payment->currency_code;
return redirect()
->route('success')
->with('payment', "Thanks, {$name}. We received your {$amount}{$currency} payment.");
}
// $errorMessage = 'We cannot capture the payment. Try again, please';
return redirect()
->route('paymentform')
->with('error','We cannot capture the payment. Try again, please');
}
public function createOrder($value, $currency)
{
return $this->makeRequest(
'POST',
'/v2/checkout/orders',
[],
[
'intent' => 'CAPTURE',
'purchase_units' => [
0 => [
'amount' => [
'currency_code' =>strtoupper($currency),
'value' => round($value * $factor = $this->resolveFactor($currency)) / $factor,
]
]
],
'application_context' => [
'brand_name' => config('app.name'),
'shipping_preference' => 'NO_SHIPPING',
'user_action' => 'PAY_NOW',
'return_url' => route('approval'),
'cancel_url' => route('cancelled'),
]
],
[],
$isJsonRequest = true,
);
}
public function capturePayment($approvalId)
{
return $this->makeRequest(
'POST',
"/v2/checkout/orders/{$approvalId}/capture",
[],
[],
[
'Content-Type' => 'application/json'
],
);
}
public function resolveFactor($currency)
{
$zeroDecimalCurrencies = ['JPY'];
if (in_array(strtoupper($currency), $zeroDecimalCurrencies)) {
return 1;
}
return 100;
}
}
PaymentController:
public function paymentForm($course_uuid)
{
$currencies = Currency::all();
$platforms = PaymentPlatform::get();
$course = Course::where('uuid', $course_uuid)->where('status',1)->where('availability',1)->first();
return view('public.payment.paypalform', compact('currencies','platforms','course'));
}
/**
* implment Payemnt process
*
* #param Request $request
*/
public function pay(Request $request)
{
// dd($request->all());
$rules = [
'value' => ['required', 'numeric', 'min:5'],
'currency' => ['required', 'exists:currencies,iso'],
'payment_platform' => ['required', 'exists:payment_platforms,id'],
];
$request->validate($rules);
$paymentPlatform = $this->paymentPlatformResolver
->resolveService($request->payment_platform);
session()->put('paymentPlatformId', $request->payment_platform);
return $paymentPlatform->handlePayment($request);
}
protected function approval()
{
if (session()->has('paymentPlatformId')) {
$paymentPlatform = $this->paymentPlatformResolver
->resolveService(session()->get('paymentPlatformId'));
return $paymentPlatform->handleApproval();
}else{
return redirect()
->route('courses.levels')
->withErrors('We cannot retrieve your payment platform. Try again, please.');
}
}
protected function canceled()
{
return redirect()
->route('courses.levels')
->withErrors('You cancelled the payment.');
}
In your payment controller you can save you the course information in session. After payment when user will redirect, save the information to your desired database table..
//save course info or other info
session()->put('courseInfo', [your data]);
your controller function:
/**
* implment Payemnt process
*
* #param Request $request
*/
public function pay(Request $request)
{
// dd($request->all());
$rules = [
'value' => ['required', 'numeric', 'min:5'],
'currency' => ['required', 'exists:currencies,iso'],
'payment_platform' => ['required', 'exists:payment_platforms,id'],
];
$request->validate($rules);
$paymentPlatform = $this->paymentPlatformResolver
->resolveService($request->payment_platform);
session()->put('paymentPlatformId', $request->payment_platform);
//save course info or other info
session()->put('courseInfo', [your data]);
return $paymentPlatform->handlePayment($request);
}
you another function: approval() call another function $paymentPlatform->handleApproval(); inside of it after payment done.
Go to handleApproval() function and hopefully you will find there user account creation codes. Now get the session value session()->get('courseInfo') and save in your desired table.
I had developed the E-Program Website on the Laravel 7 Framework. I had integrated my CC Avenue payment Gateway with the help of indipay softon, package but on payment successful I am getting error i.e POST Method is not supported.. supported method is GET, HEAD
.env file
INDIPAY_MERCHANT_ID=26XXXX
INDIPAY_ACCESS_CODE=AVPXXXXXXXXXXXXXXXX
INDIPAY_WORKING_KEY=77XXXXXXXXXXXXXXXXXXXXXXX
INDIPAY_REDIRECT_URL="http://eprogram.lfwproducts.com/checkoutResponse"
INDIPAY_CANCEL_URL="http://eprogram.lfwproducts.com/cart"
INDIPAY_CURRENCY="INR"
INDIPAY_LANGUAGE="EN"
controller File
public function checkout(Request $request)
{
if (Auth::check()) {
$courseId = $request->post('courseId');
$courseTicket = $request->post('courseTicket');
$userId = $request->post('uid');
$total = $request->post('total');
$letters = "LFW-EP";
$digits = "1234567890";
$randomString = "";
for ($i = 0; $i < 3; $i++) {
$randomString .= $digits[rand(0, strlen($digits) - 1)];
}
$orderId = $letters . $randomString;
$parameters = [
'merchant_id' => 26XXXX,
'currency' => 'INR',
'redirect_url' => 'http://eprogram.lfwproducts.com/checkoutResponse',
'cancel_url' => 'http://eprogram.lfwproducts.com/cart',
'language' => 'EN',
'order_id' => $orderId,
'amount' => $total,
'merchant_param1' => $userId,
'merchant_param2' => $courseId[0],
'merchant_param3' => $courseTicket[0],
];
$order = Indipay::gateway('CCAvenue')->prepare($parameters);
return Indipay::process($order);
} else {
return \view('auth.login');
}
}
public function myPaymentResponse(Request $request)
{
// For default Gateway
$response = Indipay::response($request);
// For Otherthan Default Gateway
$response1 = Indipay::gateway('CCAvenue')->response($request);
dd($response);
}
verifycsrftoken file
protected $except = [
'https://secure.ccavenue.com/transaction/transaction.do?command=initiateTransaction',
'http://eprogram.lfwproducts.com/checkoutResponse',
'*/checkoutResponse',
'ccavenue/*'
];
web.php file
Route::get('/checkoutResponse', 'CartController#myPaymentResponse')->name('checkoutResponse');
Error
This is my first time to implement Paypal on my project. I am using srmklive/laravel-paypal and on the documentation I tested the following code
This is my controller:
class CheckoutController extends Controller
{
protected $provider;
public function __construct(){
$this->provider = new ExpressCheckout();
}
public function getExpressCheckout(Request $request){
$data = [];
$data['items'] = [
[
'name' => 'Product 1',
'price' => 9.99,
'qty' => 1
],
[
'name' => 'Product 2',
'price' => 4.99,
'qty' => 2
]
];
$data['invoice_id'] = 2;
$data['invoice_description'] = "test";
$data['return_url'] = url('/');
$data['cancel_url'] = url('/cart');
$total = 0;
foreach($data['items'] as $item) {
$total += $item['price']*$item['qty'];
}
$data['total'] = $total;
//give a discount of 10% of the order amount
$data['shipping_discount'] = round((10 / 100) * $total, 2);
$response = $this->provider->setExpressCheckout($data);
return redirect($response['paypal_link']);
}
}
This is my route:
Route::get('/paypal/ec-checkout', 'CheckoutController#getExpressCheckout')->name('checkout');
This is my link:
Checkout
My problem is when I click the link it just loads forever and no errors are displayed. I am using Laravel 5.6
I am able to save products into my groups using the group id using the save function
but now i want to do that using an excel file. I am having issues with how to do that. Any help will be appreciated. Thank you.
Controller
public function save($id, Request $request)
{
$group = Group::whereId($id)->firstorFail();
$product = new Product(array(
'name' => $request->get('name'),
'price' => $request->get('price'),
));
$group->products()->save($product);
}
public function import($id, Request $request)
{
$group = Group::whereId($id)->firstorFail();
if($request->file('imported-file'))
{
$path = $request->file('imported-file')->getRealPath();
$data = Excel::load($path, function($reader)
{
})->get();
if(!empty($data) && $data->count())
{
foreach ($data->toArray() as $row)
{
if(!empty($row))
{
$dataArray[] =
[
'name' => $row['name'],
'price' => $row['price'],
];
}
}
if(!empty($dataArray))
{
Item::insert($dataArray);
}
}
}
}
Route
Route::post('import/{id?}', 'ItemController#import');
Model for Product
Product
public function groups()
{
return $this->belongsToMany('App\Group','group_product','product_id','group_id')
->withTimestamps();
}
Model for group
Group
public function products()
{
return $this->belongsToMany('App\Product','group_product','group_id','product_id')
}
You need saveMany
$dataArray[] =
new App\Product([
'name' => $row['name'],
'price' => $row['price'],
]);
$group->products()->saveMany($dataArray);