How to accept Apple Pay with Stripe JS inside WKWebView? - xamarin.forms

I'm trying to load up a webpage which uses Stripe.js inside a WKWebView on iOS (using Xamarin.Forms).
Everything works fine (can take card payments), except for Apple Pay: paymentRequest.canMakePayment() always returns null.
The same webpage inside a SFSafariViewController works fine, so the issue seems to be related to restrictions inside WKWebView.
However, according to https://webkit.org/blog/9674/new-webkit-features-in-safari-13/ this should now be supported and indeed the same WKWebView also loads up the official Apple Pay demo page (https://applepaydemo.apple.com/) with no issues.
It would seem that the problem is eventually with how Stripe.js works under the hood (perhaps with regards to script injection).
My custom renderer in Xamarin looks like this:
WKWebView wkWebView;
protected override void OnElementChanged(ElementChangedEventArgs<NativeWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var config = new WKWebViewConfiguration();
wkWebView = new WKWebView(Frame, config);
wkWebView.WeakNavigationDelegate = new WebNavigationDelegate();
SetNativeControl(wkWebView);
}
if (e.NewElement != null)
Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.Url)));
}
And the JS in the webpage (taken from the Stripe Element samples):
//set up payment request
var paymentRequest = stripe.paymentRequest({
country: 'GB',
currency: 'gbp',
total: {
label: 'sample order',
amount: 100,
},
requestPayerName: true,
requestPayerEmail: true,
});
paymentRequest.canMakePayment().then(function (result) {
// result is always false!
...
});
Are there perhaps additional settings that can be added to the WKWebView to make this work?

This is no longer an issue. It was acknowledged by Stripe as a flaw in their JS wrapper and has been fixed in recent updates to the official library.

Related

How to customize styles for browser push notification

I am creating an app using Angular 4, that should receive push notifications from time to time. I have managed to make the notification appear, but I wasn't able to customize its styles and its content.
What I am looking for is a way to change the background color of my notifications so that I can make them blink to call the user's attention.
Here is my code:
public showNotification(title: string, text: string): Notification {
// Check if notifications are supported
const notificationOptions = {
body: `<div style="color:red"><p>${text}</p></div>`,
icon: 'assets/img/logo_onyo-simples.png',
badge: 'assets/img/logo_onyo-simples.png',
vibrate: [200, 100, 200]
};
try {
// Throws an exception if the browser doesn't support a notification.
const notification = new Notification(title, notificationOptions);
notification.onclick = (e: any) => {
window.focus();
notification.close();
};
return notification;
}
catch (e) {
if (e.name === 'TypeError') {
this.raven.captureBreadcrum({
message: 'This browser does not support notifications',
level: 'warning',
category: 'notification'
});
}
else {
this.raven.captureException(e);
throw e;
}
}
}
I've tried to add a test HTML tag to the notification body, but that didn't work. Is there a way to do this?
Note: the reason because I am not using Toastr notifications is because I want the user to receive my notifications even if the browser is minimized or behind another application.
Note 2: here is an example of the type of notification that I am using: https://tests.peter.sh/notification-generator/.
I believe that what you're trying to do is not possible, at least according to the spec. There are no properties nor methods for passing / setting of visual presentation.
I'm speculating here, but it could be because browser vendors want to prevent exactly the type of behavior you're trying to create. Maybe you can style a notification with good taste and design. However, this would also open the door to people creating bad, annoying and straight up predatory notifications. It would open the door to dark patterns.
Although, it's possible that this kind of stylistic flexibility will be added in the future. I just don't think so.

Prism for Xamarin.Forms NavigationService OnNavigatedFrom/OnNavigatedTo

we are trying to use OnNavigatedFrom and OnNavigatedTo but the "OnNavigatedTo" is not called when needed
ViewModelOne:
_navigationService.NavigateAsync("ViewModelPageTwo", useModalNavigation: false);
ViewModelTwo:
var parameters = new NavigationParameters()
{
{
"Pesquisa",
TextoPesquisa
},
{
"DataEnvioInicial",
DataEnvioInicio
},
{
"DataEnvioFim",
DataEnvioFim
},
{
"DataHistoricoInicio",
DataHistoricoInicio
},
{
"DataHistoricoFim",
DataHistoricoFim
},
{
"TodasAsCaixas",
PesquisaEmTodasCaixas
}
};
_navigationService.GoBackAsync(parameters);
The problem is that ViewModelOne -> OnNavigatedTo not trigger when GoBackAsync from ViewModelTwo
So what's happening is when you call GoBackAsync, you are actually going back to the TabbedPage, not the actual Tab. For now, you can implement INavigationAware on the actual TabbedPage, and then pass your parameters to the selected Tab's VM in the code-behind. Not optimal, but it will work for now. There is actually a discussion on the GitHub site talking about the various issues when dealing with TabbedPages and it's children during navigation. YOu can follow it here:
https://github.com/PrismLibrary/Prism/issues/650

