Agile App Co.

Stripe and GooglePay with Xamarin Forms

If you have already followed the article we made on setting up ApplePay, you should just need the additional code to provide the Google Pay button on Android (plus a snippet for MainActivity.cs). If you haven't read it this will probably not make a huge amount of sense so it is advised to read it before continuing. 

You will notice that there is an additional class within the following Button Renderer which is in addition to the PaymentButton class we added in the iOS renderer. The additional class here is FragmentRetainInstanceSupport. You will also need to amend various parameters we have used for example the Publishable key used in stripe. 

In theory you should only need Stripe.net nuget on the Android project and Xamarin.GooglePlayServices.Wallet and Xamarin.GooglePlayServices.Base. 

Once you want to move to production... you need to complete this Google Pay application. Be sure to change the .SetEnvironment(WalletConstants.EnvironmentTest) code below to production. 

 
[assembly: ExportRenderer(typeof(RoundApp.Helpers.PaymentButton), typeof(PaymentButtonRenderer))]
namespace RoundApp.Droid.Renderers
{
    public class PaymentButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer, GoogleApiClient.IOnConnectionFailedListener, GoogleApiClient.IConnectionCallbacks
    {        
        const int MASKED_WALLET_REQUEST = 501;
 
        public Activity activity { get; private set; }
        public PaymentButton _button { get; set; }
        public FragmentRetainInstanceSupport _paymentActivity { get; private set; }
 
        public PaymentButtonRenderer(Context context) : base(context)
        {           
 
        }
 
 
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);
            
            activity = this.Context as Activity;
            _button = Element as PaymentButton;
 
            
            if (_button != null)
                _button.Clicked += _button_Clicked;
           
