I'm building a web app for inventory management. I've got React on the frontend, and Nodejs+mongodb on the backend. Our company vends at local events and most of our sales are paid with cards. To process card payments we use the Paypal Here app on our phones which connects to a card reader and we manually type in the payment amount. Since we have over 200 different products (custom art), we decided to build this application so that we can quickly search for the product(s) being purchased, add them to the "cart" where the total price plus tax will be automatically calculated, and then a total of 3 payment option buttons will be present, one for cash, one for venmo, and one for card. At first, I figured the card selection button could link externally to the Paypal Here app and the payment amount would be automatically filled in when redirected, but then I realized I could actually integrate a Paypalhere sdk in the application, which sounded better than a redirect. There's three different sdks, one for ios, one for android, and one for the web, and the one for the web is what I need. I looked for an npm package, no luck, then I tried manually inserting the script and src into the document via react helment, no luck, on componentDidMount, no luck. I'm not used to not having an npm package to use, so my question today is how can I integrate this sdk into my React app?
Heres a link to the web integration documentation: https://developer.paypal.com/docs/integration/paypal-here/sdk-dev/web/#integration
Heres an the code I used to manually insert the script onComponentDidMount, I don't know if it worked, but even if it did, I don't know how to access it...
useEffect(() => {
const script = document.createElement("script");
script.src = "https://www.paypalobjects.com/pph/websdk/js/pphwebsdk-1.1.14.min.js";
script.async = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
Don't remove the script after adding it.
You can set a callback function to have your code that uses PPH run after the script loads. Here's an example with a callback function, it's for regular PayPal buttons rather than PPH, but you can adapt it to your needs.
function loadAsync(url, callback) {
var s = document.createElement('script');
s.setAttribute('src', url); s.onload = callback;
document.head.insertBefore(s, document.head.firstElementChild);
}
loadAsync('https://www.paypal.com/sdk/js?client-id=sb¤cy=USD', function() {
paypal.Buttons({
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: '0.01'
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name);
});
}
}).render('body');
});
Alternatively, you can just load the SDK statically from in the index <head> of your application, and it'll always be there ready for use.
Related
I am currently developing a small web app using MERN. Here I have developed the backend part and frontend part of it. I want to get the response data category-wise. For example, in my database there are details of assets(Laptops, mobiles etc) with attributes like assetName and assetCategory. There's a functionality to search those assets category-wise. In the frontend, I have used a search bar and user can type the category of asset which is wanted to see. Then backend server gives the response of that. I have developed routes and controllers for the backend part and I will show the controller here.
//find assets by category
exports.assetsByCategory = async(req,res) => {
const CATEGORY = req.body.assetCategory;
const asset = await Asset.find({"assetCategory" :{$regex: new RegExp([CATEGORY?.toLowerCase()], "i") }})
.then((assets)=>{
res.json(assets)
}).catch((err)=>{
console.log(err);
res.status(500).send({message:"No assets like that category!",error:err.message})
})
}
The code for this function which is in the frontend part will be shown here.
function searchCategoryBar(e)
{
e.preventDefault();
const searchedCategory = {assetCategory}
console.log(searchedCategory)
axios.get("http://localhost:8070/assets/category",searchedCategory).then((asset)=>{
setAssets(asset.data);
console.log(asset.data);
}).catch((err)=>{
alert(err.message);
})
}
So that, when I type "lap" on the search bar and then the response must be shown the asset list of laptop category. But in here, response shows all the assets lists without category-wise.
Please give me some help to solve this issue, Thank you!
Is is possible to get the onload event for the pwa application in general. I meant we had implemented the a custom versioning logic in-order to keep the app version based on database field.(ie clearing the service worker cache). The issues here is the logic almost works but when ever a new version is updated in the database, then we need to clear the cache of the respective browser in-order to trigger the update. On more investigation I found that when once the pwa app is opened, it is keeping the some sort of cache image, on reopening the pwa app again won't trigger the start-up code of the app, but load app from cache.
So is it possible to get an onload sort of event for pwa ?
For testing purpose I added some alert() in the app component, but didn't fired, on reopening a pwa app
this.httpService.GetAppVersion(ver).subscribe(
res => {
if (res != null || res !== undefined) {
this.version = res.versionNumber;
ver = localStorage.getItem("appVersion");
if (ver === null || ver === undefined) {
localStorage.setItem("appVersion", "1.0");
ver = "1.0";
}
let localVersion = ver.split(".");
let incomingVersion = this.version.split(".");
let result = this.helperService.compareVersion(
localVersion,
incomingVersion
);
//alert("result : " + result);
if (result === 1) {
const snackBarRef = this.snackBar.open(
"New version available. Load New Version?",
"Yes",
{ duration: 50000000 }
);
snackBarRef.afterDismissed().subscribe(() => {
console.log("The snack-bar was dismissed");
});
snackBarRef.onAction().subscribe(() => {
localStorage.setItem("appVersion", this.version.toString());
this.helperService.Update(); // which clears the cache
setTimeout(() => {
window.location.reload(true);
}, 500);
});
}
}
},
error => {
alert("http error" + JSON.stringify(error));
}
);
at least the code in the app component's constructor will execute every time when the app is reopened after closing.
See: How to display a "new version available" for a Progressive Web App
I know this question is very old, but what I'm doing now (and I'm trying to find a better approach because I don't really like this one) is storing the version on the service worker code.
Then, when the window.onload fires, the main JavaScript code sends a message to the service worker (using postMessage()) and the service worker replies with the version number.
It's not exactly what you need, but it's an approximation.
Still, and as I said, I'm looking for a better, more maintenable approach. If I find one I'll post it here, just in case someone is searching for this (as I did).
I am currently working on a hybrid application (Ionic) and have a problem with detecting iBeacons on iOS (currently developing on 9.2). I'm using cordova-plugin-estimote (https://github.com/evothings/phonegap-estimotebeacons) to detect beacons, followed their documentation and everything works fine on Android, but not on iOS. The beacons are simply not detected. There is no error, it just doesn't find anything.
Fun fact: When I downloaded the original estimote app on iPhone, it also did not detect any iBeacons until I logged in. After that, it started detecting them normally. Why is this happening?
Relevant part of my AngularJS code based on plugin documentation:
$scope.init = function() {
bluetoothSerial.isEnabled(scanBeacons, errorBluetooth);
}
function scanBeacons() {
startScanning();
updateList = $interval(updateView, 5000);
}
function startScanning() {
console.log("requesting permissions");
estimote.beacons.requestAlwaysAuthorization(successAuth, errorAuth);
}
function successAuth(){
console.log("success auth, starting scan");
estimote.beacons.startRangingBeaconsInRegion(
{},
onMonitoringSuccess,
onError);
}
function errorAuth(){
console.log("error auth");
popupService.showPopup("Authorization error", "Location services required to perform scanning");
}
function onMonitoringSuccess(regionState) {
console.log("monitoring success: "+JSON.stringify(regionState));
var successHandler = function (response) {
$scope.downloadedlist = response;
$scope.offline = false;
};
var errorHandler = function (response) {
$scope.beaconList = regionState.beacons;
};
eventService.getBeaconList(regionState.beacons, storageService.getEventId())
.then(successHandler)
.catch(errorHandler);
}
function onError(response) {
console.log("monitoring error: "+JSON.stringify(response));
popupService.showPopup('popup.error.title', 'popup.error.server');
}
As you can see, I have some console.log statements (it has to be done this way) and I'm getting "requesting permissions", instantly followed by "success auth, starting scan". It's weird because authorization popup is displayed but the code does not wait for the user input, it just automatically fires success handler (successAuth) function and that's it. No more logs, which means no monitoring success, no error, it just doesn't find any beacons.
Any help appreciated. Thanks.
Finally found the solution. The problem was here:
estimote.beacons.startRangingBeaconsInRegion(
{},
onMonitoringSuccess,
onError);
Turns out Android allows for {} parameter (which means look for all regions) in the following function but iOS doesn't. After specifying a region, my application successfuly finds the beacons.
Example:
function successAuth(){
console.log("success auth, starting scan");
var region = { uuid: 'YOUR_UUID_HERE' }
estimote.beacons.startRangingBeaconsInRegion(
region,
onMonitoringSuccess,
onError);
}
Estimote Developer quote:
iOS only allows scanning for beacons the UUID of which you know. All Estimote Beacons ship with our default UUID, "B9407F30-F5F8-466E-AFF9-25556B57FE6D", and these are the beacons you can see on the radar even when not logged in. If you change this UUID to something else, then you need to stay logged in so that Estimote app can know what the UUIDs of your beacons are, and scan for them in addition to the default UUID.
Source: https://forums.estimote.com/t/beacons-not-detected-using-estimote-ios-app/1580/5
This also explains why Estimote App started detecting iBeacons after logging in.
I am new to SEO and just want to get the idea about how it works for Single Page Application with dynamic content.
In my case, I have a single page application (powered by AngularJS, using router to show different state) that provides some location-based search functionalities, similar to Zillow, Redfin, or Yelp. On mt site, user can type in a location name, and the site will return some results based on the location.
I am trying to figure out a way to make it work well with Google. For example, if I type in "Apartment San Francisco" in Google, the results will be:
And when user click on these links, the sites will display the correct result. I am thinking about having similar SEO like these for my site.
The question is, the page content is purely depending on user's query. User can search by city name, state name, zip code, etc, to show different results, and it's not possible to put them all into sitemap. How google can crawl the content for these kind of dynamic page results?
I don't have experience with SEO and not sure how to do it for my site. Please share some experience or pointers to help me get started. Thanks a lot!
===========
Follow up question:
I saw Googlebot can now run Javascript. I want to understand a bit more of this. When a specific url of my SPA app is opened, it will do some network query (XHR request) for a few seconds and then the page content will be displayed. In this case, will GoogleBot wait for the http response?
I saw some tutorial says we need to prepare static html specifically for Search Engines. If I only want to deal with Google, does it mean I don't have to serve static html anymore because Google can run Javascript?
Thanks again.
If a search engine should come across your JavaScript application then we have the permission to redirect the search engine to another URL that serves the fully rendered version of the page.
For this job
You can either use this tool by Thomas Davis available on github
SEOSERVER
Or
you can use the code below which does the same job as above this code is also available here
Implementation using Phantom.js
We can setup a node.js server that given a URL, it will fully render the page content. Then we will redirect bots to this server to retrieve the correct content.
We will need to install node.js and phantom.js onto a box. Then start up this server below. There are two files, one which is the web server and the other is a phantomjs script that renders the page.
// web.js
// Express is our web server that can handle request
var express = require('express');
var app = express();
var getContent = function(url, callback) {
var content = '';
// Here we spawn a phantom.js process, the first element of the
// array is our phantomjs script and the second element is our url
var phantom = require('child_process').spawn('phantomjs',['phantom-server.js', url]);
phantom.stdout.setEncoding('utf8');
// Our phantom.js script is simply logging the output and
// we access it here through stdout
phantom.stdout.on('data', function(data) {
content += data.toString();
});
phantom.on('exit', function(code) {
if (code !== 0) {
console.log('We have an error');
} else {
// once our phantom.js script exits, let's call out call back
// which outputs the contents to the page
callback(content);
}
});
};
var respond = function (req, res) {
// Because we use [P] in htaccess we have access to this header
url = 'http://' + req.headers['x-forwarded-host'] + req.params[0];
getContent(url, function (content) {
res.send(content);
});
}
app.get(/(.*)/, respond);
app.listen(3000);
The script below is phantom-server.js and will be in charge of fully rendering the content. We don't return the content until the page is fully rendered. We hook into the resources listener to do this.
var page = require('webpage').create();
var system = require('system');
var lastReceived = new Date().getTime();
var requestCount = 0;
var responseCount = 0;
var requestIds = [];
var startTime = new Date().getTime();
page.onResourceReceived = function (response) {
if(requestIds.indexOf(response.id) !== -1) {
lastReceived = new Date().getTime();
responseCount++;
requestIds[requestIds.indexOf(response.id)] = null;
}
};
page.onResourceRequested = function (request) {
if(requestIds.indexOf(request.id) === -1) {
requestIds.push(request.id);
requestCount++;
}
};
// Open the page
page.open(system.args[1], function () {});
var checkComplete = function () {
// We don't allow it to take longer than 5 seconds but
// don't return until all requests are finished
if((new Date().getTime() - lastReceived > 300 && requestCount === responseCount) || new Date().getTime() - startTime > 5000) {
clearInterval(checkCompleteInterval);
console.log(page.content);
phantom.exit();
}
}
// Let us check to see if the page is finished rendering
var checkCompleteInterval = setInterval(checkComplete, 1);
Once we have this server up and running we just redirect bots to the server in our client's web server configuration.
Redirecting bots
If you are using apache we can edit out .htaccess such that Google requests are proxied to our middle man phantom.js server.
RewriteEngine on
RewriteCond %{QUERY_STRING} ^_escaped_fragment_=(.*)$
RewriteRule (.*) http://webserver:3000/%1? [P]
We could also include other RewriteCond, such as user agent to redirect other search engines we wish to be indexed on.
Though Google won't use _escaped_fragment_ unless we tell it to by either including a meta tag; <meta name="fragment" content="!">or using #! URLs in our links.
You will most likely have to use both.
This has been tested with Google Webmasters fetch tool. Make sure you include #! on your URLs when using the fetch tool.
I am new to dart and I have been trying to figure out how to use the googleapis library to update a calendars events, then display the calendar/events on a webpage.
So far I have this code that I was hoping would just change the #text id's text to a list of events from the selected calendars ID:
import 'dart:html';
import 'package:googleapis/calendar/v3.dart';
import 'package:googleapis_auth/auth_io.dart';
final _credentials = new ServiceAccountCredentials.fromJson(r'''
{
"private_key_id": "myprivatekeyid",
"private_key": "myprivatekey",
"client_email": "myclientemail",
"client_id": "myclientid",
"type": "service_account"
}
''');
const _SCOPES = const [CalendarApi.CalendarScope];
void main() {
clientViaServiceAccount(_credentials, _SCOPES).then((http_client) {
var calendar = new CalendarApi(http_client);
String adminPanelCalendarId = 'mycalendarID';
var event = calendar.events;
var events = event.list(adminPanelCalendarId);
events.then((showEvents) {
querySelector("#text2").text = showEvents.toString();
});
});
}
But nothing displays on the webpage. I think I am misunderstanding how to use client-side and server-side code in dart... Do I break up the file into multiple files? How would I go about updating a calendar and displaying it on a web page with dart?
I'm familiar with the browser package, but this is the first time I have written anything with server-side libraries(googleapis uses dart:io so I assume it's server-side? I cannot run the code in dartium).
If anybody could point me in the right direction, or provide an example as to how this could be accomplished, I would really appreciate it!
What you might be looking for is the hybrid flow. This produces two items
access credentials (for client side API access)
authorization code (for server side API access using the user credentials)
From the documentation:
Use case: A web application might want to get consent for accessing data on behalf of a user. The client part is a dynamic webapp which wants to open a popup which asks the user for consent. The webapp might want to use the credentials to make API calls, but the server may want to have offline access to user data as well.
The page Google+ Sign-In for server-side apps describes how this flow works.
Using the following code you can display the events of a calendar associated with the logged account. In this example i used createImplicitBrowserFlow ( see the documentation at https://pub.dartlang.org/packages/googleapis_auth ) with id and key from Google Cloud Console Project.
import 'dart:html';
import 'package:googleapis/calendar/v3.dart';
import 'package:googleapis_auth/auth_browser.dart' as auth;
var id = new auth.ClientId("<yourID>", "<yourKey>");
var scopes = [CalendarApi.CalendarScope];
void main() {
auth.createImplicitBrowserFlow(id, scopes).then((auth.BrowserOAuth2Flow flow) {
flow.clientViaUserConsent().then((auth.AuthClient client) {
var calendar = new CalendarApi(client);
String adminPanelCalendarId = 'primary';
var event = calendar.events;
var events = event.list(adminPanelCalendarId);
events.then((showEvents) {
showEvents.items.forEach((Event ev) { print(ev.summary); });
querySelector("#text2").text = showEvents.toString();
});
client.close();
flow.close();
});
});
}