Why do Google's JS Client SDK function callbacks fail?

I'm currently in the learning phase for how the Google JS Client SDK works, since my boss needs me to learn how to integrate a Sign In button to his site to enable people to Authenticate via Google. I am testing the code for the custom Sign In button, with a touch of added functionality (like a Sign Out button), and in the process I've practically copy/pasted the code from their website. Let me show you the code first and then explain the issue, so that you can understand where the code is failing:
<script src="https://apis.google.com/js/client.js?onload=init"></script>
<script type="text/javascript">
var clientId = '{my client id here}'; // for web
var apiKey = '{my api key here}';
var scopes = 'profile email';
function SignOut() {
// I know, sloppy, but the signOut method from Google doesn't work.
window.location = 'https://accounts.google.com/logout';
// Additional code if necessary.
};
function makeApiCall() {
gapi.client.load('plus', 'v1', function () {
var request = gapi.client.plus.people.get({ 'userId': 'me' });
request.execute(function (response) {
var heading = document.createElement('h4');
var image = document.createElement('img');
image.src = response.image.url;
heading.appendChild(image);
heading.appendChild(document.createTextNode(response.displayName));
document.getElementById('name').appendChild(heading);
alert('User logged in. makeApiCall() has executed.');
})
})
};
function init() {
gapi.client.setApiKey(this.apiKey);
window.setTimeout(checkAuth, 1);
console.log('Up and ready to go.');
};
function checkAuth() {
// Triggers when the page and the SDK loads.
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: true }, handleAuthResult);
};
function handleAuthClick(event) {
// Triggers after a user click event to ensure no popup blockers interfere.
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: false }, handleAuthResult);
return false;
};
function handleAuthResult(authResult) {
var authorizeButton = document.getElementById('SignInBtn');
var signoutButton = document.getElementById('SignOutBtn');
if (authResult && !authResult.error) {
var V = JSON.stringify(authResult);
localStorage.setItem('GoogleAuthResult', V);
console.log(V); // Just for testing...
var authTimeout = (authResult.expires_in - 5 * 60) * 1000; setTimeout(checkAuth, authTimeout); // As recommended by a Google employee in a video, so that the token refreshes.
authorizeButton.style.display = 'none'; // Switching between Sign In and Out buttons.
signoutButton.style.display = 'inline-block';
makeApiCall();
} else {
// Immediate:true failed so user is NOT signed in.
// Make the Sign In button the one visible and prep it
// so that it executes the Immediate:false after user click:
authorizeButton.style.visibility = 'inline-block';
authorizeButton.onclick = handleAuthClick;
signoutButton.style.visibility = 'none';
}
};
</script>
The handleAuthClick function does run on the button click, but after taking the user to the Google Sign In page, when that page brings me back, the browser kinda flickers and the handleAuthResult function does not execute. Therefore, nothing changes in the page after the successful sign in; the button displayed is the Sign In button (Sign Out button not visible) and no information is displayed on the 'name' textNode. This happens on Internet Explorer (11), Firefox (39) and Chrome (44). Also, it happens at home on my laptop (straight connection to the web via Cable broadband) and at work (on Windows 8.1 behind an Active Directory).
I began wondering so I started refreshing the browser page and after a couple of refreshes, since the script runs from the beginning, the immediate:true fires again and voilá: user is connected and API call triggers.
So, on my laptop, I changed the function being called back, in the immediate:false line's callback parameter, to the init() function and that fixed the problem: everything runs smoothly from beginning to end. Yet, this is not the way it is supposed to work. I still don't know what is going on with that line.
This morning, on my computer at work (behind Active Directory), that fix didn't work. I have to refresh the page a couple of times so that the script runs from the beginning and the immediate:true triggers recognizing the user's Signed In state and displaying the proper button on screen.
Any ideas on why does this callback fail?
You need to define your apiKey in the first section of your code
var clientId = '{my client id here}'; // for web
var apiKey = '{my api key here}'
Maybe thats the problem.
Google ApiKeys

Regular views don't work when we are on iPhone?

I use MVC 4 with this custom displaymodeprovider. Even if I set it as "false" it still on iPhone returns mobile version, though I want to return regular version of the website. Please help
DisplayModeProvider.Instance.Modes.Insert(0, new
DefaultDisplayMode("Mobile")
{
ContextCondition = (context => false)
});
I set the break point inside this this code and it calls, though it still return mobile version.
Eventually I've figured out the problem
MVC 4 already has his own redirection for "Mobile" prefix. so to use custom logic we need to remove that DisplayModeProvider for "Mobile" prefix, like this:
var mobileModel = DisplayModeProvider.Instance.Modes.FirstOrDefault(a => a.DisplayModeId == "Mobile");
if (mobileModel != null)
{
DisplayModeProvider.Instance.Modes.Remove(mobileModel);
}
DisplayModeProvider.Instance.Modes.Insert(0, new
DefaultDisplayMode("Mobile")
{
ContextCondition = (context => <USE ANY YOUR CUSTOM LOGIC>)
});
This answer will be very useful for people who want that their website will be available in both versions for iPad or iPhone or Android and we can for example store in coockie user selection which version of the website we should display in his device.

