NAXAM / braintreedropin-ios-binding

Xamarin Binding Library - Braintree DropIn
MIT License
1 stars 1 forks source link

Code for users using Xamarin.Forms #3

Open kcrandall opened 5 years ago

kcrandall commented 5 years ago

I made this using these bindings and thought it might save others time. So here is a Xamarin.Forms renderer for drop-in.

iOS Renderer

using System;
using System.ComponentModel;
using System.Reflection;
using CoreGraphics;
using UIKit;
using Xamarin.Forms.Platform.iOS;
using Xamarin.Forms;

using BraintreeCore;
using BraintreeDropIn;
using BraintreeUIKit;

using App;
using App.iOS.Renderers;

[assembly: ExportRenderer(typeof(BraintreeDropInUIButton), typeof(BraintreeDropInUIButtonRenderer))]
namespace App.iOS.Renderers
{
    public class BraintreeDropInUIButtonRenderer : ViewRenderer<BraintreeDropInUIButton, UIButton>
    {
        public UIButton Button { get; private set; }
        public BraintreeDropInUIButton FormsButton { get; private set; }

        protected override void OnElementChanged(ElementChangedEventArgs<BraintreeDropInUIButton> e)
        {
            base.OnElementChanged(e);
            BraintreeDropInUIButton formsButton = (Element as BraintreeDropInUIButton);
            this.FormsButton = formsButton;
            if (Control == null)
            {
                this.Button = new UIButton(new CGRect(0, 64, 320, 44)) { };
                this.Button.SetTitle(formsButton.Title, UIControlState.Normal);
                this.Button.SetTitleColor(UIColor.Purple, UIControlState.Normal);

                // Perform any additional setup after loading the view, typically from a nib.
                this.Button.AccessibilityIdentifier = "Payment";
                this.Button.TouchUpInside += delegate
                {
                    ShowBraintreeDropIn(formsButton.TokenizedKey);
                };
                SetNativeControl(this.Button);
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
        }

        private void ShowBraintreeDropIn(string clientTokenOrTokenizationKey)
        {
            var request = new BTDropInRequest
            {
                ApplePayDisabled = true,
                ThreeDSecureVerification = true
            };
            BTDropInController dropIn = new BTDropInController(clientTokenOrTokenizationKey, request, HandleBTDropInControllerHandler);
            //https://forums.xamarin.com/discussion/41976/how-to-get-uiviewcontroller-associated-with-a-page
            IVisualElementRenderer renderer = Platform.GetRenderer(this.FormsButton.Page);
            if (renderer == null)
            {
                renderer = Platform.CreateRenderer(this.FormsButton.Page);
                Platform.SetRenderer(this.FormsButton.Page, renderer);
            }
            UIViewController viewController = renderer.ViewController;

            viewController.PresentViewController(dropIn, false, null);
        }

        void HandleBTDropInControllerHandler(BTDropInController controller, BTDropInResult result, Foundation.NSError error)
        {
            if (error != null)
            {
                System.Diagnostics.Debug.WriteLine("DROPIN ERROR");
                this.FormsButton.Callback.Invoke(new BraintreeDropInResponse()
                {
                    ExitCode = BraintreeDropInExitCode.Error,
                    ErorrMessage = error.ToString(),
                });
            }
            else if (result != null && result.Cancelled == true)
            {
                System.Diagnostics.Debug.WriteLine("DROPIN CANCELLED");
                this.FormsButton.Callback.Invoke(new BraintreeDropInResponse()
                {
                    ExitCode = BraintreeDropInExitCode.Canceled
                });
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("DROPIN SUCCESS");

                BTUIKPaymentOptionType selectedPaymentOptionType = result.PaymentOptionType;

                BTPaymentMethodNonce selectedPaymentMethod = result.PaymentMethod;
                string nonce = selectedPaymentMethod.Nonce;
                string type = selectedPaymentMethod.Type;
                UIView selectedPaymentMethodIcon = result.PaymentIcon;
                string selectedPaymentMethodDescription = result.PaymentDescription;
                this.FormsButton.Callback.Invoke(new BraintreeDropInResponse()
                { 
                    Nonce = nonce,
                    Type = type,
                    PaymentDescription = selectedPaymentMethodDescription,
                    ExitCode = BraintreeDropInExitCode.Succes
                });
            }
            controller.DismissViewController(true, null);
        }
    }
}

Forms View

using System;
using Xamarin.Forms;
namespace App
{
    public enum BraintreeDropInExitCode { Error, Canceled, Succes }
    public class BraintreeDropInResponse
    {
        public BraintreeDropInExitCode ExitCode { get; set; }
        public string Nonce { get; set; }
        public string Type { get; set; }
        public string PaymentDescription { get; set; }
        public string ErorrMessage { get; set; }
    }
    public class BraintreeDropInUIButton : View
    {
        /// <summary>
        /// Required - the parent page this button is in
        /// </summary>
        /// <value>The page.</value>
        public Page Page { get; set; }

        /// <summary>
        /// Invoked after the drop in UI is dismised.
        /// </summary>
        /// <value>The callback.</value>
        public Action<BraintreeDropInResponse> Callback { get; set; }

        public string Title { get; set; }
        /// <summary>
        /// Braintree tokenized key generated on their website for the client SDK.
        /// </summary>
        public string TokenizedKey { get; set; } 
        public BraintreeDropInUIButton()
        {

        }
    }
}

Example XAML

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:local="clr-namespace:App" 
    x:Class="App.AddPaymentMethodPage"
    x:Name="page">
    <ContentPage.Content>
        <local:BraintreeDropInUIButton
            x:Name="DropInButton"/>
    </ContentPage.Content>
</ContentPage>

Example Xaml.cs

using System;
using System.Collections.Generic;

using Xamarin.Forms;

namespace App
{
    public partial class AddPaymentMethodPage : ContentPage
    {
        public AddPaymentMethodPage()
        {
            InitializeComponent();
            this.DropInButton.Page = this;
        }
    }
}
gsemenov commented 5 years ago

Hello, great code, thanks. I just have one issue when use your example. When 3d verification and open in browser where user type code it do not redirect back to the app. On debug I see that BTAppSwitch.HandleOpenURL(url, options); executed, but it does not return back and does not execute HandleBTDropInControllerHandler. Did you have the same issue? or does it work for you?