I am developing a ecommerce site for a friend and in the process of updating Paypal with the shipping amount prior to executing the payment, and I get the following error. This error is occuring when I call on Patch, PatchRequest, and then try executing the payment. Here is all of the code:
if (Input::get('action', 'get') === "getDetails") { //Check to see if the action parameter is set to getDetails
$payment = \PayPal\Api\Payment::get(Input::get('paymentId', 'get'), $paypalAPI);
$payerInfo = $payment->getPayer()->payer_info;
if (!empty($payment)){
$quantity = 0;
foreach ($payment->transactions[0]->item_list->items as $item) {
$quantity += $item->quantity;
}
if ($quantity <= 20) {
$parcelType = "MediumFlatRateBox";
} else if ($quantity > 20) {
$parcelType = "LargeFlatRateBox";
}
$shipment = \EasyPost\Shipment::create([
'from_address' => \EasyPost\Address::retrieve(Config::get('easypost/addressObjectID')),
'to_address' => [
'name' => $payerInfo->shipping_address->recipient_name,
'street1'=> $payerInfo->shipping_address->line1,
'street2' => (isset($payerInfo->shipping_address->line2)) ? $payerInfo->shipping_address->line2 : null,
'city' =>$payerInfo->shipping_address->city,
'state' => $payerInfo->shipping_address->state,
'country' => $payerInfo->shipping_address->country_code,
'zip' => $payerInfo->shipping_address->postal_code,
'email' => $payerInfo->email
],
'parcel' => [
'predefined_package' => $parcelType,
'weight' => 520
]
]);
//Grab the lowest shipping rate
$shippingRate = $shipment->lowest_rate()->rate;
//Make a call to PayPal updating our transaction with the tax and shipping rate
$amount = $payment->transactions[0]->amount;
$transactionUpdate = new \PayPal\Api\Patch();
$transactionUpdate ->setOp('replace')
->setPath('transactions/0/amount')
->setValue(json_decode('{
"total": "'.$amount->total.'",
"currency":"USD",
"detail": {
"subtotal": "'.$amount->details->subtotal.'",
"shipping":"'.$shippingRate.'"
}
}'));
//Instantiate a new instance of PayPal's Patch Request Class and Update the Transaction with the tax and shipping rate
$updateRequest = new \PayPal\Api\PatchRequest();
$updateRequest->setPatches(
[$transactionUpdate]
);
//Attempt Update
$result = $payment->update($updateRequest, $paypalAPI);
if ($result) {
$transID = generateTransactionID();
$fields = [
'dateCreated' => strtotime($payment->create_time),
'transID' => $transID,
'paymentID' => $payment->id,
'shipmentID' => $shipment->id
];
$db->insert('transactionLog', $fields);
Redirect::to('shoppingCartFinalize.php?transID='.$transID);
exit();
} else {
Alert::set([
'header' => "Uh Oh....Something Went Wrong",
'message' => "Unable to update transaction. Transaction Cancelled. Please try again.",
'type' => 'danger',
'close' => 1
]);
Redirect::to('shoppingCartView.php');
exit();
}
}
THe following is the error i get during the call:
Fatal error: Uncaught exception
'PayPal\Exception\PayPalConnectionException' with message 'Got Http
response code 400 when accessing
https://api.sandbox.paypal.com/v1/payments/payment/PAY-77N347011V970714EKU2D24Q.'
in
/var/www/html/myla-dev/vendor/paypal/rest-api-sdk-php/lib/PayPal/Core/PayPalHttpConnection.php:177
Stack trace: #0
/var/www/html/myla-dev/vendor/paypal/rest-api-sdk-php/lib/PayPal/Transport/PayPalRestCall.php(74):
PayPal\Core\PayPalHttpConnection->execute('[{"op":"replace...') #1
/var/www/html/myla-dev/vendor/paypal/rest-api-sdk-php/lib/PayPal/Common/PayPalResourceModel.php(103):
PayPal\Transport\PayPalRestCall->execute(Array, '/v1/payments/pa...',
'PATCH', '[{"op":"replace...', NULL) #2
/var/www/html/myla-dev/vendor/paypal/rest-api-sdk-php/lib/PayPal/Api/Payment.php(474):
PayPal\Common\PayPalResourceModel::executeCall('/v1/payments/pa...',
'PATCH', '[{"op":"replace...', NULL, Object(PayPal\Rest\ApiContext),
NULL) #3 /var/www/html/myla-dev/apiProcessing.php(146):
PayPal\Api\Payment->update(Object(PayPal\Api\ in
/var/www/html/myla-dev/vendor/paypal/rest-api-sdk-php/lib/PayPal/Core/PayPalHttpConnection.php
on line 177
UPDATE: Thank you for the assistance Thaer. It helped. Now I am getting a separate error. When I attempt to update the payment it is now saying the following:
Array (
[name] => PAYMENT_STATE_INVALID
[message] => This request is invalid due to the current state of the payment
[information_link] => https://developer.paypal.com/webapps/developer/docs/api/#PAYMENT_STATE_INVALID
[debug_id] => 1b1b9b71e91d8 )
If you know how to fix this please. I don't know how to change the state so that the payment can be updated.
If you find an error please let me know. I am so stuck it is not funny.
Dave Douglas
Please refer to this Answer
I think you must try - catch your code to determine error
,regards.
First, can you confirm if the payment is already executed or not ?
Note that it can only be updated before the execute is done. Once, the payment is executed it is not possible to udpate that. Docs: https://developer.paypal.com/webapps/developer/docs/api/#update-a-payment-resource
You can follow the code shown here to update the Payment here : http://paypal.github.io/PayPal-PHP-SDK/sample/doc/payments/UpdatePayment.html
And I think your payment has already been executed, and so the state would be complete and not created, which is causing that 400 Exception of invalid state.
Let me know if that makes sense.
To make a change in the payment, you might be able to create a refund, to return the remaining amount.
Alternatively, you could also use authorize or order, to hold the funds first, and then actually charge them later. Kind of how you see it in restaurants, etc, where they hold the fund of your total, but eventually add the tip later, and complete the transaction later.
For more information read here: https://developer.paypal.com/webapps/developer/docs/integration/direct/create-process-order/
Related
I tried to make the payment online for my site, I work with stripe, the payment is done with success, but I add CardErrorException to handle special error messages, when I put the code 4000 0000 0000 0069 to handle the expired_card exception it normal pass without handling the "You card has expired" exception.
CheckoutController.php
public function store(Request $request)
{
$contents = Cart::content()->map(function ($item) {
return $item->model->name.', '.$item->qty;
})->values()->toJson();
try {
// Enter Your Stripe Secret
\Stripe\Stripe::setApiKey('sk_test_PcRh9XreG5jbXyhCchJf9NCK00dku1xYGi');
$payment_intent = \Stripe\PaymentIntent::create([
'amount' => round(Cart::total() / 100),
'currency' => 'MAD',
'description' => 'Stripe Test Payment ddd',
'receipt_email' => $request->email,
'payment_method_types' => ['card'],
'metadata' => [
'content' => $contents,
'quantity' => Cart::instance('default')->count(),
]
]);
$intent = $payment_intent->client_secret;
Cart::instance('default')->destroy();
return redirect()->route('confirmation.index')->with('success_message', 'Thank you! Your payment has been successfully accepted!');
} catch (CardErrorException $e) {
return back()->withErrors('Error! ' . $e->getMessage());
}
}
Your code is successfully creating a Payment Intent, but you're not confirming it. Payment is not attempted until the Payment Intent gets confirmed.
The special test card number you're using that returns an expired_card decline does not trigger until payment is attempted. That's why this code is not working as expected.
You likely want to add 'confirm' => 'true' to your arguments (assuming you want to confirm the Payment Intent server-side) or use Stripe.js to confirm the Payment Intent client-side (recommended).
So I'm building an webapp that has a shop with Laravel API and Vue as the frontend SPA.
I've been trying to use Strip to enable payments. So far, with the help of Stripe's documentation, I have been able to create a Source in the frontend. for iDEAL, Stripe highly suggests us to make use of webhooks to confirm whether a payment has succeeded. (I'm using Spatie/Laravel-Stripe-Webhook package) This is the current flow of my webapp:
Checkout.vue:
checkout() {
const sourceData = {
type: 'ideal',
amount: this.cart.total,
currency: 'eur',
owner: {
name: this.name + ' ' + this.last_name,
email: this.email,
},
metadata: {
order: JSON.stringify(order),
total_quantity: this.cart.total_quantity,
},
redirect: {
return_url: 'http://example.test/order-confirmation',
},
}
this.stripe.createSource(this.ideal, sourceData).then(function(result) {
if (result.error) {
console.log(error.message)
this.error = error.message
} else {
stripeSourceHandler(result.source)
}
})
const stripeSourceHandler = source => {
document.location.href = source.redirect.url
}
},
After filling in billing address, emails etc. the user starts the payment.
User gets redirected to iDEAL payment page where they can authorize payment.
The Source is now created. Stripe sends source.chargeable webhook:
config/stripe-webhooks.php:
'jobs' => [
'source_chargeable' => \App\Jobs\StripeWebhooks\ProcessPaymentsJob::class,
'charge_succeeded' => \App\Jobs\StripeWebhooks\ChargeSucceededJob::class,
],
ProcessPaymentsJob.php:
public function __construct(WebhookCall $webhookCall)
{
$this->webhookCall = $webhookCall;
}
public function handle()
{
$charge = $this->webhookCall->payload['data']['object'];
\Stripe\Stripe::setApiKey(config('services.stripe.secret'));
$user = '';
if(User::find(Auth::id())) {
$user = $user->name;
} else {
$user = 'a guest';
}
$payment = \Stripe\Charge::create([
'amount' => $charge['amount'],
'currency' => 'eur',
'source' => $charge['id'],
'description' => 'New payment from '. $user,
'metadata' => [
'order' => $charge['metadata']['order'],
'total_quantity' => $charge['metadata']['total_quantity'],
]
]);
}
User returns to redirect[return_url]
If all went well, Stripe should send charge.succeeded webhook:
ChargeSucceededJob.php:
public function __construct(WebhookCall $webhookCall)
{
$this->webhookCall = $webhookCall;
}
public function handle()
{
$charge = $this->webhookCall->payload['data']['object'];
$order = Order::create([
'user_id' => Auth::id() ?? null,
'payment_id' => $charge['id'],
'payment_method' => $charge['payment_method_details']['type'],
'billing_email' => $charge['billing_details']['email'],
'billing_name' => $charge['metadata']['name'],
'billing_last_name' => $charge['metadata']['last_name'],
'billing_address' => $charge['metadata']['address'],
'billing_address_number' => $charge['metadata']['address_num'],
'billing_postal_code' => $charge['metadata']['postal_code'],
'billing_city' => $charge['metadata']['city'],
'billing_phone' => strval($charge['billing_details']['phone']),
'order' => json_decode($charge['metadata']['order']),
'total_quantity' => (int) $charge['metadata']['total_quantity'],
'billing_total' => $charge['amount'],
]);
}
This is all going well. However, I do not know how to notify the customer (on the frontend) that the order has been completed. In Stripe's documentation, they explain how to retrieve the Source on the order confirmation page, but they do not explain how to retrieve the Charge, because this is what determines whether the whole order has been completed or not.
OrderConfirmation.vue:
checkPaymentStatus() {
this.stripe = Stripe(this.stripeKey)
// After some amount of time, we should stop trying to resolve the order synchronously:
const MAX_POLL_COUNT = 10;
let pollCount = 0;
let params = new URLSearchParams(location.search)
const pollForSourceStatus = async () => {
const { source } = await this.stripe.retrieveSource({id: params.get('source'), client_secret: params.get('client_secret')})
if (source.status === 'chargeable') {
// Make a request to your server to charge the Source.
// Depending on the Charge status, show your customer the relevant message.
} else if (source.status === 'pending' && pollCount < MAX_POLL_COUNT) {
// Try again in a second, if the Source is still `pending`:
pollCount += 1;
setTimeout(pollForSourceStatus, 1000);
} else {
// Depending on the Source status, show your customer the relevant message.
}
};
pollForSourceStatus();
}
How do I go from here? I am trying to notify the frontend when the Charge has been succeeded. My initial thought process was just to return the Order object, as I would do if it was a Controller, but if I understand correctly, the Job is running asynchronously, so I can't return data. I am also new to Jobs and Queues and stuff, I'm still trying to wrap my head around with it.
Another option I thought of is that I would poll requests from the frontend to the backend to request the last Order, but I have no idea how this would work and/or if this is a good solution.
Any help/tips/helpful resources would be much appreciated!
iDEAL payments are asynchronous, but they luckily do immediately notify you if the payment was successful or not.
When the iDEAL process is complete and your user is redirected to your site, Stripe automatically appends some query parameters to the URL. Meaning your users will be redirected to something like:
https://example.com/checkout/complete?payment_intent=pi_123&payment_intent_client_secret=pi_123_secret_456&source_type=ideal
The next step is to then retrieve the PaymentIntent and check on its status, which you can do by either:
Retrieving the PaymentIntent on the client using stripe.js and the PaymentIntent client secret: https://stripe.com/docs/js/payment_intents/retrieve_payment_intent
Retrieving the PaymentIntent on the server by sending an ajax request to your backend with the PaymentIntend ID: https://stripe.com/docs/api/payment_intents/retrieve
If the status is succeeded, then the payment was completed and you can proceed from there.
Description
I'm getting an error when attempting to create a stripe subscription using Laravel + the API.
Before you create the subscription you must get the token by requesting it, I have successfully created this token and I'm now using the "createSubscription()" method from the API (referenced in my code), but this is where the error is occurring.
Code
public function create()
{
$user = Auth::user();
$plan = 'prod_**********';
// Do some checks
if ($user->subscribed('main')){
return [
'status' => 'failed',
'message' => 'You are already subscribed!',
];
}
// Set the stripe Key
Stripe::setApiKey(env('STRIPE_SECRET'));
// Create the stripe token
try {
$stripeToken = Token::create([
'card' => [
'number' => str_replace(' ', '', Input::get('number')),
'exp_month' => Input::get('exp_month'),
'exp_year' => Input::get('exp_year'),
'cvc' => Input::get('cvc')
]
]);
}
catch (\Stripe\Error\InvalidRequest $e)
{
return [
'status' => 'failed',
'message' => $e->getMessage(),
];
}
try{
// This is the line thats failing
$user->newSubscription('main', $plan)->create($stripeToken);
} catch (\Stripe\Error\InvalidRequest $e) {
dd($e->getMessage());
}
return [
'status' => 'success',
'message' => 'Subscription was successful!',
];
}
The Error
The error in full is:
Invalid request. Hint: check the encoding for your request parameters
and URL (http://en.wikipedia.org/wiki/percent-encoding). For
assistance, email support#stripe.com.
I've spoken to stripe support and they're saying they think the error is on my end, everything seems good on the stripe end.
I've checked the card detailed and customer details are correct (This is why the token being created).
I've searched this error but nothing seems to come up for this, there are other questions somewhat similar but no answers.
I've uninstalled / reinstalled laravel-cashier package to no success.
Whats strange is how i've managed to solve this problem. It would seem that passing the entire stripe token does not work and instead I only needed to pass the token ID.
Simply changing
$user->newSubscription('main', $plan)->create($stripeToken);
to this
$user->newSubscription('main', $plan)->create($stripeToken->id);
Solved this error
Invalid request. Hint: check the encoding for your request parameters
and URL (http://en.wikipedia.org/wiki/percent-encoding). For
assistance, email support#stripe.com.
I'm sure that nowhere in either documentation is states that this is the solution? Or maybe I overlooked this somewhere... but this has solved it for me.
I am trying to use Stripe Connect on my website. I created connected account and customers but have a mistake while trying to share a customer to the connected account.
I get this :
"You provided a customer without specifying a source. The default source of the customer is a source and cannot be shared from existing customers."
Hier is the code I am using :
function addSource($source){
$this->source = $source;
}
function addCustomer(){
$customer = \Stripe\Customer::create(array(
"description" => "Customer ".$this->getCustomerName(),
"email" => $this->getCustomerEmail(),
"source" => $this->source
));
$this->customer = $customer;
}
function createAccount(){
$account = \Stripe\Account::create(array(
"country" => "FR",
"type" => "custom"
));
$this->account = $account->id;
}
function connectCustomer(){
$token = \Stripe\Token::create(array(
"customer" => $this->customer->id
), array("stripe_account" => $this->account));
$copiedCustomer = \Stripe\Customer::create(array(
"description" => "Customer for xxx#xxx.com",
"source" => $token->id
), array("stripe_account" => $this->account));
$this->copiedCustomer = $copiedCustomer;
}
By debugging I saw that the problem happend when I try to create $token in the connectCustomer function. The customer is well added on my Stripe Dashboard with a correct source. The account is also created.
My goal after that is to subscribe the customer to the connected account. I have succeed to subscribe him without using Stripe Connect but now I need to use it.
I tried to find a solution in many other forum but did not find anything similar.
Thanks for any help !
I know it's already too late, but here is something worked for me. I have had the same error. I have solved it by creating source instead of token.
So, replace
$token = \Stripe\Token::create(array(
"customer" => $this->customer->id
), array("stripe_account" => $this->account));
with
$token = \Stripe\Source::create([
"customer" => $this->customer->id,
], ["stripe_account" => $this->account]);
Your code looks fine to me. I'm not sure why it did not work. Maybe some steps are missing? Ran into the same issue while creating connect subscription because I was trying to associate platform customer with connected plan. The error message was not very helpful. I search google with the message and came upon this question. C'mon, I was able to use the platform customer to create one-time connected charge fine (after I create a shared source). Why can't I do the same here? Because Subscription API required customer and not a shared source. Stripe demo code wasn't of much help until I carefully read the first 4 bulleted points in the documentation here: https://stripe.com/docs/connect/subscriptions (an Ahhhhah moment!)
Using subscriptions with Connect has these restrictions:
Both the customer and the plan must be created on the connected
account (not your platform account)
Subscriptions must be created directly on the connected account (using the destination parameter is not supported)
Your platform can’t update or cancel a subscription it did not create
Your platform can’t add an application_fee to an invoice that it didn’t create or that contains invoice items it didn’t create
So, I'm just going to post some pseudo code in hoping it will help the next person who came upon this question with above error message.
You have to create a source (not token) to be reuse from the front-end/client-side javascript:
stripe.createSource(card, ownerInfo)
Then you would use this source (stripe_token) to create a customer on the platform account (stripe_customer_id). This can be useful with one-time connected charge (if you have a need for it). This is also to store the original source (stripe_token) so you can create a new re-usable token/source later for the connected/txn_customer_id.
From step 3 on, all the codes are inside of chargeMonthly function below:
Make sure the subscription plan is a connected plan created by the platform by providing a unique name (3rd bullet point above).
Next, create a new re-useable source with the platform/stripe_customer_id.
Use the new re-usable source to create a customer (txn_customer_id) on the connected account.
Create your subscription with the connected/txn_customer_id and connected plan_id.
public function createNewCustomer(&$input, $forConnect = false) {
try {
// Create new stripe customer
if ($forConnect) {
$cu = \Stripe\Customer::create(array(
'email' => $input['email'],
'source' => $input['connect_token']
), array("stripe_account" => $input['txn_account_id']));
$input['txn_customer_id'] = $cu->id;
}
else {
$cu = \Stripe\Customer::create(array(
'email' => $input['email'],
'source' => $input['stripe_token']
));
$input['stripe_customer_id'] = $cu->id;
$input['txn_customer_id'] = $cu->id;
}
} catch (\Stripe\Error\Base $e1) {
// log error
\Log::error($e1);
return false;
} catch(\Stripe\Error\Card $e2) {
\Log::error($e2);
return false;
} catch (Exception $e) {
\Log::error($e);
return false;
}
return true;
}
public function chargeMonthly(&$input, $qty = 1) {
$plan_name = 'yourplatformname-' . $input['amount'] .'-' . $input['banner_slug'];
// attempt to retrieve monthly plan
// if not found, create new plan
try {
$plan = \Stripe\Plan::retrieve($plan_name, array("stripe_account" => $input['txn_account_id']));
} catch(\Stripe\Error\InvalidRequest $e1) {
// ignore error
// \Log::error($e1);
} catch(Exception $e) {
// ignore error
// \Log::error($e);
}
try {
// create new if not found
if(empty($plan)) {
$plan = \Stripe\Plan::create(array(
'amount' => $input['amount'],
'interval' => 'month',
'currency' => 'usd',
'id' => $plan_name,
"product" => array(
"name" => $plan_name
)
), array("stripe_account" => $input['txn_account_id']));
}
$token = \Stripe\Source::create(array(
'customer' => $input['stripe_customer_id'],
'usage' => 'reusable'
), array("stripe_account" => $input['txn_account_id']));
$input['connect_token'] = $token->id;
$this->createNewCustomer($input, true);
$sub = \Stripe\Subscription::create(array(
'customer' => $input['txn_customer_id'],
'plan' => $plan->id,
'quantity' => $qty,
'application_fee_percent' => $input['application_fee_percent']),
array('stripe_account' => $input['txn_account_id'])
);
$input['txn_id'] = $sub->id;
$input['txn_log'] = json_encode($sub);
$input['recurrence_name'] = $plan->id;
// success
return true;
} catch(\Stripe\Error\InvalidRequest $e1) {
// ignore error
\Log::error($e1);
return false;
} catch(Exception $e) {
\Log::error($e);
return false;
}
}
The response i keep getting at dd($finalResponse); is:
RestResponse {#298 ▼
#statusCode: 400
#request: RestCompletePurchaseRequest {#300 ▶}
#data: array:4 [▼
"name" => "PAYMENT_NOT_APPROVED_FOR_EXECUTION"
"message" => "Payer has not approved payment"
"information_link" => "https://developer.paypal.com/webapps/developer/docs/api/#PAYMENT_NOT_APPROVED_FOR_EXECUTION"
"debug_id" => "5471589613718"
]
}
Here is the code.
$gateway = Omnipay::create('PayPal_Rest');
// Initialise the gateway
$gateway->initialize(array(
'clientId' => env('PAYMENT_SANDBOX_PAYPAL_CLIENTID'),
'secret' => env('PAYMENT_SANDBOX_PAYPAL_SECRET'),
'testMode' => true, // Or false when you are ready for live transactions
));
// Do an authorisation transaction on the gateway
$transaction = $gateway->authorize(array(
'returnUrl'=> env('PAYMENT_SANDBOX_PAYPAL_URL'),
'cancelUrl' => 'http://localhost:8000/cancel',
'amount' => '10.00',
'currency' => 'AUD',
'description' => 'This is a test authorize transaction.',
// 'card' => $card,
));
$response = $transaction->send();
if ($response->isSuccessful()) {
// Find the authorization ID
$authResponse = $response->getTransactionReference();
echo "Authorize transaction was successful!\n".$authResponse;
}else{
echo "Failed to auth transaction";
dd($response);
}
// Once the transaction has been approved, we need to complete it.
$transaction = $gateway->completePurchase(array(
'payerId' => $request->PayerID,
'transactionReference' => $authResponse
));
$finalResponse = $transaction->send();
dd($finalResponse);
if ($finalResponse->getData()) {
echo "Transaction was successful!\n";
// Find the authorization ID
$results = $finalResponse->getTransactionReference();
dd($results);
}else{
dd($finalResponse->getData());
}
After logging in as the payer and completing purchase, what else does the payer need to approve and how?
No, you aren't understanding the PayPal payment flow correctly. Here is the correct flow:
You do the call to Omnipay::create(), $gateway->initialize() and $gateway->authorize() just like you have them above. However for returnUrl you have to provide a URL on your site, just like you have for cancelUrl. Perhaps you mean to use http://localhost:8000/return (although better would be to have a transaction ID or something in the return URL).
The response from the $gateway->authorize() will be of type RedirectResponse. You can check this:
// Do an authorisation transaction on the gateway
$transaction = $gateway->authorize(array(
'returnUrl'=> env('PAYMENT_SANDBOX_PAYPAL_URL'),
'cancelUrl' => 'http://localhost:8000/cancel',
'amount' => '10.00',
'currency' => 'AUD',
'description' => 'This is a test authorize transaction.',
// 'card' => $card,
));
$response = $transaction->send();
if ($response->isRedirect()) {
// Yes it's a redirect. Redirect the customer to this URL:
$redirectUrl = $response->getRedirectUrl();
}
At that point the initial handshake with the customer is over. You have now redirected the customer to the PayPal web site where they will authorize the transaction by logging in with their PayPal account email address and password, check the invoice, click the button that says that they agree to pay.
The next thing that happens is that the customer is redirected by PayPal back to your web site, on the redirectUrl that you provided in the authorize() call. That will jump to a different place in your code. At that point you call completeAuthorize, just like you had in your code earlier:
// Once the transaction has been approved, we need to complete it.
$transaction = $gateway->completePurchase(array(
'payerId' => $request->PayerID,
'transactionReference' => $authResponse
));
$finalResponse = $transaction->send();
dd($finalResponse);
if ($finalResponse->getData()) {
echo "Transaction was successful!\n";
// Find the authorization ID
$results = $finalResponse->getTransactionReference();
dd($results);
}else{
dd($finalResponse->getData());
}
Note that you need to have stored the Payer ID and transactionReference from the authorize call and be able to recover those in your returnUrl code.
You also need to be able to handle the cancelUrl case, where the customer has decided not to agree to the payment on PayPal and gets sent back to the cancelUrl URL on your website instead.
Finally you need to be able to handle the occasional cases where the customer completes the payment on the PayPal web site but does not end back on your returnUrl. This could be because of a network issue, the browser crashed, or because the customer closed their browser between clicking "Agree to Pay" on PayPal and landing back on your site. The best way to handle those is with the omnipay-paypal calls fetchPurchase() or listPurchase().