How do I handle dojo datagrid cell updates so I can post them back automatically to the server?

I am using dojo datagrid to display my data. When the end user edit the cell values it should be updated in the server using ajax calls(when the focus goes out of the cell).
Else, I should have a Edit & update/cancel buttons for each row to handle the same feature.
But I don know how to place edit & update buttons inside the grid and capture their events.
By default dojo updates only local the store value(client side). how can I save the updated cell values into the server?
do we need to write any override methods to do so??
I am new to dojo. Any detailed explanation or sample codes would be much appreciated.
Could anyone lend a hand to solve this issue??
Thank you
Regards,
Raj
To be able to push the updates server-side, you've to override _saveCustom() or _saveEverything(). Here is a piece of code (a bit cleaned-up) I'm using to persist an update.
Note that the code below relies on the private _getModifiedItems() because the DataGrid accepts inline editions. If you do know the list of modified items (because the edition is done in a popup and you keep the item key somewhere), retreiving the modified item is simpler.
module.submitUpdates = function() {
var store = <from a variable local to the module>
if (store.isDirty() confirm("Updates to be persisted. Continue?")) {
store._saveCustom = function(saveCompleteCallback, saveFailedCallback) {
var modifiedItem = _getModifiedItems(store)[0];
dojo.xhrPost( {
headers: { "content-type": "application/json; charset=utf-8" },
content: dojo.toJson(modifiedItem),
handleAs: "json",
load: function(response) {
if (response !== null && response.success) {
saveCompleteCallback();
}
else {
saveFailedCallback(response);
}
},
error: saveFailedCallback,
url: "/API/<Object>"
});
};
store.save( {
onComplete : function() {
module.loadCachingRuleList();
},
onError : function(errorData, request) {
_reportUpdateFailure(errorData, errMsg);
}
});
}
};
Here is the code I use to get all updated items when the user is about to loose an updated DataGrid (because he leaves the page or because he wants to refresh the grid content).
Note that the following code was using Dojo 1.3. I haven't check if it's easier with Dojo 1.4... I hope that dojo.Stateful that's going to be introduced in Dojo 1.5 will simplify it, otherwise we'll have to wait for Dojo 1.6 ;)
var _getModifiedItems = function(store) {
var modifiedItems = [];
if (store !== null && store._pending !== null) {
if (store._pending._modifiedItems !== null) {
for (var modifiedItemKey in store._pending._modifiedItems) {
if (store._itemsByIdentity) {
modifiedItems.push(store._itemsByIdentity[modifiedItemKey]);
}
else {
modifiedItems.push(store._arrayOfAllItems[modifiedItemKey]);
}
}
}
if (store._pending._newItems !== null) {
for (var modifiedItemKey in store._pending._newItems) {
if (store._itemsByIdentity) {
modifiedItems.push(store._itemsByIdentity[modifiedItemKey]);
}
else {
modifiedItems.push(store._arrayOfAllItems[modifiedItemKey]);
}
}
}
}
return modifiedItems;
};
var _getDeletedItems = function(store) {
var deletedItems = [];
if (store !== null && store._pending !== null && store._pending._deletedItems !== null) {
for (var deletedItemKey in store._pending._deletedItems) {
if (store._itemsByIdentity) {
deletedItems.push(store._itemsByIdentity[deletedItemKey]);
}
else {
deletedItems.push(store._arrayOfAllItems[deletedItemKey]);
}
}
}
return deletedItems;
};
I hope this helps,
A+, Dom
This isn't a very detailed explanation but yes you should override the method that handles user data entries as I don't see an event for this. So basically create a new class that inherits from the data grid, find the method in the source code that handles the editing and override it to give it an extra action to post it back to the server. You'll want to call the datagrid's default action in that method too.
Some information here (that you probably are already aware of):
http://docs.dojocampus.org/dojox/grid/DataGrid#editing-data
I've extended Dojo's default components before, it's not hard. I find that it's always good to examine the Dojo source and to just be careful when upgrading Dojo to make sure new versions don't break existing features.
To avoid hacking your dojo library, just switch to the YUI2 data table; it's cell editor accepts the asyncsubmitter function that sends your edits to your sever
This is the description from documentation
asyncSubmitter
Function
Implementer defined function that can submit the input value to a server. This function must accept the arguments fnCallback and oNewValue. When the submission is complete, the function must also call fnCallback(bSuccess, oNewValue) to finish the save routine in the CellEditor. This function can also be used to perform extra validation or input value manipulation.
YUI2DataTable

Resources