I'm trying to use Stripe Checkout inside a WebView (both in Android and in iOS).
If I run the demo checkout in this page from Google Chrome from mobile it opens a new web page and everything works fine.
When I try to run the demo from a WebView (which I expect to behave in a completely similar way) it does not work and gives me a
Sorry, there was a problem loading Checkout.
If this persists, please try a different browser.
I thought that it is not made for mobile, but this is not true because from Google Chrome works perfectly fine.
Any suggestions to make it work?
This is the only way I managed to work with Stripe Checkout on webviews (I used Xamarin):
Install Plugin.Share from nuget (project, add newget packages both in shared and specific mobile project folders)
Try out this code to see that checkout works fine:
using System;
using Xamarin.Forms;
using Plugin.Share;
using Plugin.Share.Abstractions;
namespace stripewebviewtest
{
public class App : Application
{
public App ()
{
Button btn = new Button ();
btn.Text = "Click on me!";
// The root page of your application
MainPage = new ContentPage {
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
Children = {
new Label {
XAlign = TextAlignment.Center,
Text = "Welcome to Xamarin Forms!"
},
btn
}
}
};
btn.Clicked += (object sender, EventArgs e) => {
var url = "https://stripe.com/docs/checkout";
CrossShare.Current.OpenBrowser (url, new BrowserOptions {
ChromeShowTitle = true,
ChromeToolbarColor = new ShareColor {
A = 255,
R = 118,
G = 53,
B = 235
},
UseSafairReaderMode = false,
UseSafariWebViewController = false
});
};
}
protected override void OnStart ()
{
// Handle when your app starts
}
protected override void OnSleep ()
{
// Handle when your app sleeps
}
protected override void OnResume ()
{
// Handle when your app resumes
}
}
}
Run the project on a device
Click on "Click on me!" button
Click on "Pay with Card" button on the Webview
↝
Related
in my Xamarin.Forms App I have an Page which indicates an GoogleMap.
Everything is working perfect, but when I click on an InfowWindow from a pin, i cant open a new detailpage. The App hangs and I can do nothing with the App. I get no error message.
But its working in iOS.
Heres the Code:
private void MapObjekt_InfoWindowClicked(object sender, InfoWindowClickedEventArgs e)
{
App.locator.StopListeningAsync();
App.locator.PositionChanged -= Locator_PositionChanged;
objektliste detailPage = new objektliste();
App.mdp.Detail = detailPage;
}
Whats my fault ?
Problem was, that I have to open the Page in the Mainthread like this:
Device.BeginInvokeOnMainThread(() => { App.mdp.Detail = detailPage; });
I've implemented the Xamarin.Auth sample code to authenticate with google's identity provider on Android. I'm successfully navigated to the Google login page using the device's Chrome browser where I can enter my credentials. I successfully authorize with Google but the Chrome Custom Tabs doesn't close when it redirects back to my app, i.e., I'm left looking at the google search in the chrome browser. If I close the browser I can see my app again with the user details returned from google's identity provider displayed.
Why is Chrome's custom tabs not closing on redirect from the Google identity provider, and how can I get it to close using Xamarin Forms and Xamarin.Auth?
You can go back to your app if you add this code to the end of OnCreate method in the class that captures the Redirect (CustomUrlSchemeInterceptorActivity) in Xamarin.Auth example in Android
new Task(() =>{
StartActivity(new Intent(Application.Context,typeof(MainActivity)));
}).Start();
Where MainActivity is the name of your main Activity class in Android.
To be more exact here is a complete class that you can inherit for each redirection you intercept
public class UrlSchemeInterceptor : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
try
{
base.OnCreate(savedInstanceState);
// Convert Android.Net.Url to Uri
var uri = new Uri(Intent.Data.ToString());
new Task(() =>
{
var intent = new Intent(ApplicationContext, typeof(MainActivity));
intent.AddFlags(ActivityFlags.IncludeStoppedPackages);
intent.AddFlags(ActivityFlags.ReorderToFront);
StartActivity(intent);
}).Start();
// Load redirectUrl page
AuthenticationState.Authenticator.OnPageLoading(uri);
Finish();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
public class AuthenticationState
{
public static WebAuthenticator Authenticator;
/*This static field is used to store the object
from OAuth1Authenticator or OAuth2Authenticator
upon initialization in the UI (Xamarin forms or Android or iOS).
For example:
var authenticatorObject = new OAuth2Authenticator (YOUR PARAMETERS);
AuthenticationState.Authenticator = (WebAuthenticator)authenticatorObject;
var presenter = new OAuthLoginPresenter();
presenter.Login(authenticatorObject);
*/
}
For example in Google Case
[Activity(Label = "YOURLABEL", NoHistory = true, LaunchMode = LaunchMode.SingleTop)]
[IntentFilter( new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataSchemes = new[]
{
"com.googleusercontent.apps.",// YOUR GOOGLE ID INVERTED
},
DataPaths = new[]
{
"/oauth2redirect",
})]
public class GoogleUrlSchemeInterceptorActivity : UrlSchemeInterceptor { }
I am developing an Android app using Cordova and Ionic framework. I am playing a YouTube video with InAppBrowser using the code below:
window.open('https://www.youtube.com/embed/rAiw2SXPS-4', '_self');
But when I press the home button on the device while playing the video, the video is not paused. Due to this issue, my app is rejected after submitting to Google Play with the reason below:
Your submission has been rejected for enabling background playing of YouTube videos in violation of the YouTube API Terms of Service. If this submission was an update to an existing app, the version published prior to this update is still available in Google Play. Please modify your app and resubmit. Additional details have been sent to your account owner's email address.
I searched for a solution but have no luck. Can anybody help?
I was also struggling to find complete solution to pause(not stop) ongoing video(s) when device locks, but with no success. Eventually I found solution myself by combining several parts together.
Here is the directive that accomplishes YouTube player pause on device lock:
import { Directive, ElementRef, OnInit } from '#angular/core'
import { Platform } from 'ionic-angular'
import * as _ from 'lodash-es'
/* tslint:disable */
(function (apiInit) {
let _registerYouTubeAPIIfNotAlready = function () {
if (!window[ 'onYouTubeIframeAPIReady' ]) {
window[ 'onYouTubeIframeAPIReady' ] = function () {
apiInit.youTubeApiRegistered = true
if ((typeof apiInit.callback !== "undefined") && _.isFunction(apiInit.callback)) {
apiInit.callback()
}
}
} else {
console.error("trying to register YouTube API when it's already registered")
}
}
apiInit.setupYouTubeApiOrDefault = function (callback) {
if ((typeof callback === "undefined") || !_.isFunction(callback)) {
_registerYouTubeAPIIfNotAlready()
return
}
if(apiInit.youTubeApiRegistered){
callback()
return;
}
apiInit.callback = callback
_registerYouTubeAPIIfNotAlready()
}
}(window[ 'youTubeApiInit' ] = window[ 'youTubeApiInit' ] || {}))
#Directive({
selector: "[preventYoutubePlayOnBackground]",
})
export class PreventYouTubePlayOnBackgroundDirective implements OnInit {
public static youTubeIframeAPI = 'https://www.youtube.com/iframe_api'
public static injectYouTubeIframeApi(): void {
let youTubeCheckQuery = "script[src*='" + PreventYouTubePlayOnBackgroundDirective.youTubeIframeAPI + "']"
if (!document.querySelector(youTubeCheckQuery)) {
// from YouTube API documentation
let tag = document.createElement('script')
tag.src = PreventYouTubePlayOnBackgroundDirective.youTubeIframeAPI
let firstScriptTag = document.getElementsByTagName('script')[ 0 ]
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag)
}
}
public iframeId: string
private youTubeIframeElm: any
constructor(
public elm: ElementRef,
private platform: Platform,) {
this.youTubeIframeElm = elm.nativeElement
this.iframeId = this.youTubeIframeElm.getAttribute('id')
}
ngOnInit(): void {
this.platform.ready().then(() => {
PreventYouTubePlayOnBackgroundDirective.injectYouTubeIframeApi()
window[ 'youTubeApiInit' ].setupYouTubeApiOrDefault(() => {
this.setYouTubeApi()
this.platform.pause.subscribe(() => {
let player = new window[ 'YT' ].Player(this.iframeId) // TODO: add youtube API node module
player.a.contentWindow.postMessage('{"event":"command","func":"' + 'pauseVideo' + '","args":""}', '*')
})
})
})
}
private setYouTubeApi(): void {
let url = new URL(this.youTubeIframeElm.src)
if (!url.searchParams.get("enablejsapi")) { // enabling youTube js api to be able to create player
let prefix = (this.youTubeIframeElm.src.indexOf("?") === -1) ? "?" : "&"
this.youTubeIframeElm.src += prefix + "enablejsapi=true"
}
}
}
HTML for embedded YouTube player will be:
<iframe id="onboarding-video"
width="400"
height="300"
[src]="videoUrl"
frameborder="0"
allowfullscreen
preventYoutubePlayOnBackground
iframe-id="onboarding-video">
</iframe>
Note: above code is for ionic 2+, however for ionic 1 you can use:
(function() {
// same kind of logic here as written in above constructor body
$ionicPlatform.on('pause', function(event) {
// pausing player here
});
}())
Also you will need to create Angular 1 style directive instead of TypeScript one written above.
With $ionicPlatform you can use "on" method:
$ionicPlatform.on('pause', function(event) {
// pause video here
});
It is based on Cordova pause event:
document.addEventListener("pause", onPause, false);
function onPause() {
// Handle the pause event
}
See ionic documentation here and cordova documentation here.
You need to set shouldPauseOnSuspend=yes within the options when calling the open method for the inappbrowser. See the documentation here: https://github.com/apache/cordova-plugin-inappbrowser.
Something like this will work:
window.open('http://google.com','_blank', 'shouldPauseOnSuspend=yes');
I have the following minimal example of an app using Xamarin.Forms (version 1.2.3) and a MasterDetailPage (similar to: "Show "Back to Menu" Button in iOS NavigationBar with Xamarin.Forms"):
public static class App
{
static MasterDetailPage MDPage;
public static Page GetMainPage()
{
MDPage = new MasterDetailPage {
Master = new ContentPage {
Title = "Master",
Icon = Device.OS == TargetPlatform.iOS ? "menu.png" : null,
Content = new Button {
Text = "Open detail",
Command = new Command(o => {
MDPage.Detail = new NavigationPage(new ContentPage());
MDPage.IsPresented = false;
}),
},
},
Detail = new NavigationPage(new ContentPage()),
};
MDPage.IsPresentedChanged += (sender, e) => Console.WriteLine(DateTime.Now + ": " + MDPage.IsPresented);
return MDPage;
}
}
(hosted on GitHub)
When opening or closing the MasterPage via button click on Android, the IsPresentedChanged event is triggered three times instead of once. According to the command line output the IsPresented property is either toggling as True-False-True or False-True-False, respectively.
Opening or closing using a swipe gesture or tapping on the DetailPage does work well. On iOS there is no problem at all.
Is there something I'm doing wrong? Or is there a simple workaround to get a reliable event?
Ok, with the current version 1.4.0 of Xamarin.Forms the issue seems to be fixed. Opening the slide-out menu yields exactly one "True", closing it yields "False" - just as expected.
Does anyone have a working example of using proprietary properties like "ontouchend", and "gestureend" in TypeScript?
I've tried using something like this below:
//Create an alert.
function TouchedScreen(username: string): void {
alert(username + " has touched the screen.");
}
//Touch anywhere on screen for an alert on iOS/Android
window.ontouchend = () => {
TouchedScreen("[username]");
};
I'm assuming this is due to ontouchend being a proprietary property, using addEventListener compiles correctly but I wan't to use it with a property, how can I do this in TypeScript?
Just tell typescript that these properties exist on Window:
interface Window{
ontouchend: Function;
}
//Touch anywhere on screen for an alert on iOS/Android
window.ontouchend = () => { // compiles fine
};
If you want the same event on all HTMLElements just tell TypeScript about that too:
interface HTMLElement {
ontouchend: Function;
}
var a: HTMLAnchorElement;
a.ontouchend = () => { // compiles fine
};