Apple Pay, Stripe & Xamarin

Apple Pay, Stripe & Xamarin

I spent a fair amount of time researching the best practices for implementing Apple Pay, Xamarin, and Stripe, but came away mostly empty-handed. Here's what I did to make it all work together nicely.

Begin by setting up Apple Pay in your Stripe dashboard.

While in stripe, go to Settings -> Payment Methods -> Select Configure next to Apple Pay.

Under iOS certificates, click 'Add new application' button.

This will pop up a dialog like this:

screenshot from Stripe

This will download a certificate signing request file. Click Continue.


  • Select the link provided - 'this page' to go to Apple Developer Center.
  • Sign in and click the + button next to the 'Identifiers' title.
  • Select Merchant IDs and click 'Continue' to create your Merchant ID.
  • Click 'Register'.
  • Select your new Merchant ID from the list.
  • Click on 'Create Certificate'.
  • For 'Will payments associated with this Merchant ID be processed exclusively in China mainland?' -> Select 'No'.
  • Select 'Choose File' when prompted to 'Upload a Certificate Signing Request'.
  • Find your previously downloaded 'stripe.certSigning.Request' file and click 'Continue'.
  • Select 'Download' to get your 'apple_pay.cer'.
  • Now switch back to your Stripe tab - (should still be in another tab) and hit 'Continue'.
  • Click 'Upload certificate' and click 'Submit'.

Stripe and Apple Pay are now ready to go.

To set up your app, open the Entitlements.plist file in your .iOS Xamarin project.

Enable the Apple Pay Entitlement and add your Merchant ID to the list. Entitlements Configuration

To use Apple Pay in your project, you'll need to reference the following NuGet packages.

  • Stripe.iOS
  • Xamarin.iOS

Create a Service in your iOS Xamarin project that implements PKPaymentAuthorizationControllerDelegate. (public class PaymentService : PKPaymentAuthorizationControllerDelegate)

Implementing the abstract class will create the following two methods:

public override void DidAuthorizePayment(PKPaymentAuthorizationController controller, PKPayment payment, [BlockProxy(typeof(NIDActionArity1V213))] Action completion) => throw new NotImplementedException();

public override void DidFinish(PKPaymentAuthorizationController controller) => throw new NotImplementedException();

There will also be an error that it can't find the namespace for NIDActionArity1V213. Just delete this portion [BlockProxy(typeof(NIDActionArity1V213))] of the DidAuthorizePayment method.

Next, you will need to initialize your service with the Merchant ID and your Stripe Publishable Key. (This can be done in your AppDelegate.cs or the constructor of your service.)

var client = new ApiClient(apiConfiguration.StripePublishableKey);
client.Configuration.AppleMerchantIdentifier = apiConfiguration.MerchantId;
ApiClient.SharedClient.PublishableKey = apiConfiguration.StripePublishableKey;

Create an interface in your shared Xamarin project for use within your pages (ie. IPaymentService.cs)

Add the following methods:

event EventHandler AuthorizationComplete;
bool CanMakePayments { get; }
void AuthorizePayment(decimal total);

Add this interface to your service in the iOS service and implement its methods.

public class PaymentService : PKPaymentAuthorizationControllerDelegate, IPaymentService

Register your service and interface in your AppDelegate.cs.


Now add the following code to your new methods.

public bool CanMakePayments => StripeSdk.DeviceSupportsApplePay;

public void AuthorizePayment(decimal total)
    var request = new PKPaymentRequest
        PaymentSummaryItems = new[] { new PKPaymentSummaryItem { Label = "GoSnapShop", Amount = new NSDecimalNumber(total), Type = PKPaymentSummaryItemType.Final } },
       CountryCode = "US",
       CurrencyCode = "USD",
        MerchantIdentifier = ApiConfiguration.MerchantId,
        MerchantCapabilities = PKMerchantCapability.ThreeDS,
        SupportedNetworks = new[] { PKPaymentNetwork.Amex, PKPaymentNetwork.MasterCard, PKPaymentNetwork.Visa }

    var authorization = new PKPaymentAuthorizationController(request)
        Delegate = (IPKPaymentAuthorizationControllerDelegate)Self


