Unable to communicate with the PayPal error - magento

i m using Magento ver. 1.8.1.0
i got Unable to communicate with the PayPal gateway. error with paypal express checkout or paypal Payments Pro in both case.
i have already Enable SSL verification :- No
Can you explain me about this error.
Thanks

Paypal have recently rolled out some security updates on the sandbox (production will be updated in June) https://devblog.paypal.com/upcoming-security-changes-notice/
Most importantly, TLS 1.0 and 1.1 are no longer accepted by the sandbox, and the Magento Paypal module doesn't use 1.2 by default. We can expect an official patch to fix this shortly, but in the meantime you can work around it by overriding Mage/Paypal/Model/Api/Nvp.php (in your local codepool or with a rewrite) with the following call function:
public function call($methodName, array $request)
{
$request = $this->_addMethodToRequest($methodName, $request);
$eachCallRequest = $this->_prepareEachCallRequest($methodName);
if ($this->getUseCertAuthentication()) {
if ($key = array_search('SIGNATURE', $eachCallRequest)) {
unset($eachCallRequest[$key]);
}
}
$request = $this->_exportToRequest($eachCallRequest, $request);
$debugData = array('url' => $this->getApiEndpoint(), $methodName => $request);
try {
$http = new Varien_Http_Adapter_Curl();
$http->addOption(CURLOPT_SSLVERSION,6);//CURL_SSLVERSION_TLSv1_2
$config = array(
'timeout' => 60,
'verifypeer' => $this->_config->verifyPeer
);
if ($this->getUseProxy()) {
$config['proxy'] = $this->getProxyHost(). ':' . $this->getProxyPort();
}
if ($this->getUseCertAuthentication()) {
$config['ssl_cert'] = $this->getApiCertificate();
}
$http->setConfig($config);
$http->write(
Zend_Http_Client::POST,
$this->getApiEndpoint(),
'1.1',
$this->_headers,
$this->_buildQuery($request)
);
$response = $http->read();
} catch (Exception $e) {
$debugData['http_error'] = array('error' => $e->getMessage(), 'code' => $e->getCode());
$this->_debug($debugData);
throw $e;
}
$response = preg_split('/^\r?$/m', $response, 2);
$response = trim($response[1]);
$response = $this->_deformatNVP($response);
$debugData['response'] = $response;
$this->_debug($debugData);
$response = $this->_postProcessResponse($response);
// handle transport error
if ($http->getErrno()) {
Mage::logException(new Exception(
sprintf('PayPal NVP CURL connection error #%s: %s', $http->getErrno(), $http->getError())
));
$http->close();
Mage::throwException(Mage::helper('paypal')->__('Unable to communicate with the PayPal gateway.'));
}
// cUrl resource must be closed after checking it for errors
$http->close();
if (!$this->_validateResponse($methodName, $response)) {
Mage::logException(new Exception(
Mage::helper('paypal')->__("PayPal response hasn't required fields.")
));
Mage::throwException(Mage::helper('paypal')->__('There was an error processing your order. Please contact us or try again later.'));
}
$this->_callErrors = array();
if ($this->_isCallSuccessful($response)) {
if ($this->_rawResponseNeeded) {
$this->setRawSuccessResponseData($response);
}
return $response;
}
$this->_handleCallErrors($response);
return $response;
}
The important line is $http->addOption(CURLOPT_SSLVERSION,6);//CURL_SSLVERSION_TLSv1_2

Related

Laravel - Using Stream with the new Http client

I'm migrating my old system to the new version of Laravel, and I'm having problems with one of my requests...
Basically on this request I receive any file and simply forward it to the user. Here is the old version using Guzzle:
use Symfony\Component\HttpFoundation\StreamedResponse;
public function getMedia($media)
{
try {
$response = $this->client->get('media/' . $media, [
'stream' => true
]
);
$contentType = $response->getHeader('Content-Type');
$body = $response->getBody();
$stream = new StreamedResponse(function () use ($body) {
while (!$body->eof()) {
echo $body->read(1024);
}
});
$stream->headers->set('Content-Type', $contentType);
return $stream;
} catch (ClientException $e) {
return response()->json([
'errors' => json_decode($e->getResponse()->getBody()->getContents())->errors,
'message' => 'Unfortunately we could not find the requested file'
], 404);
}
}
And the new code that I tried to write, without success:
use Symfony\Component\HttpFoundation\StreamedResponse;
public function getMedia($media)
{
$response = Http::withOptions([
'stream' => true
])->get("media/{$media}");
$contentType = $response->header('Content-Type');
$body = $response->body();
$stream = new StreamedResponse(function () use ($body) {
while (!$body->eof()) {
echo $body->read(1024);
}
});
$stream->headers->set('Content-Type', $contentType);
return $stream;
}
Does anyone have any idea how to solve this? I don't know what to do anymore...
I know, 2 years late, but i'm doing something similar, you should access to the response via the psr
instead of:
$body = $response->body(); // This try to return an string
Use this:
$body = $response->toPsrResponse()->getBody(); // the guzzle response
Then you can use your normal code
I hope someone can find this useful,

