Step-by-Step Guide to Stripe Integration in .NET MAUI Android via Native Interopt


Introduction
Seamless and secure payments are crucial for mobile apps, but finding a well-maintained .NET MAUI SDK can be challenging. Stripe provides a reliable payment gateway, but integrating it into .NET MAUI Android requires ensuring compatibility with your project's dependencies. In this guide, weβll walk you through a step-by-step process to efficiently add Stripe payments to our app using Native Interop, addressing common challenges developers face during their first integration.
Prerequisites
Before integration, ensure we have the following prerequisites installed:
Stripe API Keys (Obtain from Stripe Dashboard)
I will explain how to integrate by steps-by-steps
π Step 1: Set Up Stripe in the Stripe Dashboard
Create a Stripe account if you havenβt already.
Navigate to Developers β API Keys and copy your Publishable Key and Secret Key.
Enable all options for payments like Alipay, PayPal etc.
π Step 2: Clone the template
To simplify the integration process, it's best to start with an existing .NET MAUI Native Interop template instead of creating a new project from scratch, which can lead to many issues. So, we can simply clone the template.
π Recommended Template:
π CommunityToolkit MAUI Native Library Interop
This template already includes an Android project structure, making it easier to integrate Stripe without running into unnecessary errors.
Alternatively, we can make our own template, but keep in mind that additional adjustments may be required.
π Step 3: Run the Template
Before making any modifications, itβs crucial to run the template to ensure everything is set up correctly. This step helps identify any potential errors early on.
β Steps to Run the Template:
1οΈβ£ Open the Cloned Project in our environment like Visual Studio
2οΈβ£ Check for Initial Errors β One common issue is: NewBindingAndroid
not found.
3οΈβ£ Remove macOS Project (if on Windows) β Since macOS is not required for Android development, temporarily removing it can prevent compatibility issues.
4οΈβ£ Delete bin/
and obj/
Folders β Clearing these temporary build files ensures a fresh start.
5οΈβ£ Run NewBinding.Android
.Binding
Project Separately β This step compiles the Android binding project first.
6οΈβ£ Finally, Build the .NET MAUI Project β Once the binding project is successfully built, proceed to compile the entire .NET MAUI project.
π Step 4: Add Stripe library in native project
To integrate Stripe into your .NET MAUI Android app, we first need to add the Stripe Android SDK to our projectβs Gradle build file.
π§ Steps to Add Stripe:
1οΈβ£ Open Android native project inside the android studio
2οΈβ£ Navigate to the build.gradle.kts
file (usually located under android/native/newbinding
).
3οΈβ£ Add the following Stripe dependency inside the dependencies
block:
implementation("com.stripe:stripe-android:21.3.1")
4οΈβ£ Sync the Gradle project and wait for dependencies to install.
5οΈβ£ Rebuild the project to ensure everything is set up correctly.
Step 5: Write native code in android project
Now that we have added the Stripe SDK, we can proceed with integrating Stripe's Payment Sheet into our .NET MAUI Android project. We'll follow Stripeβs official documentation to ensure a smooth setup.
π Reference: Stripe Payment Sheet Documentation
π Project Structure
Navigate to the following directory in your project:
android/native/newbinding/src/main/java/com/example
Here, create a new class named StripeIntegration.cs
.
π Writing the Stripe Payment Sheet Code
To efficiently integrate Stripe, open the Android project in Android Studio and add the following code inside the StripeIntegration.cs file:
package com.example.newbinding;
import android.app.Activity;
import android.widget.Toast;
import androidx.activity.ComponentActivity;
import com.stripe.android.PaymentConfiguration;
import com.stripe.android.paymentsheet.PaymentSheet;
import com.stripe.android.paymentsheet.PaymentSheetResult;
public class StripeIntegration {
static PaymentSheet paymentSheet;
static Activity activity;
static PaymentResultListener paymentResultListener;
public interface PaymentResultListener {
void onPaymentResult(String result);
}
public StripeIntegration() {
}
public static void setPaymentResultListener(PaymentResultListener listener) {
paymentResultListener = listener;
}
public static void NewStripeIntegration(Activity _activity) {
activity = _activity;
paymentSheet = new PaymentSheet((ComponentActivity) activity, StripeIntegration::onPaymentSheetResult);
}
public static void InitializePayment(Activity activity,String publishableKey, String customerId, String ephemeralKey, String paymentIntent) {
PaymentConfiguration.init(activity, publishableKey);
PaymentSheet.CustomerConfiguration customerConfig = new PaymentSheet.CustomerConfiguration(customerId,ephemeralKey);
PaymentSheet.Configuration configuration = new PaymentSheet.Configuration.Builder("ABC Company")
.customer(customerConfig)
.allowsDelayedPaymentMethods(true)
.allowsRemovalOfLastSavedPaymentMethod(true)
.allowsPaymentMethodsRequiringShippingAddress(true)
.paymentMethodLayout(PaymentSheet.PaymentMethodLayout.Automatic)
.build();
paymentSheet.presentWithPaymentIntent(paymentIntent, configuration);
}
public static void onPaymentSheetResult(final PaymentSheetResult paymentSheetResult) {
if (paymentSheetResult instanceof PaymentSheetResult.Completed) {
paymentResultListener.onPaymentResult("Completed");
Toast toast = Toast.makeText(activity.getApplicationContext(),"Completed",Toast.LENGTH_LONG);
toast.show();
} else if (paymentSheetResult instanceof PaymentSheetResult.Canceled) {
paymentResultListener.onPaymentResult("Cancelled");
Toast toast = Toast.makeText(activity.getApplicationContext(),"Cancelled",Toast.LENGTH_LONG);
toast.show();
} else if (paymentSheetResult instanceof PaymentSheetResult.Failed) {
String errorMessage = ((PaymentSheetResult.Failed) paymentSheetResult).getError().getMessage();
paymentResultListener.onPaymentResult("Failed: " + errorMessage);
Toast toast = Toast.makeText(activity.getApplicationContext(), "Failed: " + errorMessage, Toast.LENGTH_LONG);
toast.show();
}
}
}
To use our StripeIntegration.cs class inside the .NET MAUI project, we must ensure that our native Android binding is correctly linked.
π Modify the Metadata.xml
file located in:
template\android\NewBinding.Android.Binding\Transforms\Metadata.xml
π Add the following entry to Metadata.xml
:
<metadata>
<!-- Make sure the package id and name reflects the new binding name -->
<attr path="/api/package[@name='com.example.newbinding']" name="managedName">NewBindingAndroid</attr>
</metadata>
π If you created a new folder (e.g., stripe/
), you must update the path accordingly.
π Ensure the reference to the folder is correct; otherwise, the binding won't work, and the project won't compile.
Step 6: Add reference in our .NET MAUI project
To use the Stripe Android Library inside your .NET MAUI project, we need to add a reference to the Android binding project and ensure everything compiles properly.
π Add the Android Library Reference
<ItemGroup Condition="$(TargetFramework.Contains('android'))">
<ProjectReference Include="..\android\NewBinding.Android.Binding\NewBinding.Android.Binding.csproj" />
</ItemGroup>
Step 7: Integrate stripe in .NET MAUI Project
Now that we have successfully linked the Stripe Android library with our .NET MAUI project, we can proceed with the actual payment integration. This step involves displaying a payment sheet and handling callbacks to determine whether the payment was successful or failed.
namespace MauiSample;
#if ANDROID
using NewBinding = NewBindingAndroid.DotnetNewBinding;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
#elif (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID)
using NewBinding = System.Object;
#endif
public partial class MainPage : ContentPage
{
string publisherkey = ""; // Get it from stripe account Looks like pk_test*******
string customerID = ""; // Get it from APIs Looks like cus_*******
string ephemeralKey = ""; // Get it from APIs Looks like ek_test_*******
string secretKey = ""; // Get it APIs Looks like pi_*******
#if ANDROID
private readonly StripeIntegration _stripeIntegration = new StripeIntegration();
#endif
public MainPage()
{
InitializeComponent();
#if ANDROID
_stripeIntegration.InitializeStripe();
#endif
}
#if ANDROID
#endif
private void Button_Clicked(object sender, EventArgs e)
{
#if ANDROID
_stripeIntegration.InitializePayment(publisherkey, customerID, ephemeralKey, secretKey);
#endif
}
#if ANDROID
public class StripeIntegration
{
public delegate void PaymentResultHandler(string result);
public event PaymentResultHandler PaymentResultReceived;
public void InitializeStripe()
{
NewBindingAndroid.StripeIntegration.NewStripeIntegration(Platform.CurrentActivity);
var listener = new PaymentResultListener(result =>
{
PaymentResultReceived?.Invoke(result);
});
NewBindingAndroid.StripeIntegration.SetPaymentResultListener(listener);
}
public void InitializePayment(string publishableKey, string customerId, string ephemeralKey, string paymentIntent)
{
NewBindingAndroid.StripeIntegration.InitializePayment(Platform.CurrentActivity, publishableKey, customerId, ephemeralKey, paymentIntent);
}
private class PaymentResultListener : Java.Lang.Object, NewBindingAndroid.StripeIntegration.IPaymentResultListener
{
private readonly Action<string> _callback;
public PaymentResultListener(Action<string> callback)
{
_callback = callback;
}
public void OnPaymentResult(string result)
{
_callback?.Invoke(result);
}
}
}
#endif
}
The customerID, ephemeralKey, and secretKey are obtained from the API. So, I have created another project. When I run it, I get those three keys. I simply add them here because it is not efficient to store keys in the app. So, I have made another API project to get them.
Step 7: Add references in our project
Unlike iOS, where dependencies are automatically managed, Android requires manual configuration. Adding Stripe dependencies in Android can be challenging, but we simplify the process by using Android Library Binding in .NET MAUI.
To avoid downloading dependencies manually, we first need to copy dependencies into our native project for automatic generation. Otherwise, we have to download them manually. To simplify the process, we can just add the lines in our Android native project like this:
π Modify the build.gradle.kts
file inside the Android native project:
// Create configuration for copyDependencies. Uncomment line below.
configurations {
create("copyDependencies")
}
dependencies {
implementation("com.stripe:stripe-android:21.3.1")
implementation("androidx.activity:activity:1.9.3")
implementation ("androidx.fragment:fragment:1.8.5")
implementation ("androidx.appcompat:appcompat:1.7.0")
implementation ("com.google.android.material:material:1.12.0")
// Add package dependency for binding library
// Uncomment line below and replace {dependency.name.goes.here} with your dependency
// implementation("{dependency.name.goes.here}")
"copyDependencies"("com.stripe:stripe-android:21.3.1")
}
configurations.all {
resolutionStrategy {
eachDependency {
if (requested.group == "org.jetbrains.skiko" && requested.name == "skiko") {
useTarget("org.jetbrains.skiko:skiko-android:0.7.7")
}
}
}
}
// Copy dependencies for binding library. Uncomment code blocks below.
project.afterEvaluate {
tasks.register<Copy>("copyDeps") {
from(configurations["copyDependencies"])
into("${projectDir}/build/outputs/deps")
}
tasks.named("preBuild") { finalizedBy("copyDeps") }
}
Then, rebuild our project, and finally, we have all dependencies like that, and you will find all dependencies in android\native\NewBinding\build\outputs\deps:
Now, add them one by one. Add only those required in our project, not all of them. Like this:
<!--Stripe library dependencies--> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\stripe-android-21.3.1.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\stripe-core-21.3.1.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\stripe-ui-core-21.3.1.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\payments-core-21.3.1.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\payments-model-21.3.1.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\payments-ui-core-21.3.1.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\paymentsheet-21.3.1.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\stripe-3ds2-android-6.2.0.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\hcaptcha-21.3.1.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\kotlinx-serialization-json-jvm-1.7.3.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\kotlinx-serialization-core-jvm-1.7.3.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\kotlinx-coroutines-play-services-1.9.0.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\kotlinx-coroutines-core-jvm-1.9.0.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\kotlinx-coroutines-android-1.9.0.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\kotlinx-coroutines-swing-1.9.0.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\nimbus-jose-jwt-9.46.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\bcprov-jdk15to18-1.79.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\kotlin-android-extensions-runtime-2.0.21.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\kotlin-parcelize-runtime-2.0.21.jar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\link-21.3.1.aar" Bind="False" Visible="False" /> <AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\disklrucache-2.0.2.jar" Bind="False" Visible="False" /> <AndroidMavenLibrary Include="com.google.android.instantapps:instantapps" Version="1.1.0" Bind="False" Repository="Google" /> <AndroidMavenLibrary Include="org.jspecify:jspecify" Version="1.0.0" Bind="false" /> <AndroidMavenLibrary Include="jakarta.inject:jakarta.inject-api" Version="2.0.1" Bind="false" /> <AndroidMavenLibrary Include="com.google.dagger:dagger" Version="2.55" Bind="false" /> </ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net9.0-android'"> <PackageReference Include="GoogleGson"> <Version>2.11.0.5</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Activity.Compose"> <Version>1.10.0</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Activity.Ktx"> <Version>1.10.0</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Annotation"> <Version>1.9.1.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.AppCompat"> <Version>1.7.0.5</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Browser"> <Version>1.8.0.8</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Compose.Foundation"> <Version>1.7.6.1</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Compose.Material"> <Version>1.7.6.1</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Compose.Material.Icons.Core"> <Version>1.7.6.1</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Compose.Material3"> <Version>1.3.1.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Compose.Runtime"> <Version>1.7.6.1</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Compose.UI"> <Version>1.7.6.1</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Compose.UI.Tooling.Preview"> <Version>1.7.6.1</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Compose.UI.ViewBinding"> <Version>1.7.6.1</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.ConstraintLayout"> <Version>2.2.0.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Core"> <Version>1.15.0.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Core.Core.Ktx"> <Version>1.15.0.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Credentials" Version="1.3.0.2"> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Credentials.PlayServicesAuth" Version="1.3.0.2"> </PackageReference> <PackageReference Include="Xamarin.AndroidX.DataBinding.ViewBinding"> <Version>8.8.0.1</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Fragment"> <Version>1.8.5.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Fragment.Ktx" Version="1.8.5.2"> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Lifecycle.Common" Version="2.8.7.2"> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData.Ktx"> <Version>2.8.7.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Lifecycle.Runtime" Version="2.8.7.2"> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Lifecycle.Runtime.Compose"> <Version>2.8.7.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Lifecycle.Runtime.Compose.Android"> <Version>2.8.7.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Lifecycle.Runtime.Ktx" Version="2.8.7.2"> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel.Ktx"> <Version>2.8.7.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModelSavedState"> <Version>2.8.7.2</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.Navigation.Compose"> <Version>2.8.5.1</Version> </PackageReference> <PackageReference Include="Xamarin.AndroidX.RecyclerView"> <Version>1.4.0</Version> </PackageReference> <PackageReference Include="Xamarin.Google.Accompanist.FlowLayout"> <Version>0.36.0.3</Version> </PackageReference> <PackageReference Include="Xamarin.Google.Accompanist.SystemUIController"> <Version>0.36.0.3</Version> </PackageReference> <PackageReference Include="Xamarin.Google.Android.Material"> <Version>1.12.0.2</Version> </PackageReference> <PackageReference Include="Xamarin.Google.Code.FindBugs.JSR305"> <Version>3.0.2.18</Version> </PackageReference> <PackageReference Include="Xamarin.Google.ErrorProne.Annotations"> <Version>2.36.0.1</Version> </PackageReference> <PackageReference Include="Xamarin.Google.Guava"> <Version>33.4.0.1</Version> </PackageReference> <PackageReference Include="Xamarin.Google.J2Objc.Annotations"> <Version>3.0.0.8</Version> </PackageReference> <PackageReference Include="Xamarin.GoogleAndroid.Libraries.Identity.GoogleId" Version="1.1.0.8"> </PackageReference> <PackageReference Include="Xamarin.GooglePlayServices.Tasks"> <Version>118.2.0.4</Version> </PackageReference> <PackageReference Include="Xamarin.GooglePlayServices.Wallet"> <Version>119.4.0.4</Version> </PackageReference> <PackageReference Include="Xamarin.Io.OpenCensus.OpenCensusApi"> <Version>0.31.1.11</Version> </PackageReference> <PackageReference Include="Xamarin.JavaX.Inject"> <Version>1.0.0.18</Version> </PackageReference> <PackageReference Include="Xamarin.Jetbrains.Annotations"> <Version>26.0.1.2</Version> </PackageReference> <PackageReference Include="Xamarin.Kotlin.StdLib"> <Version>2.0.21.2</Version> </PackageReference> <PackageReference Include="Xamarin.Kotlin.StdLib.Jdk7"> <Version>2.0.21.2</Version> </PackageReference> <PackageReference Include="Xamarin.Kotlin.StdLib.Jdk8"> <Version>2.0.21.2</Version> </PackageReference> <PackageReference Include="Xamarin.KotlinX.Coroutines.Android"> <Version>1.9.0.2</Version> </PackageReference> <PackageReference Include="Xamarin.KotlinX.Coroutines.Core"> <Version>1.9.0.2</Version> </PackageReference> <PackageReference Include="Xamarin.KotlinX.Coroutines.Core.Jvm"> <Version>1.9.0.2</Version> </PackageReference> </ItemGroup>
Step 8: Run the project
Now, we are successfully able to integrate Stripe into our project. We can run the project, and that's all.
Common Issues & Fixes
Let's talk about some issues that many people face during integration:
Many people face common errors during integration, such as:
β Error:
Didn't find class "androidx.viewbinding.ViewBinding" on path: DexPathList[[zip file "/data/app/~~G6GMALhwSGHaz46d-eoxfw==/com.companyname.facebook.sample- Java.Lang.NoClassDefFoundError: Failed resolution of: Lio/metamask/androidsdk/DappMetadata; ---> Java.Lang.ClassNotFoundException: Didn't find class "io.metamask.androidsdk.DappMetadata" on path: DexPathList[[zip file "/data/app/~~YG-6WEJFS32FR93NKjCBDw==/com.companyname.facebook.sample-pCqNYK04hJADXKEAh_P8-A==/base.apk"],nativeLibraryDirectories=[/data/app/~~YG-6WEJFS32FR93NKjCBDw==/com.c
β Fix: For that, we have to add all reference in our .csproj of .NET MAUI like:
<AndroidLibrary Include="..\android\native\StripeBinding\build\outputs\deps\stripe-android-21.3.1.aar" Bind="False" Visible="False" />
πΉ Android requires this manually. iOS detects it automatically. Make sure to set
Bind="False"
, otherwise, it will throw errors.Another common issue is adding dependencies. We have two cases: Android Library and Maven. Maven is the best for resolving and maintaining dependencies, but here I used AndroidLibrary because Stripe dependencies are complex. In general, the best way is to solve it by using Maven, which downloads dependencies automatically. But, for our case, we manually integrate all of them.
π For more information:
Binding Java Libraries with Maven in .NET MAUIOne more common issue while building the project:
β Error:
NewBindingAndroid not found
β Fix: Rebuild both
NewBinding.Android
.Binding
and the Android project. After resolving it, we can find one more interesting scenario: resolving it automatically, and there is no red line.Another common issue is Activity initialization. In this sample, I initialized Stripe in
MainPage()
, but it causes issues in larger projects.β Fix: Initialize Stripe inside
OnCreate
ofMainActivity.cs
instead ofMainPage()
.
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
StripeAndroid.StripeInitialization(this);
}
}
GitHub Repository Sample
You can find my sample of that project here:
π GitHub Repository for Stripe Integration
Conclusion
Congratulations! π We have successfully integrated Stripe payments into our .NET MAUI Android app using Native Interop. By following this guide, we overcame common issues and ensured a smooth and secure payment experience.
π‘ What's Next?
This approach is not limited to Stripe! We can extend other payment gateways like Braintree, PayPal, or even implement KYC (Know Your Customer) verification and social logins in a similar manner in our .NET MAUI apps.
Subscribe to my newsletter
Read articles from Haider Ali Faizi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
