How do I integrate PayPal Smart Button in Laravel 8? - php

I am trying to integrate PayPal Smart buttons in my Laravel 8 Website. These are the documentation I am using:
https://developer.paypal.com/docs/checkout/integrate/
https://developer.paypal.com/docs/checkout/reference/server-integration/set-up-transaction/
https://github.com/paypal/Checkout-PHP-SDK
Issue: I get two errors:
This error underlines the fetch in my front-end.
cart:278 POST http://127.0.0.1:8000/createOrder 500 (Internal Server Error)
{err: "Error: Expected an order id to be passed↵
Here is my route file:
use App\Http\Controllers\PayPalController;
Route::post('/createOrder', [PayPalController::class, 'createOrder']);
Here is my PayPalController Method:
<?php
namespace App\Http\Controllers;
// To capture payment for paypal
namespace Sample\CaptureIntentExamples;
// Grabs the environment and request to be used for paypal
use Sample\PayPalClient;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
// Request/Response
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class PayPalController extends Controller
{
public function createOrder() {
$request = new OrdersCreateRequest();
$request->prefer('return=representation');
$request->body = [
"intent" => "CAPTURE",
"purchase_units" => [[
"reference_id" => "test_ref_id1",
"amount" => [
"value" => "100.00",
"currency_code" => "USD"
]
]],
"application_context" => [
"cancel_url" => "https://127.0.0.1:8000/cart",
"return_url" => "https://127.0.0.1:8000/"
]
];
// 3. Call PayPal to set up a transaction
$client = PayPalClient::client();
$response = $client->execute($request);
// 4. Return a successful response to the client.
return $response;
}
}
Here is my front-end .blade.view file:
<script src="https://www.paypal.com/sdk/js?client-id=*******************"></script>
<form action="/createOrder" method="POST">
#csrf
<div id="paypal-button-container"></div>
<script>
paypal.Buttons({
createOrder: function() {
return fetch('/createOrder', {
method: 'POST',
headers: {
'content-type': 'application/json',
'Accept': 'application/json',
'url': '/createOrder',
"X-CSRF-Token": document.querySelector('input[name=_token]').value
},
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.orderID; // Use the same key name for order ID on the client and server
});
}
}).render('#paypal-button-container');
</script>
</form>
I have edited the PayPalClient.php file with my credentials.

A 500 error within your /createOrder route is something you'll need to diagnose/debug internally. Try loading the URL in a browser and see if you get any more error output from Laravel. If you don't, edit the corresponding PHP to output more useful information about what's going on.
Once you sort that out and are returning an actual id as part of a JSON object, you need to change your front end code to read the key of that id. It looks like you are reading the key orderID, which won't be set unless you specifically set it.
The following front-end demo code is better, and has error handling: https://developer.paypal.com/demo/checkout/#/pattern/server
This part of your front-end code:
<form action="/createOrder" method="POST">
Makes absolutely no sense, unless it's just for testing purposes, but still really doesn't make sense.
All you should have is a container <div> with the id paypal-button-container, for the JS to render its own button in. That button does not in any way use or work with an HTML form.

Related

Stripe avoid paymentintent with status 'Incomplete'

I am using Stripe's 'Payment' element for creating paymentintent and card charges, Using PHP, HTML & JS.
https://stripe.com/docs/payments/payment-element
a) As soon as i load the payment page, Stripe generates a paymentintent with status 'Incomplete'.
b) After i enter Credit Card details and hit 'pay', Stripe again issues a second paymentintent with status accordingly (say 'Succeeded')
The result is, that my Dashboard is now full of unnecessary records
This is, because i initialize the $paymentIntent = \Stripe\PaymentIntent::create as soon as the page loads.
I understand that this is Stripe default behaviour, because at that moment, no payment_method is attached yet.
My question is: How is this resolved best, to avoid such 'Incomplete' records?
Maybe attach an object and fire the paymentintent creation only when
that object is present?
Or onclick of the 'pay' button..awaiting paymentintent, confirm presence and then submit the form?
Or retrieve that 1st paymentintent, store it and then update with
form submission?
create.php
header('Content-Type: application/json');
try {
// retrieve JSON from POST body
$jsonStr = file_get_contents('php://input');
$jsonObj = json_decode($jsonStr);
// Create a PaymentIntent with amount and currency
$paymentIntent = \Stripe\PaymentIntent::create([
'amount' => 1000,
'currency' => 'eur',
'receipt_email' => 'whatever#mail.com',
'automatic_payment_methods' => ['enabled' => true,],
'description' => 'Reservation Dimi123 / Name: John Doe',
'metadata' => ['order_id' => '12345']
]);
$output = [
'clientSecret' => $paymentIntent->client_secret,
];
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
script.js
const stripe = Stripe("pk_test_..");
let elements;
initialize();
checkStatus();
document
.querySelector("#payment-form")
.addEventListener("submit", handleSubmit);
// Fetches a payment intent and captures the client secret
async function initialize() {
const { clientSecret } = await fetch("../create.php", {
method: "POST",
headers: { "Content-Type": "application/json" },
}).then((r) => r.json());
elements = stripe.elements({ clientSecret });
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-element");
}
async function handleSubmit(e) {
e.preventDefault();
setLoading(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: "https://localhost/stripe/prebuilt-checkout-custom-flow/public/checkout.html",
},
});
if (error.type === "card_error" || error.type === "validation_error") {
showMessage(error.message);
} else {
showMessage("An unexpected error occurred.");
}
setLoading(false);
}
How to Avoid Incomplete Payment Intents? You don't.
his is how the system is designed to function since you need the client_secret from the Payment Intent (or Setup Intent) to render the Payment Element. There is no negative impact to your account.
You should feel comfortable ignoring them. You can adjust the filters in your Payments tab to not see them. They aren't a great metric of individual customers coming to your payment interface and navigating away since any user refreshing their browser would trigger a new one.