Does anyone have a working example of Omnipay and Sagepay Server or Sagepay Direct (with 3D Secure)?

I'm struggling to get either to work and Omnipay doesn't come with much documentation. I've successfully used it for other payment gateways but not with Sagepay. I'm trying to integrate it into CodeIgniter but can work from examples in other frameworks - I'm getting desperate!
Thanks to some great help on github (see comments in my original post for the thread link), I now have some workable code which I will share here in case it helps someone else in the future.
<?php
use Omnipay\Omnipay;
class PaymentGateway {
//live details
private $live_vendor = 'xxx';
//test details
private $test_vendor= 'xxx';
//payment settings
private $testMode = true;
private $api_vendor = '';
private $gateway = null;
public function __construct()
{
parent::__construct();
//setup api details for test or live
if ($this->testMode) :
$this->api_vendor = $this->test_vendor;
else :
$this->api_vendor = $this->live_vendor;
endif;
//initialise the payment gateway
$this->gateway = Omnipay::create('SagePay_Server');
$this->gateway->setVendor($this->api_vendor);
$this->gateway->setTestMode($this->testMode);
}
public function initiate()
{
//get order details
$orderNo = customFunctionToGetOrderNo(); //get the order number from your system however you store and retrieve it
$params = array(
'description'=> 'Online order',
'currency'=> 'GBP',
'transactionId'=> $orderNo,
'amount'=> customFunctionToGetOrderTotal($orderNo)
);
$customer = customFunctionToGetCustomerDetails($orderNo);
$params['returnUrl'] = '/payment-gateway-process/' . $orderNo . '/'; //this is the Sagepay NotificationURL
$params['card'] = array(
'firstName' => $customer['billing_firstname'],
'lastName' => $customer['billing_lastname'],
'email' => $customer['billing_email'],
'billingAddress1' => $customer['billing_address1'],
'billingAddress2' => $customer['billing_address2'],
'billingCity' => $customer['billing_town'],
'billingPostcode' => $customer['billing_postcode'],
'billingCountry' => $customer['billing_country'],
'billingPhone' => $customer['billing_telephone'],
'shippingAddress1' => $customer['delivery_address1'],
'shippingAddress2' => $customer['delivery_address2'],
'shippingCity' => $customer['delivery_town'],
'shippingPostcode' => $customer['delivery_postcode'],
'shippingCountry' => $customer['delivery_country']
);
try {
$response = $this->gateway->purchase($params)->send();
if ($response->isSuccessful()) :
//not using this part
elseif ($response->isRedirect()) :
$reference = $response->getTransactionReference();
customFunctionToSaveTransactionReference($orderNo, $reference);
$response->redirect();
else :
//do something with an error
echo $response->getMessage();
endif;
} catch (\Exception $e) {
//do something with this if an error has occurred
echo 'Sorry, there was an error processing your payment. Please try again later.';
}
}
public function processPayment($orderNo)
{
$params = array(
'description'=> 'Online order',
'currency'=> 'GBP',
'transactionId'=> $orderNo,
'amount'=> customFunctionToGetOrderTotal($orderNo)
);
$customer = customFunctionToGetCustomerDetails($orderNo);
$transactionReference = customFunctionToGetTransactionReference($orderNo);
try {
$response = $this->gateway->completePurchase(array(
'transactionId' => $orderNo,
'transactionReference' => $transactionReference,
))->send();
customFunctionToSaveStatus($orderNo, array('payment_status' => $response->getStatus()));
customFunctionToSaveMessage($orderNo, array('gateway_response' => $response->getMessage()));
//encrypt it to stop anyone being able to view other orders
$encodeOrderNo = customFunctionToEncodeOrderNo($orderNo);
$response->confirm('/payment-gateway-response/' . $encodeOrderNo);
} catch(InvalidResponseException $e) {
// Send "INVALID" response back to SagePay.
$request = $this->gateway->completePurchase(array());
$response = new \Omnipay\SagePay\Message\ServerCompleteAuthorizeResponse($request, array());
customFunctionToSaveStatus($orderNo, array('payment_status' => $response->getStatus()));
customFunctionToSaveMessage($orderNo, array('gateway_response' => $response->getMessage()));
redirect('/payment-error-response/');
}
}
public function paymentResponse($encodedOrderNo)
{
$orderNo = customFunctionToDecode($encodedOrderNo);
$sessionOrderNo = customFunctionToGetOrderNo();
if ($orderNo != $sessionOrderNo) :
//do something here as someone is trying to fake a successful order
endif;
$status = customFunctionToGetOrderStatus($orderNo);
switch(strtolower($status)) :
case 'ok' :
customFunctionToHandleSuccess($orderNo);
break;
case 'rejected' :
case 'notauthed' :
//do something to handle failed payments
break;
case 'error' :
//do something to handle errors
break;
default:
//do something if it ever reaches here
endswitch;
}
}
I gave a talk last night about this, and have put the working demo scripts on github here:
https://github.com/academe/OmniPay-SagePay-Demo
SagePay Direct is a one-off action - OmniPay sends the transaction details and gets an immediate response.
SagePay Server involves a redirect of the user to the SagePay website to authorise the transaction using their card details. This API uses a notify message, where SagePay will call your application directly with the authorisation results. This happens outside of the user's session, and so requires the transaction to be stored in the database so it can be shared between the two transactions.
All this is in the scripts linked above. authorize.php will do the authorisation. Edit that to use SagePay\Direct or SagePay\Server to see how it works. The notification handler for SagePay\Server is sagepay-confirm.php and that ultimately sends the user to final.php where the result can be read from the transaction stored in the database.
The scripts are all commented and should make sense, but feel free to ask more questions about them here or in the issue tracker of that github repository.
I've not tried SagePay\Direct with 3D-Secure though. The scripts may need some modification to support that, assuming that combination is a thing.

