Introduction
Stripe is one of the most popular payment gateways, offering a seamless checkout experience for mobile apps. In this guide, we'll walk through integrating Stripe Payments in .NET MAUI (iOS) using Native Interop. By leveraging native bindings, we ensure a smooth and native-like payment flow in our .NET MAUI applications.
Prerequisites
Before we begin, ensure you have the following:
.NET MAUI Installed (Latest version)
Xcode (For iOS development)
A Stripe Account (Create one at Stripe Dashboard)
A GitHub Sample Template for Native Interop (Clone from the provided link)
Basic Understanding of .NET MAUI and Native Bindings (Link)
More Details on Prerequisites: Getting Started with Native Library Interop
Step 1: Clone the Native Interop Template
For ease of integration, clone the GitHub repository that includes a template for Native Interop in .NET MAUI. This will provide the base structure required for integrating Stripe.
This repository will provide necessary configurations, including binding definitions and project setup.
https://github.com/CommunityToolkit/Maui.NativeLibraryInterop
Copy Template Folder
Once cloned, copy the template folder from the repository and place it in a separate directory. Now, navigate to macios/native
and open the Xcode project.
Step 2: Set Up Stripe in iOS
Follow Stripe’s official documentation on accepting payments: Stripe iOS Payment Guide.
Register with Stripe
Create a Stripe account.
Obtain your publishable key and secret key from the Stripe Dashboard.
Download Stripe SDK
Navigate to the Manual Framework Integration section.
Download
Stripe.xcframework.zip
from Stripe’s GitHub or official site.Extract and move
StripeCore.xcframework
StripeUICore.xcframework
Stripe3DS.xcframework
StripePayments.xcframework
StripePaymentsUI.xcframework
StripeApplePay.xcframework
StripePaymentSheet.xcframework
to your Xcode project.
Step 3: Configure Xcode Project
Navigate to
General Settings
in Xcode.Locate Embedded Binaries and add all .xcframework
Ensure that all required dependencies are linked correctly.
Step 4: Add Required Code in Xcode
Now, follow Stripe’s documentation and copy the necessary code snippets in DotnetNewBinding.swift.
For more ease i added code snippets for better understanding.
DotnetNewBinding.swift
import Foundation
import UIKit
import StripePaymentSheet;
@objc(DotnetNewBinding)
public class DotnetNewBinding: NSObject
{
static var paymentSheet: PaymentSheet?
static var paymentIntentClientSecret: String? // Store the client secret manually
@objc(setupPaymentSheet:customerEphemeralKeySecret:clientSecret:publishableKey:)
public static func setupPaymentSheet(
customerId: NSString,
customerEphemeralKeySecret: NSString,
clientSecret: NSString,
publishableKey: NSString
) -> Bool {
print("Setting up payment sheet...")
STPAPIClient.shared.publishableKey = publishableKey as String
var configuration = PaymentSheet.Configuration()
configuration.merchantDisplayName = "Native Interopp :)"
configuration.customer = .init(id: customerId as String, ephemeralKeySecret: customerEphemeralKeySecret as String)
configuration.allowsDelayedPaymentMethods = true
self.paymentIntentClientSecret = clientSecret as String // Store for later use
self.paymentSheet = PaymentSheet(paymentIntentClientSecret: clientSecret as String, configuration: configuration)
if self.paymentSheet == nil {
print("Error: paymentSheet failed to initialize!")
return false
}
print("Payment sheet successfully initialized!")
return true
}
@objc(presentPaymentSheet:completion:)
public static func presentPaymentSheet(viewController: UIViewController, completion: @escaping (Bool, NSString?, NSString?) -> Void) {
DispatchQueue.main.async {
guard let paymentSheet = self.paymentSheet else {
print("Error: paymentSheet is nil. Did you call setupPaymentSheet() first?")
completion(false, nil, nil)
return
}
paymentSheet.present(from: viewController) { paymentResult in
switch paymentResult {
case .completed:
print("Payment Successful")
fetchPaymentDetails { paymentIntentId, status in
completion(true, paymentIntentId as NSString?, status as NSString?)
}
case .canceled:
print("Payment Canceled")
completion(false, nil, nil)
case .failed(let error):
print("Payment failed: \(error)")
completion(false, nil, nil)
}
}
}
}
// Fetch payment details using the stored paymentIntentClientSecret
private static func fetchPaymentDetails(completion: @escaping (String?, String?) -> Void) {
guard let clientSecret = self.paymentIntentClientSecret else {
print("Error: paymentIntentClientSecret is nil")
completion(nil, nil)
return
}
STPAPIClient.shared.retrievePaymentIntent(withClientSecret: clientSecret) { paymentIntent, error in
if let error = error {
print("Error retrieving payment intent: \(error.localizedDescription)")
completion(nil, nil)
return
}
if let paymentIntent = paymentIntent {
let paymentId = paymentIntent.stripeId
let status = String(paymentIntent.status.rawValue) // Convert Int to String
print("Payment Intent ID: \(paymentId), Status: \(status)")
completion(paymentId, status)
} else {
completion(nil, nil)
}
}
}
}
Build the Xcode project and check if it compiles successfully.
Step 5: Implement Native Interop in .NET MAUI (iOS)
Now, integrate Stripe with .NET MAUI iOS Project using Native Interop.
✨ Modify ApiDefinition.cs
In your .NET MAUI iOS project, add the following bindings in ApiDefinition.cs
to expose native Stripe functionality:
ApiDefinition.cs
using Foundation;
using UIKit;
using System;
namespace NewBindingMaciOS
{
// @interface DotnetNewBinding : NSObject
[BaseType (typeof(NSObject))]
interface DotnetNewBinding
{
[Static]
[Export("setupPaymentSheet:customerEphemeralKeySecret:clientSecret:publishableKey:")]
bool SetupPaymentSheet(NSString customerId, NSString customerEphemeralKeySecret, NSString clientSecret, NSString publishableKey);
// Use Action<bool> as a callback for the result
[Static]
[Export("presentPaymentSheet:completion:")]
void PresentPaymentSheet(UIViewController viewController, Action<bool, NSString, NSString> completion);
}
}
Now copy all the following frameworks: StripeCore.xcframework
, StripeUICore.xcframework
, Stripe3DS.xcframework
, StripePayments.xcframework
, StripePaymentsUI.xcframework
, StripeApplePay.xcframework
, and StripePaymentSheet.xcframework
. Add them to this project as well so we can include native references.
NewBinding.MaciOS.Binding.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net9.0-ios;net9.0-maccatalyst</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<IsBindingProject>true</IsBindingProject>
<!--
Enable trim analyzers for class libraries.
To learn more, see: https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming
-->
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
<ItemGroup>
<ObjcBindingApiDefinition Include="ApiDefinition.cs" />
<ObjcBindingCoreSource Include="StructsAndEnums.cs" />
</ItemGroup>
<!-- Reference to Xcode project -->
<ItemGroup>
<XcodeProject Include="../native/NewBinding/NewBinding.xcodeproj">
<SchemeName>NewBinding</SchemeName>
<!-- Metadata applicable to @(NativeReference) will be used if set, otherwise the following defaults will be used:
<Kind>Framework</Kind>
<SmartLink>true</SmartLink>
-->
<Visible>False</Visible>
</XcodeProject>
<NativeReference Include="StripePaymentSheet.xcframework">
<Kind>Framework</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>true</ForceLoad>
<Frameworks>UIKit Foundation</Frameworks>
<Visible>true</Visible>
</NativeReference>
<NativeReference Include="Stripe3DS2.xcframework">
<Kind>Framework</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>true</ForceLoad>
<Frameworks>UIKit Foundation</Frameworks>
<Visible>true</Visible>
</NativeReference>
<NativeReference Include="StripeApplePay.xcframework">
<Kind>Framework</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>true</ForceLoad>
<Frameworks>UIKit Foundation</Frameworks>
<Visible>true</Visible>
</NativeReference>
<NativeReference Include="StripeCore.xcframework">
<Kind>Framework</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>true</ForceLoad>
<Frameworks>UIKit Foundation</Frameworks>
<Visible>true</Visible>
</NativeReference>
<NativeReference Include="StripePayments.xcframework">
<Kind>Framework</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>true</ForceLoad>
<Frameworks>UIKit Foundation</Frameworks>
<Visible>true</Visible>
</NativeReference>
<NativeReference Include="StripePaymentsUI.xcframework">
<Kind>Framework</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>true</ForceLoad>
<Frameworks>UIKit Foundation</Frameworks>
<Visible>true</Visible>
</NativeReference>
<NativeReference Include="StripeUICore.xcframework">
<Kind>Framework</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>true</ForceLoad>
<Frameworks>UIKit Foundation</Frameworks>
<Visible>true</Visible>
</NativeReference>
<BundleResource Include="StripePaymentSheet.xcframework/**/Stripe_StripePaymentSheet.bundle" />
</ItemGroup>
</Project>
Build the iOS project and check if it builds successfully. It will take some time to compile.
Step 6: Access Stripe in .NET MAUI
Now, you can call the Stripe in your .NET MAUI project:
MainPage.cs
namespace MauiSample;
#if IOS || MACCATALYST
using NewBinding = NewBindingMaciOS.DotnetNewBinding;
#elif ANDROID
using NewBinding = NewBindingAndroid.DotnetNewBinding;
#elif (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID)
using NewBinding = System.Object;
#endif
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
// Call the native binding, which will append a platform specific string to the input string
var labelText = NewBinding.GetString("Community Toolkit");
newBindingSampleLabel.Text = "Hello, " + labelText;
}
async void OnDocsButtonClicked(object sender, EventArgs e)
{
#if IOS || MACCATALYST
if(await ShowPaymentSheetAsync(payment))
{
}
#endif
}
#if IOS || MACCATALYST
public Task<bool> ShowPaymentSheetAsync(Payment payment)
{
try
{
var window = UIApplication.SharedApplication.KeyWindow;
var currentUIViewController = window?.RootViewController;
if (currentUIViewController == null)
{
return Task.FromResult(false);
}
bool isInitialized = NewBinding.SetupPaymentSheet(
new NSString(payment.Customer),
new NSString(payment.EphemeralKey),
new NSString(payment.PaymentIntent),
new NSString(payment.PublishableKey)
);
if (isInitialized)
{
var tcs = new TaskCompletionSource<bool>();
MainThread.BeginInvokeOnMainThread(() =>
{
NewBinding.PresentPaymentSheet(currentUIViewController, (success, paymentIntentId, status) =>
{
if (success)
{
tcs.SetResult(true);
}
else
{
tcs.SetResult(false);
}
});
});
return tcs.Task;
}
}
catch (Exception ex)
{
}
return Task.FromResult(false);
}
}
This ensures that Stripe’s payment flow is properly handled in your .NET MAUI iOS app.
GitHub Repository
You can find the complete implementation in my GitHub repository:
🔗 GitHub Repository for Stripe Integration
Conclusion
Congratulations! You have successfully integrated Stripe Payments in a .NET MAUI iOS app using Native Interop. This approach allows you to utilize Stripe’s native capabilities while keeping the .NET MAUI ecosystem intact.
🚀 Stay tuned for the next part, where we cover Stripe integration in .NET MAUI Android!