            if (Element != null)
            {               
                Element.Text = "Pay (GooglePay)";              
            }
 
 
        }
 
        private void _button_Clicked(object sender, EventArgs e)
        {
            PaymentDataRequest request = _paymentActivity.createPaymentDataRequest(_button.Amount.ToString("0.00"));
            if (request != null)
            {
                _paymentActivity.LoadPaymentData(request);
                
                
            }
        }
 
        protected override void OnLayout(bool changed, int l, int t, int r, int b)
        {
            base.OnLayout(changed, l, t, r, b);
            _paymentActivity = new FragmentRetainInstanceSupport(activity);
 
        }
 
        public void OnConnectionFailed(ConnectionResult result)
        {
            throw new NotImplementedException();
        }
 
        public void OnConnected(Bundle connectionHint)
        {
            throw new NotImplementedException();
        }
 
        public void OnConnectionSuspended(int cause)
        {
            throw new NotImplementedException();
        }
        
 
 
    }
 
 
    public class FragmentRetainInstanceSupport : FragmentActivity, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener
    {
        const int MASKED_WALLET_REQUEST = 501;
        public const int LOAD_PAYMENT_DATA_REQUEST_CODE = 504;
      
        public Android.Support.V4.App.FragmentTransaction sup;
 
        public Activity _activity { get; private set; }
        public PaymentsClient paymentsClient { get; private set; }
      
        private void handleGooglePayResult(Intent data)
        {
            PaymentData paymentData = PaymentData.GetFromIntent(data);
            if (paymentData != null) {
 
            }
            
        }
        public void LoadPaymentData(PaymentDataRequest request) {
 
          
 
 
            AutoResolveHelper.ResolveTask(
                     paymentsClient.LoadPaymentData(request),
                     _activity,
                     LOAD_PAYMENT_DATA_REQUEST_CODE);
            // LOAD_PAYMENT_DATA_REQUEST_CODE is a constant integer
            // of your choice, similar to what you would use
            // in startActivityForResult
        }
        private PaymentMethodTokenizationParameters createTokenizationParameters()
        {
            #if (DEBUG)
                        return PaymentMethodTokenizationParameters.NewBuilder()
                            .SetPaymentMethodTokenizationType(
                                WalletConstants.PaymentMethodTokenizationTypePaymentGateway)
                            .AddParameter("gateway", "stripe")
                            .AddParameter("stripe:publishableKey",
                                App.StripePublishableKeyTEST)
                            .AddParameter("stripe:version", "2019-05-16")
                            .Build();
            #else
                        return PaymentMethodTokenizationParameters.NewBuilder()
                            .SetPaymentMethodTokenizationType(
                                WalletConstants.PaymentMethodTokenizationTypePaymentGateway)
                            .AddParameter("gateway", "stripe")
                            .AddParameter("stripe:publishableKey",
                                App.StripePublishableKey)
                            .AddParameter("stripe:version", "2019-05-16")
                            .Build();
            #endif
 
        }
        public PaymentDataRequest createPaymentDataRequest(string decimalAmount)
        {
            var pMethods = new List<Java.Lang.Integer> { (Java.Lang.Integer)WalletConstants.CardNetworkAmex,
                           (Java.Lang.Integer) WalletConstants.CardNetworkDiscover,
                            (Java.Lang.Integer)WalletConstants.CardNetworkVisa,
                            (Java.Lang.Integer)WalletConstants.CardNetworkMastercard } as ICollection<Java.Lang.Integer>;
 
            return PaymentDataRequest.NewBuilder()
                .SetTransactionInfo(
                    TransactionInfo.NewBuilder()
                        .SetTotalPriceStatus(WalletConstants.TotalPriceStatusFinal)
                        .SetTotalPrice(decimalAmount) // "10.00"
                        .SetCurrencyCode("GBP")
                        .Build())
                .AddAllowedPaymentMethod(WalletConstants.CardClassCredit)
                .AddAllowedPaymentMethod(WalletConstants.PaymentMethodTokenizedCard)
                .SetCardRequirements(
                    CardRequirements.NewBuilder()
                    .AddAllowedCardNetworks(pMethods)
                        .Build())
                .SetPaymentMethodTokenizationParameters(createTokenizationParameters())
                .Build();
        }
        public FragmentRetainInstanceSupport(Activity activity)
        {
 
            _activity = activity;
 
            paymentsClient = WalletClass.GetPaymentsClient(
             _activity,
             new WalletClass.WalletOptions.Builder()
                     .SetEnvironment(WalletConstants.EnvironmentTest)
                     .Build()
            );
 
            IsReadyToPayRequest request = IsReadyToPayRequest.NewBuilder()
                .AddAllowedPaymentMethod(WalletConstants.CardClassCredit)
                .AddAllowedPaymentMethod(WalletConstants.CardClassDebit)
                .Build();
 
            paymentsClient.IsReadyToPay(request);
 
 
 
 
        }
 
 
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
 
 
 
 
 
 
        }
        public void OnConnected(Bundle connectionHint)
        {
            throw new NotImplementedException();
        }
 
        public void OnConnectionSuspended(int cause)
        {
            throw new NotImplementedException();
        }
 
        public void OnConnectionFailed(ConnectionResult result)
        {
            throw new NotImplementedException();
        }
    }
}
 
 
You will also need to add some code to the MainActivity file to handle when a payment response comes in. 
 
 
 
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);
            switch (requestCode)
            {
                case 504:
                    {
                        switch (resultCode)
                        {
                            case Result.Ok:
                                {
                                    if (data != null)
                                    {
                                        PaymentData paymentData = PaymentData.GetFromIntent(data);
                                        if (paymentData != null)
                                        {
                                            //paymentData.PaymentMethodToken.Token
                                            var tokenData = paymentData.PaymentMethodToken.Token;
                                            var t = JsonConvert.DeserializeObject<Helpers.AndroidPaymentData>(tokenData);
                                            if (t != null)
                                            {
                                                MessagingCenter.Send(App.Current, Helpers.Messaging.GooglePayTokenReceived, t);
                                            }
                                            else
                                            {
                                                MessagingCenter.Send(App.Current, Helpers.Messaging.GooglePayTokenReceived, new Stripe.PaymentMethodCreateOptions
                                                {
                                                    BillingDetails = new Stripe.BillingDetailsOptions
                                                    {
                                                        Address = new Stripe.AddressOptions
                                                        {
                                                            Line1 = paymentData.ShippingAddress.Address1,
                                                            Line2 = paymentData.ShippingAddress.Address2,
                                                            PostalCode = paymentData.ShippingAddress.PostalCode,
                                                            City = paymentData.ShippingAddress.Address3,
                                                            Country = paymentData.ShippingAddress.CountryCode,
                                                            State = paymentData.ShippingAddress.Address4
                                                        },
                                                        Email = paymentData.Email
                                                    }
                                                });
                                            }
 
                                        }
                                    }
                                    break;
                                }
                            case Result.Canceled:
                                {
                                    break;
                                }
                                //case AutoResolveHelper.ResultError:
                                //    {
                                //        Statuses status = AutoResolveHelper.GetStatusFromIntent(data);
                                //        // Log the status for debugging
                                //        // Generally there is no need to show an error to
                                //        // the user as the Google Payment API will do that
                                //        break;
                                //    }
 
                        }
                        break; // Breaks the case LOAD_PAYMENT_DATA_REQUEST_CODE
                    }
                    // Handle any other startActivityForResult calls you may have made.
 
            }
        }
 
 
 
 
UPDATE:
 
There is a lot to do to get your android pay in to production. You can request access to production here 
 
There is also additional work to get the Google Pay button in to your renderer. First download the assets from Google here. You then need to bring certain resources in to your project.
 
Here are some screenshots of how we did it. 
 
 
 

 

 

 

You will also need to ensure your Stripe code is SCA compliant. We will write another blog entry with info on this soon!