StripeView in Xamarin Forms

MehdyMehdy USMember
edited August 2015 in Xamarin.Forms

I did a little research and it seems braintree and stripe are suitable for app payments.
braintree has no official xamarin bindings but there's a Stripe component by Xamarin for Android and iOS.
is it possible to use this component in Xamarin.Forms (and it's StripeView UI) ?

Posts

  • JoeMankeJoeManke USMember ✭✭✭✭✭

    You should be able to use the component, but you will probably need to use the DependencyService to access Stripe code in the Forms project, and will definitely need to write a custom view with a renderer to get the StripeView UI.

  • MehdyMehdy USMember

    @JoeManke Thank you, I'm working on that but I can not convert StripeView to Xamarin.Forms View.
    in DependencyService samples , the returned type is usually a simple class that can be re-created in xamarin.forms project I can't find anything like this.

  • JoeMankeJoeManke USMember ✭✭✭✭✭

    For the views, you need to use a renderer, not the DependencyService. I am not familiar with Stripe so I don't know what properties you'll need to expose on Forms to get proper usage, but the absolute basics are:

    In your Forms project

    namespace MyApp.Forms
    {
        public class StripeView : View
        {
            //create BindableProperties for anything you need to interact with/bind to
        }
    }
    

    In your Android project

    [assembly: ExportRenderer(typeof(MyApp.Forms.StripeView), typeof(Stripe.StripeView))]
    namespace MyApp.Droid
    {
        public class StripeViewRenderer : ViewRenderer<MyApp.Forms.StripeView, Stripe.StripeView>
        {
            protected override void OnElementChanged(ElementChangedEventArgs<MyApp.Forms.StripeView> e)
            {
                base.OnElementChanged(e);
    
                var native = new Stripe.StripeView();
    
                // set up whatever you need to between the native and XF views
                // hint: the Element property of this class will be the XF view
    
                SetNativeControl(native);
            }
        }
    }
    

    In your iOS project

    [assembly: ExportRenderer(typeof(MyApp.Forms.StripeView), typeof(Stripe.StripeView))]
    namespace MyApp.iOS
    {
        public class StripeViewRenderer : ViewRenderer<MyApp.Forms.StripeView, Stripe.StripeView>
        {
            protected override void OnElementChanged(ElementChangedEventArgs<MyApp.Forms.StripeView> e)
            {
                base.OnElementChanged(e);
    
                var native = new Stripe.StripeView();
    
                // set up whatever you need to between the native and XF views
                // hint: the Element property of this class will be the XF view
    
                SetNativeControl(native);
            }
        }
    }
    

    Here is a guide on writing renderers.

  • MehdyMehdy USMember

    @JoeMachek thank you! I was able to compile the custom renderer and get access to stripeView in PCL project.
    while I'm adding the control to the page I get
    System.InvalidCastException: Unable to cast object of type 'Stripe.StripeView' to type 'Xamarin.Forms.IRegisterable'.
    any Idea this issue?

  • JoeMankeJoeManke USMember ✭✭✭✭✭

    Oh, that's my bad for just glancing at my code then writing that. In the ExportRenderer attribute, the second parameter should be the type of the renderer, (typeof(MyApp.Droid.StripeViewRenderer)), not the type of the native control.

  • MehdyMehdy USMember
    edited September 2015

    @JoeManke with a little change I was able to render StripeView in both iOS and Android.
    it's available in bottom of the post.
    now I can't get properties from this component. in most samples the bindable properties are simple types like string and int. but the property that needs to bound is Card with a lot of properties .
    is there a better way for accessing native control properties?

    Thanks,

    Mehdy

    //StripeView Custom Renderer for Android
    
    using System;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.Android;
    
    [assembly: ExportRenderer(typeof(mesh1.StripeView), typeof(mesh1.Droid.StripeViewRenderer))]
    namespace mesh1.Droid
    {
        public class StripeViewRenderer :  ViewRenderer<mesh1.StripeView, Stripe.StripeView> , IVisualElementRenderer, IDisposable, IRegisterable
        {
            public StripeViewRenderer ()
            {
            }
    
            Stripe.StripeView _view;
            protected override void OnElementChanged(ElementChangedEventArgs<mesh1.StripeView> e)
            {
                base.OnElementChanged(e);
                // set up whatever you need to between the native and XF views
                // hint: the Element property of this class will be the XF view
                if (Control == null) {   // perform initial setup
                    // do whatever you want to the UITextField here!
                    _view= new Stripe.StripeView(this.Context);
                    SetNativeControl(_view);
    
                }
            }
        }
    }
    
    //StripeView Custom Renderer for iOS
    using System;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.iOS;
    
    
    [assembly: ExportRenderer(typeof(mesh1.StripeView), typeof(mesh1.iOS.StripeViewRenderer))]
    namespace mesh1.iOS
    {
        public class StripeViewRenderer :  ViewRenderer<mesh1.StripeView, Stripe.StripeView> , IVisualElementRenderer, IDisposable, IRegisterable
        {
    
            public StripeViewRenderer ()
            {
            }
    
            Stripe.StripeView _view;
            protected override void OnElementChanged(ElementChangedEventArgs<mesh1.StripeView> e)
            {
                base.OnElementChanged(e);
                // set up whatever you need to between the native and XF views
                // hint: the Element property of this class will be the XF view
                if (Control == null) {   // perform initial setup
                    // do whatever you want to the UITextField here!
                    _view= new Stripe.StripeView();
                    SetNativeControl(_view);
                }
            }
        }
    }
    
  • I would need to know as well. Tried to do binding, but can't get StripeView.Card property to bind properly. Has anyone made any progress with this?

  • Since StripeView.Card doesn't seem bindable, I filed a bug against Xamarin (and another one that the comment below in the code describes).

    Here is my current workaround to solve this issue. I traverse the tree of Subviews and attach editing changed event to each text input - not elegant by any means, but it works. Here is the code for iOS, for Android im going to do something similar:

    using System;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.iOS;
    
    
    [assembly: ExportRenderer(typeof(TestApp.Views.StripeView), typeof(TestApp.iOS.StripeViewRenderer))]
    namespace TestApp.iOS
    {
        public class StripeViewRenderer :  ViewRenderer<TestApp.Views.StripeView, Stripe.StripeView> , IVisualElementRenderer, IDisposable, IRegisterable
        {
            private Stripe.StripeView m_stripeView;
    
            public StripeViewRenderer ()
            {
            }
    
            private bool FindTextInputAndBindCardPropertyHack(UIKit.UIView View) {
                var Result = false;
                if (View == null) {
                    return Result;
                }
    
                foreach (UIKit.UIView Subview in View.Subviews) {
                    Result = FindTextInputAndBindCardPropertyHack (Subview) || Result;
                }
    
                if (View is UIKit.UITextField) {
                    var TextField = View as UIKit.UITextField;
                    TextField.EditingChanged += (object sender, EventArgs e) => {
                        try {
                            Element.Card = m_stripeView.Card;
                        } catch (Exception Ex) {
    
                            //
                            // StripeView.Card returns ArgumentOutOfBoundsException when expiry month is
                            // filled in
                            //
    
                            Console.WriteLine (Ex.ToString ());
                        }
                    };
    
                    Result = true;
                }
    
                return Result;
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<TestApp.Views.StripeView> e)
            {
                base.OnElementChanged(e);
                if (e.OldElement != null || this.Element == null) {
                    return;
                }
    
                m_stripeView = new Stripe.StripeView ();
                if (!FindTextInputAndBindCardPropertyHack (m_stripeView)) {
                    Xamarin.Insights.Report (new Exception("StripeView.Card not bound, StripeView.Card property cannot be read!"), 
                                             Xamarin.Insights.Severity.Critical);
                }
    
                SetNativeControl(m_stripeView);
            }
    
            protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                base.OnElementPropertyChanged (sender, e);
                if (Control == null || Element == null) {
                    return;
                }
            }
        }
    }
    

    Here is my StripeView in Shared code:

    using Xamarin.Forms;
    using System;
    
    namespace TestApp.Views
    {
        public class StripeView : View
        {
            public Stripe.Card Card {
                get; set;
            }
    
            public StripeView ()
            {
            }
        }
    }
    
  • MehdyMehdy USMember
    edited September 2015

    I had the same issue and I used a trick to update the properties manually whenever a trigger flag changes (GUID).
    when I want to read properties, I change the GUID and then in OnElementPropertyChanged I set the properties of PCL control.

    StripeViewRenderer.cs

    [assembly: ExportRenderer(typeof(mesh1.StripeView),     typeof(mesh1.iOS.StripeViewRenderer))]
    namespace mesh1.iOS
    {
    public class StripeViewRenderer :  ViewRenderer<mesh1.StripeView,
    Stripe.StripeView> , IVisualElementRenderer, IDisposable, IRegisterable
    {
    
        public StripeViewRenderer ()
        {
        }
    
        Stripe.StripeView _view;
        protected override void     OnElementChanged(ElementChangedEventArgs<mesh1.StripeView> e)
        {
            base.OnElementChanged(e);
            // set up whatever you need to between the native and XF views
            // hint: the Element property of this class will be the XF view
            if(_view==null)
             {   // perform initial setup
                // do whatever you want to the UITextField here!
                _view= new Stripe.StripeView();
                SetNativeControl(_view);
            }
            if (Element != null) {
                var stripeView1 = (mesh1.StripeView)Element;
                _view.LayoutIfNeeded ();
            }
    
    
        }
    
    
        protected override void  OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender,  e);
            if (e.PropertyName == "GUID")
            {
                Element.Number = Control.Card.Number;
                Element.ExpiryYear = Control.Card.ExpiryYear;
                Element.ExpiryMonth = Control.Card.ExpiryMonth;
                Element.CVC = Control.Card.CVC;
            }
        }
    
    }
    }
    

    binding properties

    using System;
    using Xamarin.Forms;
    
    namespace mesh1
    {
        public class StripeView : View
        {
    
            public static readonly BindableProperty StripeViewProperty =
    BindableProperty.Create<StripeView, string>(p => p.GUID,null,BindingMode.TwoWay);
    
            public static readonly BindableProperty NumberProperty =
                BindableProperty.Create<StripeView, string>(p =>p.Number,null,BindingMode.TwoWay);
    
            public string  Number { get { return    (string)GetValue(NumberProperty ); } 
                set {  SetValue (NumberProperty, value);  } } // it would rise ElementPropertyChanged event}
            public string GUID { get { return (string)GetValue(StripeViewProperty ); } 
                set { SetValue (StripeViewProperty, value); } } // it would rise ElementPropertyChanged event}
            public string CVC { get; set;}
            public string Name { get; set;}
            public int ExpiryYear  { get; set;}
            public int ExpiryMonth  { get; set;}
    
    
            public class Card
            {
                public string Number { get; set;}
                public string CVC { get; set;}
                public string Name { get; set;}
                public int ExpiryYear{ get; set;}
                public int ExpiryMonth{ get; set;}
                public string AddressCity { get; set;}
                public string AddressCountry { get; set;}
            }
        }
    }
    

    and to show the control and get card info:

        StripeView sv1;
        public PaymentInfo ()
        {
            InitializeComponent ();
            sv1= new StripeView ();
        }
    
        async void btn_cardInfoClicked(object sender,EventArgs e)
        {
            sv1.GUID = Guid.NewGuid ().ToString ();
            var card1 = new StripeView.Card ();
            card1.Number = sv1.Number;
            card1.CVC = sv1.CVC;
            card1.ExpiryYear = sv1.ExpiryYear;
            card1.ExpiryMonth = sv1.ExpiryMonth;
        }
    
  • MehdyMehdy USMember

    and here is a sample for calling StripeView Methods using Dependency services:

    in IStripe.cs

    using System;
    using System.Threading.Tasks;
    
    namespace mesh1
    {
        public interface IStripe
        {
            Task<string> CreateToken(string Number, string CVC, int ExpiryYear,int ExpiryMonth);
         }
    }
    

    in Stripe_iOS.cs

    using System;
    using Xamarin.Forms;
    using mesh1.iOS;
    using Stripe;
    using System.Threading.Tasks;
    
    [assembly: Dependency (typeof (Stripe_iOS))]
    namespace mesh1.iOS
    {
        public class Stripe_iOS : IStripe
        {
            public Stripe_iOS ()
            {
            }
    
            public async Task<string> CreateToken(string Number, string CVC, int ExpiryYear,int ExpiryMonth ) 
            {
                Stripe.Card stripeCard1 = new Stripe.Card ();
                stripeCard1.Number = Number;
                stripeCard1.ExpiryYear = ExpiryYear;
                stripeCard1.ExpiryMonth = ExpiryMonth;
                stripeCard1.CVC = CVC;
                var token =  await Stripe.StripeClient.CreateToken(stripeCard1,"pk_test_from_stripe_dashboard");
                return token.Id;
            }
        }
    }
    
  • AlexaMayerAlexaMayer PHMember

    @Mehdy I'm currently referencing on your last post. Does it also use the StripeView UI? Because I can't seem to retrieve the values that I've entered on the UI. I just started on Xamarin so I don't know much. Thanks!

  • MehdyMehdy USMember
    edited December 2015

    @Alexah as mentioned there's a bug in this control binding, when you want to read values from this control do as this:
    (sv1 is the StripeView).

    sv1.GUID = Guid.NewGuid ().ToString (); var card1 = new StripeView.Card (); card1.Number = sv1.Number; card1.CVC = sv1.CVC; card1.ExpiryYear = sv1.ExpiryYear; card1.ExpiryMonth = sv1.ExpiryMonth;

    and make sure you have OnElementPropertyChanged like this:

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (e.PropertyName == "GUID") { Element.Number = Control.Card.Number; Element.Name = Control.Card.Name; Element.Zip = Control.Card.AddressZip; Element.ExpiryYear = Control.Card.ExpiryYear; Element.ExpiryMonth = Control.Card.ExpiryMonth; Element.CVC = Control.Card.CVC; } }

  • Can someone share a project using Stripe with Xamarin.Forms?

    Thanks.

  • NobbyHardingNobbyHarding GBMember

    Bit late but this is a portable stripe implementation and is active. https://github.com/jaymedavis/stripe.net

  • JaymeDavisJaymeDavis USMember

    Thanks for the ping @NobbyHarding. This is active, and it's coming to the Xamarin store soon. I have a working package for it now, I just don't have any examples built yet. Is it acceptable in the Xamarin community to release your package and have examples later? Trying to get a feel for how many I need to put together before adding it to the store.

    Thanks!

  • XLR876XLR876 USMember

    @Mehdy,

    There is a section where you mention "and to show the control and get card info", which has methods public PaymentInfo () and async void btn_cardInfoClicked(object sender,EventArgs e).

    In what file does that code live?

    Thanks!

  • hii,
    How to write stripe render or dependency for android ?

  • Can someone share a project using Stripe with Xamarin.Forms?

    Thanks.

  • PaulMouraPaulMoura USUniversity ✭✭

    @Mehdy What a great hack!! Thank you so much!! This is exactly what I needed!!

    @XLR876 PaymentInfo() is the constructor for the .cs file backing the view containing the StripeView and a button to submit the card info. The 'async void btn_cardInfoClicked(object sender, EventArgs e)' is the event for the button.

    @VenkataSivaprasadReddyPulagam Mehdy wrote examples for both Android and iOS in separate comments above.

  • @Mehdy good job and thank you.. :)

    @PaulMoura thank you for tagging me in this conversation. you reminded me about this thing again (Y)

  • SvetchSvetch USMember

    I did it with help @Mehdy code with Dependency;
    Add to Android Project -> Components -> add Stripe
    after add Activity :
    Stripe_Android.cs in Android Project
    ``
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    using Android.App;
    using Android.Content;
    using Android.OS;
    using Android.Runtime;
    using Android.Views;
    using Android.Widget;
    using Xamarin.Forms;
    using MyApp.Interfaces;
    using System.Threading.Tasks;
    using Android.Util;

    [assembly: Dependency(typeof(MyApp.Droid.Stripe_Android))]
    namespace MyApp.Droid
    {
    [Activity(Label = "Stripe")]
    public class Stripe_Android : Activity, IStripe
    {
    protected override void OnCreate(Bundle savedInstanceState)
    {
    base.OnCreate(savedInstanceState);
    }

        public async void CreateToken(string Number, string CVC, int ExpiryYear, int ExpiryMonth)
        {
            var token = new Stripe.Token();
            try
            {
                Stripe.Card stripeCard1 = new Stripe.Card();
                stripeCard1.Number = Number;
                stripeCard1.ExpiryYear = ExpiryYear;
                stripeCard1.ExpiryMonth = ExpiryMonth;
                stripeCard1.CVC = CVC;
                token = await Stripe.StripeClient.CreateToken(stripeCard1, "YOUR-STRIPE-PUBLISHER-KEY");
    
                MessagingCenter.Send<IStripe, AppPosition>(this, "gotStripe", new AppPosition()
                {
                    StripeToken = token, Token = token.Id
    
                });
    
            }
            catch (Exception ex)
            {
                MessagingCenter.Send<IStripe, AppPosition>(this, "gotStripe", new AppPosition()
                {
                    Error = ex.Message
                });
    
                Log.Debug("Stripe Error", ex.ToString());
            }
        }
    
    }
    

    }
    MyApp.Interfaces.AppPosition (just class in Forms Project, Create new Folder Interfaces, add there AppPosition class;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace MyApp.Interfaces
    {
    public class AppPosition
    {
    public string Token { get; set; }
    public string Error { get; set; }
    public object StripeToken { get; set; }
    }
    }
    In Interfaces folder also new Interface - IStripe.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace MyApp.Interfaces
    {
    public interface IStripe
    {
    void CreateToken(string Number, string CVC, int ExpiryYear, int ExpiryMonth);
    }
    }
    ``

    and into the StripePage.xaml.cs on Button Clicked Pay
    ``
    void ValidateCard(object sender, EventArgs args)
    {
    IStripe stripe = DependencyService.Get();
    MessagingCenter.Subscribe<IStripe, AppPosition>(this, "gotStripe", DisplayStripe);

            if(inputCardNumber.Text != null && inputCVC.Text != null && inputMonthYear.Text != null)
            {
                int month =12;
                int year = 18;
                try
                {
                    string[] monthyear = inputMonthYear.Text.Split('/');
                     month = int.Parse(monthyear[0]);
                     year = int.Parse(monthyear[1]);
                }
                catch (Exception)
                {
                    inputMonthYear.Text = "";
                }
                finally
                {
                    string cardNumber = inputCardNumber.Text;
                    string cvc = inputCVC.Text;
    
        //this is calling the Android Activity/Iphone Delegate that implements IStripe interface with method CreateToken;
                    stripe.CreateToken(cardNumber, cvc, year, month);
                }
    
            }
    

    }
    the xaml of the StripePage is :
    Stripe.xaml
    <?xml version="1.0" encoding="UTF-8"?>

    <ContentPage.Content>

            <Label Text="Date expiration" />
      <Entry Keyboard="Numeric" x:Name="inputMonthYear" Placeholder="mm/yy" >
      </Entry>
    
            <Label Text="CVC" />
      <Entry Keyboard="Numeric" x:Name="inputCVC" >
        <Entry.Behaviors>
        </Entry.Behaviors>
      </Entry>
    
            <Button Text="Pay 60 €" Grid.Row="0" Grid.Column="0" />
        </StackLayout>
    </ContentPage.Content>
    


    same for iOS: new Class in iOS Project > Stripe_iOS.cs
    using MyApp.Interfaces;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Text;
    using System.Threading.Tasks;
    using Xamarin.Forms;

    namespace MyApp.iOS
    {
    public class Stripe_iOS : IStripe
    {
    public Stripe_iOS()
    {
    }

        public async void CreateToken(string Number, string CVC, int ExpiryYear, int ExpiryMonth)
        {
            var token = new Stripe.Token();
            try
            {
                Stripe.Card stripeCard1 = new Stripe.Card();
                //stripeCard1.Name = App.currentPerson
                stripeCard1.Number = Number;
                stripeCard1.ExpiryYear = ExpiryYear;
                stripeCard1.ExpiryMonth = ExpiryMonth;
                stripeCard1.CVC = CVC;
                token = await Stripe.StripeClient.CreateToken(stripeCard1, "YOUR-STRIPE-PUBLISHER-KEY");
    
                MessagingCenter.Send<IStripe, AppPosition>(this, "gotStripe", new AppPosition()
                {
                    StripeToken = token,
                    Token = token.Id
    
                });
    
            }
            catch (Exception ex)
            {
                MessagingCenter.Send<IStripe, AppPosition>(this, "gotStripe", new AppPosition()
                {
                    Error = ex.Message
                });
    
                Debug.WriteLine("Stripe Error", ex.ToString());
            }
        }
    
    
        }
    

    }
    ``
    Sorry for not formatted text, something is not ok with escaping maybe;))

  • JaydenCarneyJaydenCarney USMember ✭✭

    Sorry to kick up old dust but has there been any revelations on this in recent times, only just looking to integrate stripe now. Is this still the best way to integrate into XF with PCL?

  • JaydenCarneyJaydenCarney USMember ✭✭

    @Svetch
    I tried to implement the code you provided but perhaps there has been changes to the API. I am getting a few errors such as,
    In Stripe_Android.cs & Stripe_iOS.cs
    var token = new Stripe.Token(); <- Token isn't in the namespace.
    Same with Stripe.Card and Stripe.StripeClient

    In StripePage.xaml.cs
    MessagingCenter.Subscribe<IStripe, AppPosition>(this, "gotStripe", DisplayStripe); Display stripe doesn't exist, the Xaml seems to be a little incomplete above.

  • gdkgdk INMember ✭✭✭

    @Svet ch

    Thanks for detail explanation but there some thing not understand i.e where do you add stripe view UI to the portable xaml page.

  • MexmaMexma INMember ✭✭
    edited September 2017

    it seems Xamarin has taken off stripe payment component . following link is no longer available.
    https://components.xamarin.com/view/stripe

    i have similar requirement, need to integrate Stripe component in xamarin forms (collect credit card info and generate token)

  • gdkgdk INMember ✭✭✭

    @Mahesh Thakkar

    Hi, I have already included one of my Ios project, now need to integrated in xamarin forms android project. Then what can I do if they remove 'Stripe payment' component.

  • JaydenCarneyJaydenCarney USMember ✭✭

    Thanks for the reply, I was able to integrate stripe by switching my forms project to .netstandard 2.0 then used stripe.net

  • HashimKSHashimKS USMember ✭✭

    @JaydenCarney Can you please share your sample? I did not find any samples in the nuget..

  • DeepakDYDeepakDY INMember ✭✭✭

    Hello Developers
    I am getting this Exception in this line var token = tokenService.Create(tokenCreateOptions);

    System.Net.Http.HttpRequestException: An error occurred while sending the request ---> System.Net.WebException: Error: NameResolutionFailure
    at System.Net.HttpWebRequest.EndGetRequestStream (System.IAsyncResult asyncResult)

    Any permission required for integrate stripe for xamarin

  • Omer.7911Omer.7911 USMember ✭✭

    I am unable to access Stripe.StripeView, any idea what i am missing ?

Sign In or Register to comment.