For some reason PayPal isn't receiving the total price I'm assigning to the transaction.
I'm using NetShell's PayPal module.
I have my controller that looks like this:
class PayPalController extends Controller {
private $_apiContext;
public function __construct() {
$this->_apiContext = PayPal::ApiContext(
config('services.paypal.client_id'),
config('services.paypal.secret'));
$this->_apiContext->setConfig(array(
'mode' => 'sandbox',
'service.EndPoint' => 'https://api.sandbox.paypal.com',
'http.ConnectionTimeOut' => 30,
'log.LogEnabled' => true,
'log.FileName' => storage_path('logs/paypal.log'),
'log.LogLevel' => 'FINE'
));
}
public function getCheckout() {
$payer = PayPal::Payer();
$payer->setPaymentMethod('paypal');
$amount = PayPal::Amount();
$amount->setCurrency('USD');
$amount->setTotal(42); // This is the simple way,
// you can alternatively describe everything in the order separately;
// Reference the PayPal PHP REST SDK for details.
$transaction = PayPal::Transaction();
$transaction->setAmount($amount);
$transaction->setDescription('What are you selling?');
$redirectUrls = PayPal::RedirectUrls();
$redirectUrls->setReturnUrl(action('PayPalController#getDone'));
$redirectUrls->setCancelUrl(action('PayPalController#getCancel'));
$payment = PayPal::Payment();
$payment->setIntent('sale');
$payment->setPayer($payer);
$payment->setRedirectUrls($redirectUrls);
$payment->setTransactions(array($transaction));
$response = $payment->create($this->_apiContext);
$redirectUrl = $response->links[1]->href;
return Redirect::to( $redirectUrl );
}
// Other functions here. Removed for question.
}
getCheckout() gets called at the route `/paypal/checkout'
When I go to /paypal/checkout there is no price.
Have I missed something? Any help would be appreciated.
Thanks!
It could be about the price type.Price must be declared in float.
Try,
floatval($price)
And check this out,I wrote about integrating paypal express to laravel,
Paypal express integration
Related
I can't seem to get data transferred after I submit a create form of a model record to a mailable. I think I'm right on most of it, but I'm stuck at passing the data through the gap between a controller and my mailable.
I have a shipment controller with the following function:
public function store(Request $request)
{
$this->validate(request(), [
'pro_number' => 'required',
'shipment_origin' => 'required'
/*'piecesNumber' => 'required' (had to remove for now, but must review)*/
]);
$user_id = Auth::id();
$input = $request->all();
//Save Initial Shipment Data
$shipment = new Shipment();
$shipment->pro_number = request('pro_number');
$shipment->shipment_origin = request('shipment_origin');
$shipment->date = request('date');
$shipment->due_date = request('due_date');
$shipment->tractor_id = request('tractor_id');
$shipment->trailer_id = request('trailer_id');
$shipment->driver_id = request('driver_id');
$shipment->notes = request('notes');
$shipment->shipper_no = request('shipper_no');
$shipment->ship_to = request('ship_to');
$shipment->ship_from = request('ship_from');
$shipment->bill_to = request('bill_to');
$shipment->bill_type = request('bill_type');
$shipment->load_date = request('load_date');
$shipment->shipment_status = 0;
$shipment->created_by = $user_id;
$shipment->save();
//Save Shipment Details
for ($i = 0; $i < count($request->shipment_details['piecesNumber']); $i++) {
Shipment_Detail::create([
'shipment_id' => $shipment->id,
'pieces_number' => $request->shipment_details['piecesNumber'][$i],
'pieces_type' => $request->shipment_details['piecesType'][$i],
'rate_type' => $request->shipment_details['rateType'][$i],
'charge' => $request->shipment_details['charge'][$i],
'weight' => $request->shipment_details['weight'][$i],
'hazmat' => $request->shipment_details['hazmat'][$i],
'description' => $request->shipment_details['description'][$i] ]);
}
$user = Auth::user()->email;
Mail::to($user)->send(new newFreightBill($shipment));
Session::flash('success_message','Freight Bill Successfully Created'); //<--FLASH MESSAGE
//Return to Register
return redirect('/shipments/i/'.$shipment->url_string);
}
Now as you can see there is a line near the bottom "Mail::to($user)->send(new newFreightBill($shipment));", what I would like to be doing is moving the copying the data from the submitted $shipment to the mailable
My newFreightBill mailable:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Shipment;
class newFreightBill extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* #return void
*/
public $shipment;
public function __construct(Shipment $shipment)
{
$shipment = $this->shipment;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from('MYEMAIL')
->view('emails.shipments.created')->with(['shipment', $this->shipment]);
}
}
and my mail template (which I do receive in my tests):
<div>
Pro Number: {{ $shipment['pro_number'] }}
</div>
As you can see the template is VERY simple to say the least. I get in my emails to me the following:
Pro Number:
but absolutely nothing else, no actual number. SO I know the issue is passing the data from the form submit to the mailable variables.
If you are using properties on your Mail class such as public $shipment, you shouldn't use with() method. The properties on your class are available on your view without the need of using with.
And then you should do this: Pro Number: {{ $shipment->pro_number }}
Oh yes, and this: $shipment = $this->shipment; is backwards, it should be $this->shipment = $shipment;.
I'm trying to implement the PayPal API
namespace App\Http\Controllers;
use PayPal;
use Redirect;
class PaypalPaymentController extends Controller{
private $_apiContext;
public function __construct()
{
$this->_apiContext = PayPal::ApiContext(
config('services.paypal.client_id'),
config('services.paypal.secret'));
$this->_apiContext->setConfig(array(
'mode' => 'sandbox',
'service.EndPoint' => 'https://api.sandbox.paypal.com',
'http.ConnectionTimeOut' => 30,
'log.LogEnabled' => true,
'log.FileName' => storage_path('logs/paypal.log'),
'log.LogLevel' => 'FINE'
));
}
public function getCheckout()
{
$payer = PayPal::Payer();
$payer->setPaymentMethod('paypal');
$amount = PayPal:: Amount();
$amount->setCurrency('EUR');
$amount->setTotal(500); // This is the simple way,
// you can alternatively describe everything in the order separately;
// Reference the PayPal PHP REST SDK for details.
$transaction = PayPal::Transaction();
$transaction->setAmount($amount);
$transaction->setDescription('Altaro VM Backup');
$redirectUrls = PayPal:: RedirectUrls();
$redirectUrls->setReturnUrl(action('PaypalPaymentController#getDone'));
$redirectUrls->setCancelUrl(action('PaypalPaymentController#getCancel'));
$payment = PayPal::Payment();
$payment->setIntent('sale');
$payment->setPayer($payer);
$payment->setRedirectUrls($redirectUrls);
$payment->setTransactions(array($transaction));
$response = $payment->create($this->_apiContext);
$redirectUrl = $response->links[1]->href;
return Redirect::to( $redirectUrl );
}
public function getDone(Request $request)
{
$id = $request->get('paymentId');
$token = $request->get('token');
$payer_id = $request->get('PayerID');
$payment = PayPal::getById($id, $this->_apiContext);
$paymentExecution = PayPal::PaymentExecution();
$paymentExecution->setPayerId($payer_id);
$executePayment = $payment->execute($paymentExecution, $this->_apiContext);
// Clear the shopping cart, write to database, send notifications, etc.
// Thank the user for the purchase
return "Merci pour votre achat";
}
public function getCancel()
{
// Curse and humiliate the user for cancelling this most sacred payment (yours)
return "Erreur";
}
}
As you can see in the controller i'm trying to call other function of the controller i'm in :
$redirectUrls->setReturnUrl(action('PaypalPaymentController#getDone'));
$redirectUrls->setCancelUrl(action('PaypalPaymentController#getCancel'));
But i have the following error :
InvalidArgumentException in UrlGenerator.php line 602:
Action App\Http\Controllers\PaypalPaymentController#getDone not defined.
I don't understand how it is even possible... I checked multiple times the spelling, everything's correct here.
Any thoughts ?
EDIT:
I also tried this :
$redirectUrls->setReturnUrl(route('paypal.done'));
$redirectUrls->setCancelUrl(route('paypal.cancel'));
with these routes:
Route::get('done', [
'as' => 'paypal.done',
'uses' => 'PaypalPaymentController#getDone'
]);
Route::get('cancel', [
'as' => 'paypal.cancel',
'uses' => 'PaypalPaymentController#getCancel'
]);
It works !
I think you also have to define a route to the methods, otherwise the URL cannot be build and returns an error.
I am using zendframework 2 and My translations are not working here in form class where the form is formed and there is validation, elsewhere in whole applications they are working properly.
I have pasted all the code in my file with namespaces.
<?php
namespace Services\Form;
use Zend\Form\Form;
use Zend\Form\Element;
use Zend\InputFilter\Input;
use Zend\InputFilter\InputFilter;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ProfilePicForm extends Form implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function init()
{
$routeMatch = $this->getServiceLocator()->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch();
$translator = $this->getServiceLocator()->getServiceLocator()->get('viewHelperManager')->get('translate')->getTranslator();
$action = $routeMatch->getParam('action');
// Form
parent::__construct('profile_pic_form');
$this->setAttribute('method', 'post');
$this->setAttribute('enctype','multipart/form-data');
$profile_pic_form_csrf = new Element\Csrf('profile_pic_form_csrf');
$profile_pic_form_csrf->setCsrfValidatorOptions(array('timeout'=>'3600'));
$this->add($profile_pic_form_csrf);
$profile_pic = new Element\File('profile_pic');
$this->add($profile_pic);
// Validation
$inputFilter = new InputFilter();
$profile_pic = new Input('profile_pic');
$profile_pic->getFilterChain()
->attach(new \Lib\MyLib\Filter\RenameUpload(array(
'target' => SERVICE_PROFILE_PIC_UPLOAD_PATH.'/profile-pic.*',
'use_upload_extension' => true,
'randomize' => true
)));
$required = true;
$profile_pic->setRequired($required);
$validator = new \Zend\Validator\File\UploadFile();
$validator->setOptions(array(
'messageTemplates' => array(
\Zend\Validator\File\UploadFile::FILE_NOT_FOUND => 'Please select picture.'
)));
$profile_pic->getValidatorChain()->attach($validator,true);
$validator = new \Zend\Validator\File\Size(array('max' => 250*1024));
$validator->setMessage(**$translator->translate('MyAccountPictureErrorMessage1')**);
$profile_pic->getValidatorChain()->attach($validator,true);
$validator = new \Zend\Validator\File\Extension('png,jpg');
$validator->setMessage(**$translator->translate('MyAccountPictureErrorMessage2')**);
$profile_pic->getValidatorChain()->attach($validator,true);
$inputFilter->add($profile_pic);
$this->setInputFilter($inputFilter);
}
this is my controller function.
public function profileAction() {
$this->layout('ajax-layout');
$var = new \stdClass();
$viewmodel = new ViewModel();
$this->authPlugin()->checkLogin();
$this->servicePlugin()->checkSid();
$this->layout()->setVariable('allowedFeatures', $this->featurePlugin()->getAllowedFeatures());
$this->languagePlugin()->translate();
$var->userInfo = $this->authPlugin()->getUserInfo();
if($this->params()->fromRoute('sid') !== null){
$var->sid = $this->params()->fromRoute('sid');
}
elseif ($this->params()->fromRoute('id') !== null) {
$var->sid = $this->params()->fromRoute('id');
}
// ----------------------- i m here --------------------------
// $var->sid = $this->params()->fromRoute('sid');
$var->profilePicForm = $this->getServiceLocator()->get('FormElementManager')->get('\Services\Form\ProfilePicForm');
$var->serviceNameForm = $this->getServiceLocator()->get('FormElementManager')->get('\Services\Form\ServiceNameForm');
$var->service = $this->getServices()->fetchServiceById($var->sid);
// Fetch payment history
$var->paymentHistory = $this->getServiceLocator()->get('Services\Model\PaymentTransactionService')->fetchPaymentTransactionsByServiceId($var->sid);
$var->timezones = $this->getTimeZoneTable()->listAll();
$viewmodel->setVariables(array('var' => $var));
return $viewmodel;
}
This is happening because of your validator.
I already talked about this problem, when you call new validators without the service locator :
https://stackoverflow.com/a/36500438/3333246
To fix that you need to set the translator in your options because:
class Size extends AbstractValidator
abstract class AbstractValidator implements
Translator\TranslatorAwareInterface,
ValidatorInterface
And TranslatorAwareInterface is not initialized if you instanciate a new Validator without ServiceLocator.
So your validators need options declared like this in your code:
$validator = new \Zend\Validator\File\Size(array('translator' => $translator, 'max' => 250*1024));
$validator->setMessage('MyAccountPictureErrorMessage1');
No need to translate the message now, the validator will translate it for you.
For my comment, about your code, nevermind it's not related to your problem. It's just personal in fact.
EDIT:
You don't need this translator :
$translator = $this->getServiceLocator()->getServiceLocator()->get('viewHelperManager')->get('translate')->getTranslator();
But this one
$translator = $this->getServiceLocator()->get('translator');
I have found another way to do this job, i have made an ajax call and on its response i show the divs having the translations.
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.
I am using ci-merchant library and integrated it succesfully and also works for paypal account owner user.But dont know how to processs for user who dont have paypal acc and wants to pay via credit or debit card on my website only*(without redirect to paypal)* any idea????abt that.....this is the code i use for the normal paypal payment in my controller and works good as well..
$this->load->library('merchant');
$this->merchant->load('paypal_express');
$settings = $this->merchant->default_settings();
$settings = array(
'username' => 'takeout_api1.rest.com',
'password' => '1369227981',
'signature' => 'AnOQDpMvzNQqHN5u7vb9BKLaKYLoALq6R0g3ohOwD4RQgO0DQDI5l7V4',
'test_mode' => true,
);
$this->merchant->initialize($settings);
$params = array(
'amount' => 1500.00,
'currency' => 'CAD',
'return_url' => 'http://192.168.1.7/takeout/order_detail/test',
'cancel_url' => 'http://192.168.1.7/takeout/order_detail/test');
$response = $this->merchant->purchase($params);
function test()
{
$settings = array(
'username' => 'takeout_api1.rest.com',
'password' => '1369227981',
'signature' => 'AnOQDpMvzNQqHN5u7vb9BKLaKYLoALq6R0g3ohOwD4RQgO0DQDI5l7V4',
'test_mode' => true);
$this->merchant->initialize($settings);
$params = array(
'amount' => 1500.00,
'currency' => 'CAD',
'return_url' => 'http://192.168.1.7/takeout/order_detail/test',
'cancel_url' => 'http://192.168.1.7/takeout/order_detail/test');
$response = $this->merchant->purchase_return($params);
if ($response->success())
{
// mark order as complete
echo "yo";
exit;
}
else
{
$message = $response->message();
echo('Error processing payment: ' . $message);
exit;
}
}
You could interface your Merchant services
interface merchantServiceInterface
{
public function initialize();
public function purchase();
public function purchase_return();
}
Paypal
class Paypal implements merchantServiceInterface
{
public function initialize(){}
public function purchase(){}
public function purchase_return(){}
}
Credit/Debit Cards
class Realex implements merchantServiceInterface
{
public function initialize(){}
public function purchase(){}
public function purchase_return(){}
}
Now in your form, have a little radio button group and ask the user to select
either paypal or credit/debit card
<label>Choose a payment Method</label>
<label>Paypal<label>
<input type="radio" name="merchant" value="paypal" />
<label>Credit/Debit Card<label>
<input type="radio" name="merchant" value="debit" />
Merchant Library
class Merchant
{
protected $_service;
public function __construct(merchantServiceInterface $service)
{
$this->_service = $service;
}
public function initialize()
{
// Will either run Paypal/Realex initialize()
// Depending on what instance was passed to _service
//
$this->_service->initialize();
}
}
Controller
class Controller extends CI_Controller
{
public function method()
{
if($this->input->post('merchant') == 'paypal')
{
$service = new Paypal();
}
elseif($this->input->post('merchant') == 'debit')
{
$service = new Realex();
}
$this->load->library('Merchant', $service);
$this->merchant->initialize();
}
}
Edit to answer your comment
I just used Realex as an example
You need to figure out what both libraries have in common, or at a very low level abstraction figure out what they share.
an example would be
They both need a initialize method to configure options
They both need to send a request to an API
They both need a responce
etc etc keep abstracting away
How you handle these, will be unique to the library itself.
interface merchantServiceInterface
{
// Use the facade design pattern here
// so configuration is done in each library
public function initialize();
// Send a request with data
// Paypal - use http_build_query and curl
// Realex - use xml and curl
public function request(array $data);
public function responce();
}
Paypal Express Checkout doesn't support taking credit cards on your site. It is an off-site gateway, so redirect is mandatory.
You need to explore using PayPal Pro, Payflow, or any number of other gateways which support accepting credit cards directly on your website (plus the extra PCI requirements which come with that).