omnipay paypal express not returning address - codeigniter

I am using the omnipay setup here: https://github.com/adrianmacneil/omnipay to process a paypal express checkout.
The process works fine in that the user is redirected to paypal -> they login and choose to pay -> they get returned to my site at which point I capture the payment.
The problem I've got is that I need to capture the address they have entered into paypal as their billing / shipping address.
To send the user across to paypal I have the following:
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('XX-USERNAME_XX');
$gateway->setPassword('XX_PASSWORDXX');
$gateway->setSignature('XX_SIG_XX');
$gateway->setTestMode(true);
$response = $gateway->purchase(
array(
'cancelUrl'=>'http://www.XXX.co.uk/',
'returnUrl'=>'http://www.XXX.co.uk/paypalexpress_confirm',
'amount' => $totalamount,
'currency' => 'GBP'
)
)->send();
$response->redirect();
When the user is returned I have the following:
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('XX-USERNAME_XX');
$gateway->setPassword('XX_PASSWORDXX');
$gateway->setSignature('XX_SIG_XX');
$gateway->setTestMode(true);
$response = $gateway->completePurchase(
array(
'cancelUrl'=>'http://www.XXX.co.uk/',
'returnUrl'=>'http://www.XXX.co.uk/paypalexpress_confirm',
'amount' => $totalamount,
'currency' => 'GBP'
)
)->send();
echo $responsemsg=$response->getMessage();
echo '<br><br><br>';
$data = $response->getData();
print_r($data);
Nothing in the response message or the raw data contains the customer address.
Has anyone got this working as i'm struggling and it's the final step to complete the transaction.

For those who are trying to get this work it's as Adrian said.
You first do the normal omnipay paypal payment and then afterwards:
get the token you were given
preform a second call to paypal using the call getexpresscheckoutdetails method
this returns all the info you need
API info here: https://cms.paypal.com/uk/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetExpressCheckoutDetails
The php script paypal provide to do it all for you:
https://cms.paypal.com/cms_content/ES/es_ES/files/developer/nvp_ECGetExpressCheckout_php.txt

omnipay\paypal\ProGateway.php add new function
public function fetchExpressCheckoutDetail(array $parameters = array())
{
return $this->createRequest('\Omnipay\PayPal\Message\FetchExpressCheckoutRequest', $parameters);
}
omnipay\paypal\src\Message add new file FetchExpressCheckoutRequest.php
namespace Omnipay\PayPal\Message;
class FetchExpressCheckoutRequest extends AbstractRequest
{
public function getData()
{
$data = $this->getBaseData('GetExpressCheckoutDetails');
$this->validate('transactionReference');
$data['TOKEN'] = $this->getTransactionReference();
$url = $this->getEndpoint()."?USER={$data['USER']}&PWD={$data['PWD']}&SIGNATURE={$data['SIGNATURE']}&METHOD=GetExpressCheckoutDetails&VERSION={$data['VERSION']}&TOKEN={$data['TOKEN']}";
parse_str (file_get_contents( $url ),$output);
$data = array_merge($data,$output);
return $data;
}
}
Usage:
$response = $gateway->completePurchase($params)->send();
$data = $response->getData();
$gateway->fetchExpressCheckoutDetail(array('transactionReference'=>$data['TOKEN']))->getData();
It will be not the best. But it works. :)

If it's not returned by the $response->getData() method, you might need to call PayPal's GetExpressCheckoutDetails API method to get the extra details about the transaction.
Omnipay doesn't support this out of the box, so you will probably need to copy and customize one of the existing requests to make a separate API call after you have confirmed payment.

Related

I need to do a function to resend OTP if user has not received it to update phone number functionality in laravel