public override void DidAuthorizePayment(PKPaymentAuthorizationController controller, PKPayment payment, Action completion)
    ApiClient.SharedClient.CreateToken(payment, TokenComplete);

protected void TokenComplete(Token token, NSError arg1) => AuthorizationComplete.Invoke(this, token.TokenId);

public override void DidFinish(PKPaymentAuthorizationController controller) => controller.Dismiss(null);

You're now ready to implement your service in your ViewModel.

Add IPaymentService paymentService to the constructor of your ViewModel to inject it as a dependency and then initialize it as a property.

protected IPaymentService PaymentService { get; }
public AddCreditCardViewModel(IRouteNavigator routeNavigator, IShoppingCartService shoppingCartService,
IStorageService storageService, IOrderService orderService, IPaymentService paymentService)
    RouteNavigator = routeNavigator;
    ShoppingCartService = shoppingCartService;
    StorageService = storageService;
    PaymentService = paymentService;

You can also subscribe to your event in the constructor.

PaymentService.AuthorizationComplete += PaymentService_AuthorizationComplete;

private void PaymentService_AuthorizationComplete(object sender, string token)
    PaymentToken = token;
    Task.Run(async () => await PlaceOrder());

Next, you'll need to wire up your button. In your page's xaml - add the button (or image).

Your button IsVisible will be bound to a property in your view model.

private bool IsApplePayReadyField;
public bool IsApplePayReady
    get => IsApplePayReadyField;
    set { IsApplePayReadyField = value; NotifyOfChange(); }

This will be initialized in your OnActivate method.

IsApplePayReady = PaymentService.CanMakePayments;

Now, you're ready to capture the payment.

On the server side, you'll create an API method that allows you to pass the amount to capture along with your token.

In my case, I'm using a model that will be part of the order save method.

var order = new Order
    OrderId = 1,
    Total = 100.00,
    PaymentInformation = new PaymentInformation { NameOnCard = "Mobile Wallet Payment", WalletPaymentToken = PaymentToken };

The controller method is implemented as follows:

public async Task AddOrder([FromBody] Models.Order order)
        var savedOrder = await OrderService.AddOrder(Mapper.Map(order));
        var mapped = Mapper.Map(savedOrder);
        mapped.PaymentResult = Mapper.Map(await PaymentService.AddPayment(savedOrder.OrderId, savedOrder.Total, Mapper.Map(order.PaymentInformation)));
        return Json(mapped);
    catch (Exception e)
        return StatusCode(StatusCodes.Status500InternalServerError, e.Message);

The PaymentService.AddPayment is then created to capture the payment.

public async Task AddPayment(int orderId, decimal amount, PaymentInformation paymentInformation)
    string tokenId = paymentInformation.WalletPaymentToken;
    if (string.IsNullOrWhiteSpace(tokenId))
        // handle regular credit card payments

    var chargeOptions = new ChargeCreateOptions
        Amount = (long)( amount * 100 ),
        Currency = "usd",
        Description = " Mobile Order",
        Metadata = new Dictionary { { "OrderId", orderId.ToString() } },
        Source = tokenId

    var service = new ChargeService();
    var result = await service.CreateAsync(chargeOptions);

    var payment = new DB.Payment
        OrderId = orderId,
        Amount = amount,
        Name = paymentInformation.NameOnCard,
        ZipCode = paymentInformation.ZipCode,
        Response = result.Outcome.ToJson(),
        ResponseCode = result.Status,
        ReceiptUrl = result.ReceiptUrl,
        AuthCode = result.Id


    await PaymentRepository.SaveAsync();

    return Mapper.Map(payment);