How can I process a Response directly in Laravel?

I'm writing code in my Laravel Controller and I want to trap some exceptions firing a response directly without returning something to the routes.
For example, I wrote a method for returning a 404 response:
public static function respondNotFound( $message = null, $instantResponse = false )
{
$message = $message ? $message : "Not Found";
$statusCode = self::STATUS_NOTFOUND;
return self::makeResponse( array( 'status' => $statusCode, 'message' => $message ), $statusCode );
}
This method calls another one for building an Illuminate Response
protected static function makeResponse( $data, $statusCode = self::STATUS_OK, $instantResponse = false )
{
$response = Illuminate\Support\Facades\Response::json( $data, $statusCode );
$response->setCallback( Input::get( 'callback' ) );
if( $instantResponse ) {
//..... I want to fire my Response here!
}
else {
return $response;
}
}
Referring to the method above, I want to specify that my response must be fired directly rather than being returned outside, avoiding a "waterfall of return".
My solution is to set up some php headers and then kill the script, but I think that it's a bit rough.
Can someone help me?
Thanks in advance.
I'm confused - why dont you just use the App::missing() filter and handle your 404 in there?
In 'app/start/global.php' add:
App::missing(function($exception)
{
if (Request::ajax())
{
return Response::json( ['status' => 'error', 'msg' => 'There was an error. I could not find what you were looking for.'] );
}
elseif ( ! Config::get('app.debug'))
{
return Response::view('errors.404', array(), 404);
}
});
From looking at your code - you seem to be duplicating the response class. I dont understand why you are doing all of that, when you can just do
return Response::view('error', $message, $statuscode);
anywhere in your application...
Edit: you could also do
App::abort(404);
And then catch the abort filter in your app. You can change the 404 to be any HTTP response code you want.

Laravel 4 Basic Auth custom error