Calling a PHP method inside a controller with Ajax (Laravel)

I have a Controller in my Laravel project called Clientecontroller, it works perfectly. Inside it, I have a method called listar() who brings me client's information.
public function listar(Cliente $cliente) {
$clientes = DB::table('clientes')
->where('documento_id', 1)
->first();
return $clientes;
}
Sure it has some troubles but my main question is, how I call this listar() function from a view with Angular or Ajax or whatever could work.
I am working in a selling system and I have to bring the client information before selecting anything else. I want to write the ID number from the clients in my view and bring the client information from my controller without reloading. But I am still stuck in the processing reaching the listar() function.
Thank you very much.
in your routes.php file add
Route::post('/cliente', 'Clientecontroller#listar');
And now use your ajax call in order to send data to /cliente the data will be sent through to your listar method in the ClienteController.
$.ajax({
type: "POST",
url: '/cliente',
data: { id: 7 }
}).done(function( msg ) {
alert( msg );
});
This question was answered, for more details head over here
1. The classical HTML approach
Let's say you have a button on your page :
<button id="call-listar">Call !</button>
You could send an HTTP Request to your Laravel application like that :
document.querySelector('#call-listar').addEventListener('click', (e) => {
// Use the fetch() API to send an HTTP Request :
fetch('/the-url-of-listar-controller')
.then(response => response.json())
.then(json => {
// Do what you want to do with the JSON
});
});
đź“– You can find a very usefull documentation about the fetch() API here : https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
2. Inside an Angular Component
This is an other story here, let's say you have this button in your HTML Template :
<button (click)="callListar()">Call !</button>
Inside your TypeScript, you could use HttpClientModule to send an HTTP Request to your Laravel App :
class MyComponent {
constructor(private http: HttpClient){}
callListar() {
this.http.get('/url-of-listar-controller')
.subscribe(response => {
// Do what you want with the response
});
}
}
WARNING : HttpClientModule needed !
You must import the HttpClientModule inside your AppModule or any other module of your Angular App where you want to use this component :
import { HttpClientModule } from '#angular/common/http';
#NgModule({
declarations: [...],
imports: [HttpClientModule]
})

How can I set amount field to PayPal SDK