The following result will be returned from the ChargeService.CreateAsync().

  "id": "ch_1IEdnPIzLphRtLIE89seDJAL",
  "object": "charge",
  "amount": 198,
  "amount_captured": 198,
  "amount_refunded": 0,
  "application": null,
  "application_fee": null,
  "application_fee_amount": null,
  "authorization_code": null,
  "balance_transaction": "txn_1IEdnPIzLphRtLIERwOqJS6s",
  "billing_details": {
    "address": {
      "city": null,
      "country": null,
      "line1": null,
      "line2": null,
      "postal_code": null,
      "state": null
    "email": null,
    "name": null,
    "phone": null
  "calculated_statement_descriptor": "JUSTIN TEST ACCOUNT",
  "captured": true,
  "created": 1611852999,
  "currency": "usd",
  "customer": null,
  "description": " Mobile Order",
  "destination": null,
  "dispute": null,
  "disputed": false,
  "failure_code": null,
  "failure_message": null,
  "fraud_details": {
    "stripe_report": null,
    "user_report": null
  "invoice": null,
  "level3": null,
  "livemode": false,
  "metadata": {
    "OrderId": "29"
  "on_behalf_of": null,
  "order": null,
  "outcome": {
    "network_status": "approved_by_network",
    "reason": null,
    "risk_level": "normal",
    "risk_score": 42,
    "rule": null,
    "seller_message": "Payment complete.",
    "type": "authorized"
  "paid": true,
  "payment_intent": null,
  "payment_method": "card_1IEdnJIzLphRtLIEKPw7oitN",
  "payment_method_details": {
    "ach_credit_transfer": null,
    "ach_debit": null,
    "acss_debit": null,
    "alipay": null,
    "au_becs_debit": null,
    "bacs_debit": null,
    "bancontact": null,
    "card": {
      "brand": "visa",
      "checks": {
        "address_line1_check": null,
        "address_postal_code_check": null,
        "cvc_check": null
      "country": "US",
      "description": null,
      "exp_month": 12,
      "exp_year": 2022,
      "fingerprint": "v3sKq9NqSbnF3ZLd",
      "funding": "credit",
      "iin": null,
      "installments": null,
      "issuer": null,
      "last4": "4242",
      "moto": null,
      "network": "visa",
      "three_d_secure": null,
      "wallet": {
        "amex_express_checkout": null,
        "apple_pay": {},
        "dynamic_last4": "4242",
        "google_pay": null,
        "masterpass": null,
        "samsung_pay": null,
        "type": "apple_pay",
        "visa_checkout": null
    "card_present": null,
    "eps": null,
    "fpx": null,
    "giropay": null,
    "grabpay": null,
    "ideal": null,
    "interac_present": null,
    "klarna": null,
    "multibanco": null,
    "oxxo": null,
    "p24": null,
    "sepa_debit": null,
    "stripe_account": null,
    "type": "card",
    "wechat": null
  "receipt_email": null,
  "receipt_number": null,
  "receipt_url": "",
  "refunded": false,
  "refunds": {
    "object": "list",
    "data": [],
    "has_more": false,
    "url": "/v1/charges/ch_1IEdnPIzLphRtLIE89seDJAL/refunds"
  "review": null,
  "shipping": null,
  "source": {
    "id": "card_1IEdnJIzLphRtLIEKPw7oitN",
    "object": "card",
    "account": null,
    "address_city": null,
    "address_country": null,
    "address_line1": null,
    "address_line1_check": null,
    "address_line2": null,
    "address_state": null,
    "address_zip": null,
    "address_zip_check": null,
    "available_payout_methods": null,
    "brand": "Visa",
    "country": "US",
    "currency": null,
    "customer": null,
    "cvc_check": null,
    "default_for_currency": null,
    "description": null,
    "dynamic_last4": "4242",
    "exp_month": 12,
    "exp_year": 2022,
    "fingerprint": "v3sKq9NqSbnF3ZLd",
    "funding": "credit",
    "iin": null,
    "issuer": null,
    "last4": "4242",
    "metadata": {},
    "name": null,
    "tokenization_method": "apple_pay"
  "source_transfer": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "succeeded",
  "transfer": null,
  "transfer_data": null,
  "transfer_group": null


comments powered by Disqus

Follow Us

Latest Posts

subscribe to our newsletter