I'm using the 'HTTP Basic Authentication' feature of laravel. I want to customize the error message which is generated from laravel if the entered credentials are wrong.
Is it possible to catch the 401 Error which is generated when HTTP Auth fails?
Hope you can help me.
Regards
Basic Auth
Try to capture 401 error and return cusom view?!
App::error(function($exception, $code)
{
switch ($code)
{
case 401:
return Response::view('errors.403', array(), 401);
case 403:
return Response::view('errors.403', array(), 403);
case 404:
return Response::view('errors.404', array(), 404);
case 500:
return Response::view('errors.500', array(), 500);
default:
return Response::view('errors.default', array(), $code);
}
});
Using Auth library
I think, code is pretty straightforward and self explaining.
Just to note, $errors variable is of type MessageBag and is available in views even if you don't set it explicitly! Which is great! :)
I used simple routing, place it into your controllers
app/routes.php
Route::get('auth', function()
{
$creds = array(
'email' => Input::get('email'),
'password' => Input::get('password'),
);
if ( ! Auth::attempt($creds))
{
$errors = new MessageBag;
$errors->add('login', trans("Username and/or password invalid."));
return Redirect::to('/')->withErrors($errors);
}
return Redirect::to('/protected/area');
});
Route::get('/', function(){
return View::make('hello');
});
// app/views/hello.php
#if($errors->has('login'))
{{ $errors->first('login') }}
#endif
Here's how I did it:
Route::filter('auth.basic', function()
{
$message = [
"error" => [
"code" => 401,
"message" => "Invalid Credentials"
]
];
$headers = ['WWW-Authenticate' => 'Basic'];
$response = Auth::basic();
if (!is_null($response)) {
return Response::json($message, 401, $headers);
}
});
If you look in Illuminate\Auth\Guard you'll find the basic method that's called by Auth::basic(). It either returns null or a Response object via the getBasicResponse method.
/**
* Attempt to authenticate using HTTP Basic Auth.
*
* #param string $field
* #param \Symfony\Component\HttpFoundation\Request $request
* #return \Symfony\Component\HttpFoundation\Response|null
*/
public function basic($field = 'email', Request $request = null)
{
if ($this->check()) return;
$request = $request ?: $this->getRequest();
// If a username is set on the HTTP basic request, we will return out without
// interrupting the request lifecycle. Otherwise, we'll need to generate a
// request indicating that the given credentials were invalid for login.
if ($this->attemptBasic($request, $field)) return;
return $this->getBasicResponse();
}
Here's getBasicResponse:
/**
* Get the response for basic authentication.
*
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function getBasicResponse()
{
$headers = array('WWW-Authenticate' => 'Basic');
return new Response('Invalid credentials.', 401, $headers);
}
Here we finally have our 'Invalid credentials.' text that we're looking to change. We see it's just returning an instance of a Symphony response with a 401 status code and the Basic Auth header and null in all other occasions. So, we'll just check for a non-null result and if we get one, return our new response as shown above.
Also, if you want it to actually be stateless you should use:
Auth::onceBasic()
I don't know how future proof this method is, but it works as of Laravel 4.1.
Final results once again:
Route::filter('auth.basic', function()
{
$message = [
"error" => [
"code" => 401,
"message" => "Invalid Credentials"
]
];
$headers = ['WWW-Authenticate' => 'Basic'];
$response = Auth::onceBasic();
if (!is_null($response)) {
return Response::json($message, 401, $headers);
}
});

Hybrid Auth with Codeigniter

I downloaded the codeigniter extension of HybridAuth here:
https://github.com/andacata/HybridIgniter
I followed instructions on its use. When I try to login via any provider at: www.mydomainname.com/hauth/login/twitter it loads a page saying:
HybridAuth
Open Source Social Sign On PHP Library.
hybridauth.sourceforge.net/
It never works. I have valid API credentials for Twitter and Facebook but both load this page and nothing else happens. Any tips would be greatly appreciated.
UPDATE
My log says:
Hybrid_Provider_Adapter::login( facebook ), redirect the user to login_start URL. -- Array
(
[hauth_return_to] => http://www.sitename.com/hauth/login/facebook
[hauth_token] => 6vjglu8usmsjqsi74cku8o85j3
[hauth_time] => 1335997302
[login_start] => http://sitename.com/hauth/endpoint?hauth.start=facebook&hauth.time=1335997302
[login_done] => http://sitename.com/hauth/endpoint?hauth.done=facebook
)
INFO -- 127.0.0.1 -- 2012-05-03T00:21:42+02:00 -- Enter Hybrid_Auth::redirect( http://sitename.com/hauth/endpoint?hauth.start=facebook&hauth.time=1335997302, PHP )
UPDATE
Here is a link to the controller
https://github.com/andacata/HybridIgniter/blob/master/application/controllers/hauth.php
the above answer didn't help much to me but i figured out the problem.
Add index.php to base_url in config/hybridauthlib.php
'base_url' => '/index.php/hauth/endpoint',
here is my code which work 100% :
class Auth extends MX_Controller {
public function __construct()
{
parent::__construct();
$this->template->set_layout('login');
}
//social login
public function social($provider)
{
try{
$this->load->library('HybridAuthLib');
$this->load->model('auth_model');
$serviceEnabled = $this->hybridauthlib->serviceEnabled($provider);
if ($serviceEnabled)
{
$this->service = $this->hybridauthlib->authenticate($provider);
if ($this->service->isUserConnected())
{
$user_profile = $this->service->getUserProfile();
if($this->auth_model->count_user_by_uid($user_profile->identifier) === 0)
{
$this->session->set_flashdata('message','You Dont have account.. Create one.');
redirect('/users/register','refresh');
}
else
{
$dump_data = $this->auth_model->get_by(array('provider_uid'=>$user_profile->identifier));
$user = $this->ion_auth->user($dump_data->user_id)->row();
$session_data = array(
'identity' => $user->{$this->config->item('identity', 'ion_auth')},
'username' => $user->username,
'email' => $user->email,
'user_id' => $user->id, //everyone likes to overwrite id so we'll use user_id
'old_last_login' => $user->last_login
);
$this->ion_auth->update_last_login($user->id);
$this->ion_auth->clear_login_attempts($this->config->item('identity', 'ion_auth'));
$this->session->set_userdata($session_data);
if ($this->config->item('remember_users', 'ion_auth'))
{
$this->ion_auth->remember_user($user->id);
}
$this->ion_auth->trigger_events(array('post_login', 'post_login_successful'));
$this->ion_auth->set_message('login_successful');
redirect('/','refresh');
}
}
else // Cannot authenticate user
{
$this->session->set_flashdata('message','Cannot authenticate user');
redirect('/users/auth/login/','refresh');
}
}
else // This service is not enabled.
{
$this->session->set_flashdata('message','This providers is not enabled.');
redirect('/users/auth/login/','refresh');
}
}
catch(Exception $e)
{
$error = 'Unexpected error';
switch($e->getCode())
{
case 0 : $error = 'Unspecified error.'; break;
case 1 : $error = 'Hybriauth configuration error.'; break;
case 2 : $error = 'Provider not properly configured.'; break;
case 3 : $error = 'Unknown or disabled provider.'; break;
case 4 : $error = 'Missing provider application credentials.'; break;
case 5 : log_message('debug', 'controllers.HAuth.login: Authentification failed. The user has canceled the authentication or the provider refused the connection.');
//redirect();
if (isset($service))
{
$service->logout();
}
$error = 'User has cancelled the authentication or the provider refused the connection.';
break;
case 6 : $error = 'User profile request failed. Most likely the user is not connected to the provider and he should to authenticate again.';
break;
case 7 : $error = 'User not connected to the provider.';
break;
}
if (isset($this->service))
{
$this->service->logout();
}
log_message('error', 'controllers.HAuth.login: '.$error);
$this->session->set_flashdata('message', $error);
redirect('/users/auth/login/', 'refresh');
}
}
public function endpoint()
{
log_message('debug', 'controllers.HAuth.endpoint called.');
log_message('info', 'controllers.HAuth.endpoint: $_REQUEST: '.print_r($_REQUEST, TRUE));
if ($_SERVER['REQUEST_METHOD'] === 'GET')
{
log_message('debug', 'controllers.HAuth.endpoint: the request method is GET, copying REQUEST array into GET array.');
$_GET = $_REQUEST;
}
log_message('debug', 'controllers.HAuth.endpoint: loading the original HybridAuth endpoint script.');
require_once ADDONPATH.'/users/third_party/hybridauth/index.php'; //ADDONPATH is my modules path
}
}
i hope that you can find it useful.
am using the ion_auth for the main login system.
the auth_model is a small model which check if the user has enabled this provider with name or not, since i want the user to have the same data even if he use another social network to login with ..

Resources