I have integrated the Razorpay payment gateway in my laravel project. It works well on desktop but does not work on mobile .
Here is my code
public function pay(Request $request)
{
//Input items of form
$input = $request->all();
//get API Configuration
$api = new Api(ENV('RZP_KEY_ID'), ENV('RZP_KEY_SECRET'));
//Fetch payment information by razorpay_payment_id
$payment = $api->payment->fetch($input['razorpay_payment_id']);
if(count($input) && !empty($input['razorpay_payment_id'])) {
try {
$paymentDetails = $api->payment->fetch($input['razorpay_payment_id'])->capture(array('amount'=>$payment['amount']));
} catch (\Exception $e) {
//delete params from session
if(Session::has('amount')){
Session::pull('amount');
Session::pull('converted_amount');
Session::pull('currency');
Session::pull('charge');
}
return redirect(route('user.deposit.index'))->with('fail', 'Your Deposit request failed');
}
//record payment
//somwe codes for validating and recording payment removed
}
}
When I try to make payment using a mobile device, after inputing the credit card details, Instead of a popup windown that appears on desktop, I get redirected to https://api.razorpay.com/v1/payments/create/checkout to enter otp and confirm the payment.
After a Payment, it will redirect back to my site, here are the problems;
All sessions would be deleted and I would need to login again,
The redirect is a post request but if I relogin, it becomes a get request
How can I solve this problem
**Modifed:
For those who may have this same problem. Simply move the specific route from web.php to api.php
Related
I have two questions, first about the error, Laravel 8.20.1 + Paypal SDK v1.
App Routes:
Route::get('/agreement/execute', [PaymentController::class, 'agreementExecute']);
Route::post('/agreement/create', [PaymentController::class, 'subscribeMonthly']);
Admin Routes:
Route::prefix('paypal')->name('plan.')->group(function () {
Route::get('/monthly/create', [PrivatePaypal::class, 'createMonthly'])
Route::get('/list', [PrivatePaypal::class, 'showPlans'])
Route::get('/plan/{plan}', [PrivatePaypal::class, 'plan'])
Route::get('/delete', [PrivatePaypal::class, 'deletePlan'])
});
Created plan:
Created plan image
Plan code:
public function createMonthly() {
$plan = new \PayPal\Api\Plan();
$plan->setName('Monthly')
->setDescription('Activate partnership for one month.')
->setType('INFINITE'); // or FIXED: The plan has a fixed number of payment cycles
$paymentDefinition = new \PayPal\Api\PaymentDefinition();
$paymentDefinition->setName('Monthly Payments')
->setType('REGULAR') // or TRIAL
->setFrequency('Month') // or WEEK, DAY, YEAR, MONTH
->setFrequencyInterval("1") // The interval at which the customer is charged. Value cannot be greater than 12 months
->setAmount(new \PayPal\Api\Currency(array('value' => 20, 'currency' => 'USD')));
// ->setCycles("12")
$merchantPreferences = new \PayPal\Api\MerchantPreferences();
$merchantPreferences->setReturnUrl(route('account.agreement',['success'=>'true']))
->setCancelUrl(route('account.agreement',['success'=>'false']))
->setAutoBillAmount("yes")
->setInitialFailAmountAction("CONTINUE")
->setMaxFailAttempts("0")
->setSetupFee(new \PayPal\Api\Currency(array('value' => config('settings.price_monthly'), 'currency' => 'USD')))
;
$plan->setPaymentDefinitions(array($paymentDefinition));
$plan->setMerchantPreferences($merchantPreferences);
try {
$createdPlan = $plan->create($this->apiContext);
} catch(\Exception $ex) {
print_r($ex->getMessage());
die();
}
// dd($createdPlan);
$this->activatePlan($createdPlan);
}
After I send data with the form
<form action="https://site.test/agreement/create" method="post">
<input type="hidden" name="_token" value="XSM2gxx0Cqs5dlloYScQfl2GdeGqrz4lkWLfm42a">
<input type="hidden" name="_method" value="POST">
<input type="hidden" name="id" value="P-0UV961714R317531UT5H72WI">
<button class="button compact">Activate</button>
</form>
After succesful redirect to paypal with all data, i click accept (sandbox) and after that i get succesfull rerict back, redirect functions:
if (!empty($request->input('success')))
{
$success = $request->input('success');
if ($success && !empty($request->input('token')))
{
$token = $request->input('token');
$agreement = new \PayPal\Api\Agreement();
try {
// Execute agreement
$agreement->execute($token, $this->apiContext);
} catch (PayPal\Exception\PayPalConnectionException $ex) {
print_r($ex->getMessage());
echo $ex->getCode();
echo $ex->getData();
print_r($ex->getData());
die($ex);
} catch (Exception $ex) {
// die($ex);
}
And when $agreement->execute runs I get errors with no detailed information.
Form subscribe function:
public function subscribeMonthly(Request $request) {
$id = $request->id;
$agreement = new \PayPal\Api\Agreement();
$agreement->setName('Monthly subscription')
->setDescription('Activate partnership for one month.')
->setStartDate(Carbon::now()->addMonth()->toIso8601String());
$plan = new \PayPal\Api\Plan();
$plan->setId($id);
$agreement->setPlan($plan);
$payer = new \PayPal\Api\Payer();
$payer->setPaymentMethod('paypal');
$agreement->setPayer($payer);
try {
$agreement = $agreement->create($this->apiContext);
$approvalUrl = $agreement->getApprovalLink();
} catch(\Exception $ex) {
print_r($ex->getMessage());
die();
}
return redirect($approvalUrl);
}
Error is : PayPal\Exception\PayPalConnectionException
Got Http response code 400 when accessing https://api.sandbox.paypal.com/v1/payments/billing-agreements/EC-1LE052463N345662M/agreement-execute.
https://am.test/account/agreement?ba_token=BA-9X735270PX851462W&success=true&token=EC-1LE052463N345662M
I reviewed a lot of tutorials, re-read the code in PayPal guides several times. I'm new to this and can't figure out what the reason is, it just doesn't work for me. I do everything one to one.
No subscriptions are created in the buyer account or by the administrator, everything is empty.
And the second question, Paypal writes that v1 is deprecated. How can I use the second version with a checkout v2 for subscriptions and where can I find detailed guides with the Laravel about this question.
It's hard for me to fully understand API and code not created by myself, I create a big project on my own, but stuck a few days with that PayPal error. Thank you for reading so much of what I have written, I hope for your support.
The PayPal-PHP-SDK is deprecated, and not compatible with the current version of PayPal Subscriptions. You should not be using it for anything.
Instead, integrate directly with the necessary Product, Plan, and subscription management API calls described in that Subscriptions documentation. (Plus webhooks, if desired--to be notified of future subscription events)
You can also manage Products and Plans manually in the receiver account:
Sandbox: https://www.sandbox.paypal.com/billing/plans
Live: https://www.paypal.com/billing/plans
Using laravel 5.2, I'm hitting a logout listener at the time of logging out that logs the user out of their cognito instance.
This seems to be successful but I"m having a hard time getting a working message to flash on logout, specifically, I want the 200 message response from the auth call below
I have handled the JSON for errors at the endpoint call, but if the part of my try block is successful then I get a 200 response with message "User logged out successfully".
How should I properly flash the 200 message on the sign in screen/redirect after logging out?
try {
$authService = new AuthService();
$authLogout = $authService->logout($logoutToken);
}catch (\Exception $e) {
$response = json_decode($e->getResponse()->getBody()->getContents());
$message = $response->message;
return Redirect::back()->withErrors($message);
}
Add to a session
session(['key' => 'value']);
Retrieve from the session
$value = session('key');
if you use the rest api, you can wrtite this in your logout fonction
public function logout (Request $request)
{
$token = $request->user()->token();
$token->revoke();
$response = 'You have been succesfully logged out!';
return response(["message"=>$response], 200);
}
I'm sure I'm missing something obvious here, but I can't get my head around how to check for an existing card against a customer.
I'm using the stripe connect api within an laravel app to manage payments on behalf of others, and the basic process is as follows:
a stripe token is created via stripe.js and submitted with the payment form
if the customer exists in the local database, I grab their stripe_id, otherwise a new customer is created using the token as the source/card
a charge is then created using the retrieved or new customer stripe_id
Currently, if the customer returns and uses a different card, as the charge only includes a customer, not source, it'll be charged against their default card regardless.
What I'd like to do is:
create a stripe token
check customer against local database etc
check card fingerprint against customer's cards
if necessary, create new card on customer's record
create charge using both customer and card ids
Simply put: I can't see where in the process a persistent card_id is generated; both those used in the stripe.js response, and when created in the stripe dashboard, appear to be unique, meaning every charge creates a brand-new card object in stripe.
I know I can retrieve a list of cards stored against a customer's account - but where do I get the initial card_id from to search against?
I've seen a question that touches on this here - Can I check whether stripe a card is already existed before going to create new one? - but I don't know Ruby, so can't make head nor tail of it.
EDIT:
Simpler version - is there a way to get the fingerprint as described in the stripe docs here - https://stripe.com/docs/api/php#card_object - without having to first create a card object ?
So the idea here would be to use the fingerprint on the Card object or the Token object and not the id itself as those would be different if you add the same card multiple times.
When you get a new card token you can retrieve it through the Retrieve Token API and look for the fingerprint in the card hash.
You would keep a list of known fingerprints in your database associated with a specific customer and/or card so that you can detect duplicate cards.
NOTE: make sure you are using the secret keys to get those information. otherwise if you are using the publishable key, you might not get the fingerprint value.
I created a function to do this:
$customer is the stripe customer object
$stripe_account is either your account's stripe ID or the connected account's stripe ID
$token comes from stripe.js elements
$check_exp allows you to decide if you want to check the card's expiration date as well, because the fingerprint does not change if the card's number is the same
stripe PHP API 7.0.0
function check_duplicate_card($customer, $stripe_account, $token, $check_exp) {
$loc = "check_duplicate_card >> ";
$debug = true;
if ($debug) {
// see here for an explanation for logging: http://php.net/set_error_handler >> Examples
trigger_error("$loc started", E_USER_NOTICE);
}
try
{
// get token data
$response = \Stripe\Token::retrieve(
$token,
["stripe_account" => $stripe_account]
);
$token_fingerprint = $response->card->fingerprint;
$token_exp_month = $response->card->exp_month;
$token_exp_year = $response->card->exp_year;
if ($debug) {
trigger_error("$loc token_fingerprint = $token_fingerprint; token_exp_month = $token_exp_month; token_exp_year = $token_exp_year", E_USER_NOTICE);
}
// check for duplicate source
if ($debug) {
trigger_error("$loc customer sources = " . json_encode($customer->sources), E_USER_NOTICE);
}
$duplicate_found = false;
foreach ($customer->sources->data as &$value) {
// get data
$fingerprint = $value->fingerprint;
$exp_month = $value->exp_month;
$exp_year = $value->exp_year;
if ($fingerprint == $token_fingerprint) {
if ($check_exp) {
if (($exp_month == $token_exp_month) && ($exp_year == $token_exp_year)) {
$duplicate_found = true;
break;
}
} else {
$duplicate_found = true;
break;
}
}
}
if ($debug) {
trigger_error("$loc duplicate_found = " . json_encode($duplicate_found), E_USER_NOTICE);
}
} catch (Exception $e) {
if ($e instanceof \Stripe\Exception\ApiErrorException) {
$return_array = [
"status" => $e->getHttpStatus(),
"type" => $e->getError()->type,
"code" => $e->getError()->code,
"param" => $e->getError()->param,
"message" => $e->getError()->message,
];
$return_str = json_encode($return_array);
trigger_error("$loc $return_str", E_USER_WARNING);
http_response_code($e->getHttpStatus());
echo $return_str;
} else {
$return_array = [
"message" => $e->getMessage(),
];
$return_str = json_encode($return_array);
trigger_error("$loc $return_str", E_USER_ERROR);
http_response_code(500); // Internal Server Error
echo $return_str;
}
}
if ($debug) {
trigger_error("$loc ended", E_USER_NOTICE);
}
return $duplicate_found;
}
I have successfully integrated a custom payment solution for magento with a lot of help from this tutorial http://www.junaidbhura.com/how-to-make-a-custom-magento-payment-extension-for-an-external-gateway/
I am at the last stage of notifying the website if a payment has been successful or not.
I have a PaymentController.php file below but am not sure how to link this to the payment gateway notification.
The payment gateway provide a notification to the server via an HTTP GET request, letting you know if a payment was accepted or declined. This is below
http://www.websitedomain.co.uk/mygateway/payment/response?Operator=&SessionID=&Note=&Tariff=&Status=&Mobile=
The code for the payment controller where I need to enter the code is below
class Myname_Mygateway_PaymentController extends Mage_Core_Controller_Front_Action {
// The redirect action is triggered when someone places an order
public function redirectAction() {
$this->loadLayout();
$block = $this->getLayout()->createBlock('Mage_Core_Block_Template','mygateway',array('template' => 'mygateway/redirect.phtml'));
$this->getLayout()->getBlock('content')->append($block);
$this->renderLayout();
}
// The response action is triggered when your gateway sends back a response after processing the customer's payment
public function responseAction() {
if($this->getRequest()->isPost()) {
/*
/* Your gateway's code to make sure the reponse you
/* just got is from the gatway and not from some weirdo.
/* This generally has some checksum or other checks,
/* and is provided by the gateway.
/* For now, we assume that the gateway's response is valid
*/
$validated = true;
$orderId = ''; // Generally sent by gateway
if($validated) {
// Payment was successful, so update the order's state, send order email and move to the success page
$order = Mage::getModel('sales/order');
$order->loadByIncrementId($orderId);
$order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true, 'Gateway has authorized the payment.');
$order->sendNewOrderEmail();
$order->setEmailSent(true);
$order->save();
Mage::getSingleton('checkout/session')->unsQuoteId();
Mage_Core_Controller_Varien_Action::_redirect('checkout/onepage/success', array('_secure'=>true));
}
else {
// There is a problem in the response we got
$this->cancelAction();
Mage_Core_Controller_Varien_Action::_redirect('checkout/onepage/failure', array('_secure'=>true));
}
}
else
Mage_Core_Controller_Varien_Action::_redirect('');
}
// The cancel action is triggered when an order is to be cancelled
public function cancelAction() {
if (Mage::getSingleton('checkout/session')->getLastRealOrderId()) {
$order = Mage::getModel('sales/order')->loadByIncrementId(Mage::getSingleton('checkout/session')->getLastRealOrderId());
if($order->getId()) {
// Flag the order as 'cancelled' and save it
$order->cancel()->setState(Mage_Sales_Model_Order::STATE_CANCELED, true, 'Gateway has declined the payment.')->save();
}
}
}
}
You have the line $validated = true;
Here you have to enter your own conditions if the payment has been validated.
So if this is the response from the payment gateway use the variables in the response
http://www.websitedomain.co.uk/mygateway/payment/response?Operator=&SessionID=&Note=&Tariff=&Status=&Mobile=
for example
if ( $_GET('Status') == 'Paid' ){
$validated = true;
}
You have to read the payment gateway documentation on info for their codes. For example the Status variable can have values like "Paid", "Pending", "Expired" or "1", "2", "3". Depends on the gateway.
On my site user can fill their order basket with items.
Once this is finished they can click the checkout button.
I want them to checkout using PayPal.
Once the user click on the checkout button the user is redirected to PayPal and sees an overview of the products to pay for.
If the user goes through the payment process the user is redirected to my success page.
However I expect the success page to also receive the transaction id of the payment but paypal only sends back a token and a payerid.
My checkout form looks like this:
<form action="/en/checkout">
<input type="submit" name="submit" value="Checkout">
</form>
My code that does the checkout is:
function checkoutAction()
{
$request = $this->getRequest();
require_once(LIB_PATH.'/MFW/Paypal/Flows/Paypal_NVP.php');
$paypal_nvp = new MFW_Paypal_NVP();
// this should normally be filled by looping though the basket items
$data = array('L_PAYMENTREQUEST_0_NAME0'=>'Single License',
'L_PAYMENTREQUEST_0_NUMBER0'=>'1111-2222-3333-4444-5555-6666-7777-8888',
'L_PAYMENTREQUEST_0_AMT0'=>39.99, // or enterprise 299.00
'L_PAYMENTREQUEST_0_QTY0'=>1,
);
$_SESSION['Payment_Amount'] = 39.99;
$result = $paypal_nvp->CallShortcutExpressCheckout(59.98, $data);
$ack = strtoupper($result['ACK']);
if($ack == 'SUCCESS' || $ack == 'SUCCESSWITHWARNING') {
$paypal->RedirectToPayPal($result['TOKEN']);
exit();
}
}
The code in the Paypal_NCP class:
function generate_nvp_string($total_value, $data = array())
{
$params = array('PAYMENTREQUEST_0_AMT'=>$total_value,
'PAYMENTREQUEST_0_PAYMENTACTION'=>$this->payment_type,
'RETURNURL'=>$this->return_url,
'CANCELURL'=>$this->cancel_url,
'PAYMENTREQUEST_0_CURRENCYCODE'=>$this->currency,
);
$params = array_merge($params, $data);
$nvp_string = '';
foreach($params as $name => $value) {
$nvp_string.= '&'.$name.'='.$value;
}
// example string
// &PAYMENTREQUEST_0_AMT=39.99&PAYMENTREQUEST_0_PAYMENTACTION=Sale&RETURNURL=http://return-address&CANCELURL=http://cancel-address&PAYMENTREQUEST_0_CURRENCYCODE=EUR&L_PAYMENTREQUEST_0_NAME0=Single License&L_PAYMENTREQUEST_0_NUMBER0=1111-2222-3333-4444-5555-6666-7777-8888&L_PAYMENTREQUEST_0_AMT0=39.99&L_PAYMENTREQUEST_0_QTY0=1
return $nvp_string;
}
function CallShortcutExpressCheckout($total_value, $data = array())
{
$_SESSION['currencyCodeType'] = $this->currency;
$_SESSION['PaymentType'] = $this->payment_type;
$result = $this->hash_call('SetExpressCheckout', $this->generate_nvp_string($total_value, $data));
$ack = strtoupper($result['ACK']);
if ($ack == 'SUCCESS' || $ack == 'SUCCESSWITHWARNING') {
$_SESSION['TOKEN'] = urldecode($result['TOKEN']);
}
return $result;
}
So how do I get the information of the transaction for me to be able to process the payment in the backoffice? (I need an transaction ID for this)
You're only calling SetExpressCheckout. In order to finalize a transaction with Express Checkout, you must also call (optional) GetExpressCheckoutDetails to get the PayerID (a unique identifier of the buyer) and (required) DoExpressCheckoutPayment.
To recap:
To use Express Checkout, you would call the SetExpressCheckout API. In the API call, you specify the details of the products, amounts, and the RETURNURL. This is what you're doing in the code above.
Once you post this data to PayPal's API endpoint, you receive a token in return. You would then redirect the buyer, and append the token to the following URL: https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-XXXXXXX
Once the buyer has agreed to your purchase, he is redirected back to the URL you specified in the RETURNURL.
You should now show the order confirmation, and call the GetExpressCheckoutDetails API**.
When calling GetExpressCheckoutDetails, supply the token. In the GetExpressCheckoutDetails API response you'll find a PayerID.
Now you're ready to call DoExpressCheckoutPayment, and charge the buyer. Remember to include both the token and the payerID when calling DoExpressCheckoutPayment.
With regards to IPN: You don't really need it anymore, as you'll also get the TransactionID back in the API response to DoExpressCheckoutPayment.
IPN would be useful if you would subsequently want to 'keep track' of the transaction. E.g., get notified in case of any refunds / chargebacks, etc.
This simply requires setting up an IPN script and including NOTIFYURL=http://.... in both SetExpressCheckout and DoExpressCheckoutPayment.
** The PayerID is appended in the GET of your RETURNURL as well. So you could skip calling GetExpressCheckoutDetails if you wanted to.
(Partial copy of my answer at Why is DoExpressCheckoutPayment required for Paypal? )