Maybe I'm losing in a glass of water but I'm not understanding how implements this function, I use OTP code to verify phone when user compile the registration and when he try to update number. I'm using vonage, I put below my update method:
public function updatePhoneNumber(Request $request)
{
$basic = new \Vonage\Client\Credentials\Basic("44bc4bb2", "fYVcLeo0lMhmtjm1");
$client = new \Vonage\Client($basic);
$request->validate([
'telefono' => 'required|unique:users,telefono'
]);
$telefono = $request->input('telefono');
$otp = VerificationCode::create([
'otp' => rand(10000, 99999),
'expire_at' => Carbon::now()->addMinutes(10)
]);
$response = $client->sms()->send(
new SMS($telefono, 'Help4You', 'Il tuo codice di verifica è:'. "\n" . $otp->otp)
);
$message = $response->current();
if ($message->getStatus() == 0) {
echo "The message was sent successfully\n";
} else {
echo "The message failed with status: " . $message->getStatus() . "\n";
}
User::where('id', '=', Auth::user()->id)->update([
'telefono'=>Hash::make($request['telefono']),
]);
}
how can I resend the otp if user has not received it?
You simply need to have 2 separate functions. The logic for creating an OTP and sending the OTP is in a different function. Myself, I create a service for it and store it in a App\Services folder since it is a functionality I commonly use.
So, back to your question, separate the logic for creating an OTP and sending the otp, in your case, the following code should not be part of your function above
$telefono = $request->input('telefono');
$otp = VerificationCode::create([
'otp' => rand(10000, 99999),
'expire_at' => Carbon::now()->addMinutes(10)
]);
$response = $client->sms()->send(
new SMS($telefono, 'Help4You', 'Il tuo codice di verifica è:'. "\n" . $otp-
>otp)
);
}
If you are implementing this is a separate class, remember to return the outcome of the sending of the OTP, it can either be success or error. Also, the only value you need to pass onto the function will be the recipient's cellphone number.
So, first time you send the OTP, you simply call the function or instance of the class, depending how you implemented.
Now, if the OTP was not delivered or a user wants to request another OTP, you call the function again.
You can add additional validation or a field on you table to check how many retries a user does for an OTP since the sms will cost your client money.
Once you separate your logic, you have now made your code to be reusable and is you need to use the same API to send a different SMS, you can do so.
Hope this helps.

How to use a Webhook url with Paystack and send values to Database