I am working on PayPal express and want to send the amount field value to the server file (payment.php)
from the client-side script and also want to gets its value to the server-side file.
Here is client-side script
<script src="https://www.paypal.com/sdk/js?client-id=<client-id>"></script>
<script>
paypal.Buttons({
createOrder: function() {
return fetch('payment.php', {
method: 'post',
headers: {
'content-type': 'application/json'
},
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.id;
});
},
onApprove: function(data, actions) {
// This function captures the funds from the transaction.
return actions.order.capture().then(function(details) {
// This function shows a transaction success message to your buyer.
window.location = "paypal-transaction-complete.php?&orderID="+data.orderID;
});
}
}).render('#paypal-button-container');
</script>
<div id="paypal-button-container"></div>
Here is the payment.php where I want to get the amount field value
<?php
namespace Sample\CaptureIntentExamples;
require __DIR__ . '/vendor/autoload.php';
//1. Import the PayPal SDK client that was created in `Set up Server-Side SDK`.
use Sample\PayPalClient;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
require 'paypal-client.php';
class CreateOrder
{
public static function createOrder($debug=false)
{
$request = new OrdersCreateRequest();
$request->prefer('return=representation');
$request->body = self::buildRequestBody();
// 3. Call PayPal to set up a transaction
$client = PayPalClient::client();
$response = $client->execute($request);
echo json_encode($response->result, JSON_PRETTY_PRINT);
return $response;
}
private static function buildRequestBody()
{
return array(
'intent' => 'CAPTURE',
'application_context' =>
array(
'return_url' => 'https://example.com/return',
'cancel_url' => 'https://example.com/cancel'
),
'purchase_units' =>
array(
0 =>
array(
'amount' =>
array(
'currency_code' => 'USD',
'value' => '20.00'
)
)
)
);
}
}
if (!count(debug_backtrace()))
{
CreateOrder::createOrder(true);
}
?>
Please let me know how can I do that? Thanks!
With PHP the simplest way is going to be to use a GET string.
return fetch('payment.php?amount=' + document.getElementById('amount').value , {
^ You might want to validate or sanitize that input or URL encode the value, but for the most part it should work.
array(
'currency_code' => 'USD',
'value' => $_GET['amount']
)
It's not modern but that's PHP for you, parsing JSON is probably overkill.
You shouldn't be using actions.order.capture() when creating on the server, I'd be surprised if that even works. Implement a capture order route on your server, and use this JS sample that has proper error handling: https://developer.paypal.com/demo/checkout/#/pattern/server

Send Json from PHP to React Native

So I have a josn object which has an array of objects which I want to send to a react native app through https but the problem is that I get null in react native
The code of the php :
<?php
class Product {
// Properties
public $title;
public $price;
}
header('Content-Type: application/json');
$ProductList =array();
$aa=$a->{'shopping_results'};
foreach($aa as $y => $y_value) {
$product = new Product();
$product->{'title'} = $y_value ->{'title'};
$product->{'price'} = $y_value ->{'price'};
array_push($ProductList,$product);
}
echo $x=json_encode(array('listx' => $ProductList),JSON_UNESCAPED_UNICODE);// the JSON_UNESCAPED_UNICODE for the Arabic letters
?>
When I try to view the content of this json on the browser this is what I get
https://i.stack.imgur.com/gXT4X.png
The react native code
await fetch(URL, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
// , body: JSON.stringify({ name: "tea" })
})
.then((response) => response.text()) //tried .json() got JSON Parse error: Unexpected EOF
.then((responseJson) => {
console.log(responseJson);//This prints blank
console.log("hi");
this.setState({ output: responseJson });//nothing shows
})
.catch((error) => {
console.error(error);
});
Note: I tried to receive a text from HTTPs request and it worked (The connection is fine)
You need to set HTTP headers, methods in your PHP code so as to accept requests from your react native app (basically I'm telling you to implement REST APIs). If already implemented, just make sure you are giving the correct endpoint in your react-native's fetch URL. And one more thing, when you are trying to retrieve data from the server make sure to set method: 'GET'.
If you're a beginner/ don't have prior knowledge about REST APIs, then here's a reference for you : https://www.positronx.io/create-simple-php-crud-rest-api-with-mysql-php-pdo/ I'm sure it'll give you some basic idea about your need.

How to send a request from angular front end to laravel API?

I'm building a RESTful API with Laravel 5.2 and I have an AngularJS 1.5 front end. I am successfully writing services to get information but I am having troubble putting or posting anything to the database when I pass it to the API. I've tried doing some searching and but I just don't understand how to actually save data I would send the API. Here is my attempt so far:
-Service from the Factory-
addReceipt: function(request) {
return $http.post(api_url + "/rewards/receipts/add", request).then(function(results) {
console.log(results);
return results.data;
});
}
-From the Controller
$scope.submitReceipt = function() {
rewardsFactory.addReceipt($scope.model).then(function() {
console.log($scope.model);
toaster.pop({ type: 'success', title: 'Claim Submitted!', body: "Thanks! We'll take a look at your claim shortly.", showCloseButton: true });
});
};
-From Laravel API routes
Route::post('rewards/receipts/add', 'Rewards\RewardsController#addReceipt');
-From Laravel Controller
public function addReceipt(Request $request)
{
//Add the Receipt
DB::table('receipts')->insert(
['transaction_id' => $request->input('transactionID'),
'client_id' => $request->input('client_id'),
'location_id' => $request->input('location_id') ]
);
}
My Current Cors setup seems to be working out well enough for at least some traffic so I don't think that is the problem but I'm just still not sure what I'm doing wrong.
Note that $http does not send form encoded data by default, it sends application/json in request body.
I don't do any work with laravel but if you check $_POST you will see it is empty so $request->input is probably empty also.
In php you can access the response body using :
json_decode(file_get_contents('php://input')[,true/*optional to convert to array*/])
I believe that json_decode($request->getContent()) will do the same in laravel
The alternative is to use the following $http set up taken from the docs to send form encoded data
.controller(function($http, $httpParamSerializerJQLike) {
//...
$http({
url: myUrl,
method: 'POST',
data: $httpParamSerializerJQLike(myData),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
});
You can also set $http.defaults in a run block so all post or put are sent as x-www-form-urlencoded and not have to add the config to each use

Categories