I am using https://github.com/iamraphson/vue-paystack in my laravel+vue SPA project. I can receive payments perfectly but now need to hit a webhook url so that I can update database and give value to user. I have set up the route and added the url on my paystack dashboard. What should I do next? How do I get the charge events and other details in my controller method and send to database?
I have tried the following in my controller:
public function payme(Request $request){
//This receives the webhook
//$email = json_decode($input['data']['customer']['email']);
$email = $request->input('data.customer.email');
$bankname = $request->input('data.gateway_response');
//$bankname = json_decode($input['data']['gateway_response']);
$user = User::where('email', $email);
$user->bankname = $bankname;
$user->save();
return response()->json('processed', 200);

Laravel 5.3 - Omnipay Paypal Express not returning success message

I'm new to Laravel. I've been struggling to implement Paypal Express Checkout in my website for a couple days in order to enable donations to a non-profit organization. Thanks to these explanations I've been able to install Omnipay, let the user input the amount (s)he wants to donate and go to Paypal.
But, when I try to end the transaction (Pay), I'm not redirected to my succes message. My sandbox account does not show any transactions either, so it seems the payment is not completed correctly. I'm guessing there's something wrong with my "getSuccessPayment" function, but I can't figure out what it is...
Here's my Controller so far :
<?php namespace App\Http\Controllers;
use Omnipay\Omnipay;
use Session;
use App\Http\Requests\PaymentRequest;
class PaymentController extends Controller {
public function postPayment(PaymentRequest $request)
{
$price = $request->get('price');
$items[] = array('name' => 'Don', 'quantity' => 1, 'price' => $price);
$params = array(
'cancelUrl'=>url('/donner'),
'returnUrl'=>url('/payment_success'),
'amount' => $price,
'currency' => 'EUR'
);
Session::put('params', $params);
Session::save();
$gateway = Omnipay::create('PayPal_Express');
$gateway->setUsername('my sandbox email');
$gateway->setPassword('my sandbox password');
$gateway->setSignature('my sandbox signature');
$gateway->setTestMode(true);
$response = $gateway->purchase($params)->setItems($items)->send();
if ($response->isSuccessful()) {
print_r($response);
} elseif ($response->isRedirect()) {
$response->redirect();
} else {
echo $response->getMessage();
}
}
public function getSuccessPayment()
{
$gateway = Omnipay::create('PayPal_Express');
$gateway->setUsername('my sandbox email');
$gateway->setPassword('my sandbox password');
$gateway->setSignature('my sandbox signature');
$gateway->setTestMode(true);
$params = Session::get('params');
$response = $gateway->completePurchase($params)->send();
$paypalResponse = $response->getData();
if(isset($paypalResponse['PAYMENTINFO_0_ACK']) && $paypalResponse['PAYMENTINFO_0_ACK'] === 'Success') {
return redirect('/payment_success');
} else {
//payment fails
return redirect('/payment_failure');
}
}
}
?>
And my Routes :
Route::post('donner',
['as' => 'payment', 'uses' => 'PaymentController#postPayment']);
Route::get('payment_success', 'PaymentController#getSuccessPayment');
Route::get('payment_failure', 'PaymentController#getSuccessPayment');
When creating your gateway parameters you are passing in /donner as the returnUrl, this is where your users are returned to after completing the PayPal express login and payment confirmation so Laravel would look Route::get('donner'... route which you don't have, changing this to 'returnUrl'=>url('/payment_success'), will bring your users back to your success route and allow you to file the completePurchase call.
Edit for further details based on edited question and comments:
Customers are returned to your returnUrl if the successfully complete the PayPal login and checkout screens, they go to the cancelUrl if for whatever reason they quit the process.
In your PaymentController#getSuccessPayment method paypal will send back a token and payerID in the query string (www.example.com/payment_success?token=EC-12345&PayerID=ABC123, omnipay-paypal will automatically pick up on this in the completePurchase call which is where you are effective confirming with PayPal that the customer completed the checkout correctly and that the transaction was successful.
To avoid confusion I would rename your current Route::get('payment_success', 'PaymentController#getSuccessPayment'); route to something like Route::get('complete_payment', 'PaymentController#getCompletePayment'); and create a new payment_success route that a user is sent to after you have confirmed the status of the payment with PayPal.

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.

magento:get list of all available payment method

how can i get list of payment methods with detail like code,title,method and? is it possible get available payment method in store with API ? i need list of all available payment method in magento store.
Here You can get all available payment methods in Magento
If for any reason you need to a get a list with all payment methods in Magento, you can do it easily by using the payment config class (app/code/core/Mage/Payment/Model/Config.php).
To get a list with all payments active and inactive:
$allAvailablePaymentMethods = Mage::getModel('payment/config')->getAllMethods();
To get a list with all active payment methods:
$allActivePaymentMethods = Mage::getModel('payment/config')->getActiveMethods();
To get a list with all credit cards that Magento supports:
$allCcTypes = Mage::getModel('payment/config')->getCcTypes();
get active payment methods
$payments = Mage::getSingleton('payment/config')->getActiveMethods();
$methods = array(array('value'=>'','label'=>Mage::helper('adminhtml')->__('–Please Select–')));
foreach ($payments as $paymentCode=>$paymentModel) {
$paymentTitle = Mage::getStoreConfig('payment/'.$paymentCode.'/title');
$methods[$paymentCode] = array(
'label' => $paymentTitle,
'value' => $paymentCode,
);
}
return $methods;
yes you can get payment method using api. here is your solution
$client = new SoapClient('http://magentohost/api/soap/?wsdl');
// If somestuff requires api authentification,
// then get a session token
$session = $client->login('apiUser', 'apiKey');
$result = $client->call($session, 'cart_payment.list', 'quoteId');
var_dump($result);
You can use also this helper:
$methods = Mage::helper('payment)->getStoreMethods($store = null, $quote = null)
So you can check for a single store and for a quote if you need.

Resources