Integrate Apple & Google Pay via Stripe Flutter

Ayesha Iftikhar
9 min readFeb 12, 2024

In today’s fast-paced digital landscape, providing a frictionless and secure payment experience is paramount for any business operating online. With the increasing popularity of mobile wallets like Apple Pay and Google Pay, integrating these payment methods into your application can significantly enhance user convenience and satisfaction. Fortunately, with the advent of technologies like Stripe Flutter, developers can seamlessly integrate these payment options into their Flutter applications, offering users a streamlined checkout process. Let’s delve into how to integrate Apple Pay and Google Pay via Stripe Flutter for an optimized payment experience.

Understanding Stripe Flutter

Stripe Flutter is a powerful SDK that allows developers to integrate Stripe’s payment infrastructure seamlessly into Flutter applications. It provides a comprehensive set of features and APIs to handle various aspects of payment processing, including card payments, Apple Pay, Google Pay, and more. By leveraging Stripe Flutter, developers can ensure PCI compliance, securely handle payment information, and deliver a smooth checkout experience across different platforms.

Setup Google Pay

Creating a Google Pay Merchant Account

Note: If you just want to test GPay, this step is not required. You can directly move to ‘Installing the Pay plugin’ Section.

  • Creating a Google Pay merchant account is the first step in integrating the google payment option into your app.
  • Head over to Google Pay & Wallet Console. You will see a dialog in which you need to specify your business name and country.
  • After entering your Business details you will be redirected to the Dashboard of the Google Pay Console.

Verifying Business Profile

  • Now that we have created a Business in GPay Console, we need to set up the Business Profile. Note: This is mandatory if we need to enable all Pay & Wallet Console features in our app.

Click on the Business Profile section on the left of the Dashboard. You will see 2 sections :

  • Business Identity and
  • Business Information
  • Let’s first fill Business Identity.
  • Let’s fill the Business information.

You will see different form that you need to fill according to your selected Country.

Please note that: I’ve entered dummy info, just to show you. You need to fill valid information, otherwise it will not get approved.

  • Okay, so after filling in all the details hit the Save button. After a few minutes, your business profile will be approved. If not, you can contact them via e-mail.
  • You will see the approved label right after Business Profile.

Installing the Pay plugin

  • Okay, so now that we’ve done the setup in Google Pay Console. We are ready to use Google Pay in our Flutter App.
  • We are going to the use pay package to add support for payments.
  • So, head over to pubspec.yaml and add this package :
dependencies:
pay: <latest-version>

Configuring the plugin and integrating it into the app

First, we will look into how to configure Google Pay.

  • We will be adding the configuration file for Google Pay as a Json file or we can also use it in string format. I will be using the string format as for now but you can copy the below configuration and create a json file as well.
  • I am using Stripe as a payment gateway.
{
"provider": "google_pay",
"data": {
"environment": "TEST", // PRODUCTION for Live Application
"apiVersion": 2,
"apiVersionMinor": 0,
"allowedPaymentMethods": [
{
"type": "CARD",
"tokenizationSpecification": {
"type": "PAYMENT_GATEWAY",
"parameters": {
"gateway": "stripe",
"stripe:version": "2023-10-16",
"stripe:publishableKey": "{STRIPE_PUBLIC_KEY}"
}
},
"parameters": {
"allowedCardNetworks": ["VISA", "MASTERCARD"],
"allowedAuthMethods": ["PAN_ONLY", "CRYPTOGRAM_3DS"],
"billingAddressRequired": true, // can be false for digital goods
"billingAddressParameters": {
"format": "FULL",
"phoneNumberRequired": true
}
}
}
],
"merchantInfo": {
"merchantId": "01234567890123456789", // Merchant ID
"merchantName": "{Merchant Name}", // Merchant Name
},
"transactionInfo": {
"countryCode": "US",
"currencyCode": "USD"
}
}
}

For using this configuration as String , copy the below code.

String get googlePayPaymentProfile => """{
"provider": "google_pay",
"data": {
"environment": "${kDebugMode ? "TEST" : "PRODUCTION"}",
"apiVersion": 2,
"apiVersionMinor": 0,
"allowedPaymentMethods": [
{
"type": "CARD",
"tokenizationSpecification": {
"type": "PAYMENT_GATEWAY",
"parameters": {
"gateway": "stripe",
"stripe:version": "2023-10-16",
"stripe:publishableKey": "{STRIPE PUBLIC KEY}"
}
},
"parameters": {
"allowedCardNetworks": ["VISA", "MASTERCARD"],
"allowedAuthMethods": ["PAN_ONLY", "CRYPTOGRAM_3DS"],
"billingAddressRequired": false,
"billingAddressParameters": {
"format": "FULL",
"phoneNumberRequired": true
}
}
}
],
"merchantInfo": {
"merchantId": "01234567890123456789",
"merchantName": "{MERCHANT ID}"
},
"transactionInfo": {
"countryCode": "US",
"currencyCode": "USD"
}
}
}""";

Note: environment : Please change this when you are ready to ship your app.

  • allowedPaymentMethods : You can configure here, which payment methods are allowed in GPay.
  • merchantInfo : Do not forget to update this when you are ready to ship. You can get merchantId and merchantName in your Google Pay Console.
  • transactionInfo : Here you can mention the currency code. ex: USD, EUR, INR, etc.

Reminder: Do not forget to add assets folder in pubspec file.

  • Don’t Forget to add the required metadata to work this properly in AndroidManifest.xml.
 <meta-data
android:name="com.google.android.gms.wallet.api.enabled"
android:value="true" />
  • Add the following in app>build.gradle & change minSdkVersion to 21
minSdkVersion 23
...
...
...
dependencies {
implementation 'com.stripe:stripe-android:20.37.2'
}

We will use Default Google Pay button because it follows the proper guideline but first we need to import the package:

import 'package:pay/pay.dart' as pay;

Default Google Pay Button:

pay.GooglePayButton(
width: context.width,
height: 55,
type: GooglePayButtonType.pay,
paymentConfiguration: pay.PaymentConfiguration.fromJsonString(
googlePayPaymentProfile,
),
paymentItems: [
pay.PaymentItem(
label: PRODUCT_LABEL,
amount: getTotal().toString(),
status: pay.PaymentItemStatus.final_price,
)
],
margin: const EdgeInsets.only(top: 15),
onPaymentResult: onGooglePayResult,
loadingIndicator: const Center(
child: CircularProgressIndicator(),
),
childOnError: const Text(
'Google Pay is not available in this device',
),
onError: (e) {
Get.showSnackbar(
Ui.ErrorSnackBar(
message:'There was an error while trying to perform the payment',
),
);
},
)

Once the process is initiated, it will look something like this:

After updating or contiune to purchase if the transaction is a successful. The response will look something like this:

{
"apiVersion": 2,
"apiVersionMinor": 0,
"paymentMethodData": {
"description": " Mastercard •••• 0166",
"info": { "cardDetails": "0166", "cardNetwork": "MASTERCARD" },
"tokenizationData": {
"token": {
"id": "tok_1OiyDFIPCGgTZqpcElNo2mM1",
"object": "token",
"card": {
"id": "card_1OiyDFIPCGgTZqpctQf9ark2",
"object": "card",
"address_city": "Okara",
"address_country": "PK",
"address_line1": "Chaudhry House",
"address_line1_check": "unchecked",
"address_line2": null,
"address_state": null,
"address_zip": "56300",
"address_zip_check": "unchecked",
"brand": "MasterCard",
"country": "US",
"cvc_check": null,
"dynamic_last4": "4444",
"exp_month": 6,
"exp_year": 2027,
"funding": "credit",
"last4": "0166",
"metadata": {},
"name": "Ayesha Iftikhar",
"tokenization_method": "android_pay",
"wallet": null
},
"client_ip": "209.85.198.113",
"created": 1707739337,
"livemode": false,
"type": "card",
"used": false
},
"type": "PAYMENT_GATEWAY"
},
"type": "CARD"
}
}

For making the transaction successful, deducting user and assigning some product to user. We will create a Payment Intent and confirm the payment from that Intent and perform some operation:

Future<void> onGooglePayResult(paymentResult) async {
try {
Get.log('on Google Pay Result');
loadingDialog();
Get.log('result:$paymentResult');
// 2. fetch Intent Client Secret from backend
final token =
paymentResult['paymentMethodData']['tokenizationData']['token'];
final tokenJson = Map.castFrom(json.decode(token));
Get.log('token:$tokenJson');
final response = await paymentIntentKey();
Get.log('payment intent response: $response');
final clientSecret = response['client_secret'];
Get.log('client secret: $clientSecret');
final params = PaymentMethodParams.cardFromToken(
paymentMethodData: PaymentMethodDataCardFromToken(
token: tokenJson['id'],
),
);
Get.log('params: $params');
// 3. Confirm Google pay payment method
await Stripe.instance.confirmPayment(
paymentIntentClientSecret: clientSecret,
data: params,
);
debugPrint('payment successful');
await purchaseTicket();

} catch (e, trace) {
Get.log('Error: $e');
Get.log('trace: $trace');
Get.back();
Get.showSnackbar(Ui.ErrorSnackBar(message: 'Error: $e'));
}
}

Function for generating payment intent:

Future<Map<String, dynamic>> paymentIntentKey() async {
final empResponse = await http.post(
Uri.parse('https://api.stripe.com/v1/payment_intents'),
headers: {
"Stripe-Version": "2023-10-16",
'Authorization': 'Bearer ${StripeService.secret}',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: {
'customer': Get.find<UserServices>().user.customer,
'amount': (getTotal().toInt() * 100).toString(),
'currency': widget.event.currency,
},
);
print(empResponse.body);
return jsonDecode(empResponse.body);
}

That’s all for Google Pay!!!

Now let’s move forward with Apple Pay. Follow along these instructions provided by Apple to setup apple pay.

https://developer.apple.com/documentation/passkit_apple_pay_and_wallet/apple_pay/setting_up_apple_pay

  • Open the project in XCode.
  • Add the required Apple Pay capability in Runner. Enable your desired Merchant ID .

Add the configuration file in your assets or you can use the configuration string like me.

  • Configuration File:
{
"provider": "apple_pay",
"data": {
"merchantIdentifier": "merchant.com.stripe.test", // change to your own merchant id
"displayName": "{MERCHANT NAME}",
"merchantCapabilities": ["3DS", "debit", "credit"],
"supportedNetworks": ["amex", "visa", "discover", "masterCard"],
"countryCode": "US",
"currencyCode": "USD",
"requiredBillingContactFields": ["emailAddress", "name", "phoneNumber", "postalAddress"],
"requiredShippingContactFields": [],
"shippingMethods": [
{
"amount": "0.00",
"detail": "Available within an hour",
"identifier": "in_store_pickup",
"label": "In-Store Pickup"
},
{
"amount": "4.99",
"detail": "5-8 Business Days",
"identifier": "flat_rate_shipping_id_2",
"label": "UPS Ground"
},
{
"amount": "29.99",
"detail": "1-3 Business Days",
"identifier": "flat_rate_shipping_id_1",
"label": "FedEx Priority Mail"
}
]
}
}
  • Configuration String
  String get applePayPaymentProfile => """{
"provider": "apple_pay",
"data": {
"merchantIdentifier": "{MERCHANT ID}",
"displayName": "{MERCHANT NAME}",
"merchantCapabilities": ["3DS", "debit", "credit"],
"supportedNetworks": ["amex", "visa", "discover", "masterCard"],
"countryCode": "SE",
"currencyCode": "EUR",
"requiredBillingContactFields": ["emailAddress", "name", "phoneNumber"],
"requiredShippingContactFields": [],
"shippingMethods": [] // if you don't include shipping, just past empty list like this.Ï
}
}""";

Default Apple Pay Button:

pay.ApplePayButton(
width: context.width,
height: 55,
paymentConfiguration: pay.PaymentConfiguration.fromJsonString(
applePayPaymentProfile,
),
paymentItems: [
pay.PaymentItem(
label: widget.event.title,
amount: getTotal().toString(),
status: pay.PaymentItemStatus.final_price,
)
],
margin: const EdgeInsets.only(top: 15),
onPaymentResult: applePayResult,
loadingIndicator: const Center(
child: CircularProgressIndicator(),
),
childOnError: const Text(
'Apple Pay is not available on this device',
),
onError: (e) {
Get.showSnackbar(
Ui.ErrorSnackBar(
message:'There was an error while trying to perform the payment',
),
);
},
)

The successful respone will look something like this:

{
paymentMethod: {
type: 0,
network: AmEx,
displayName: Simulated Instrument
},
billingContact: {
name: {
familyName: "",
namePrefix: "",
phoneticRepresentation: {
nickname: null,
phoneticRepresentation: null,
namePrefix: null,
nameSuffix: null,
middleName: "",
givenName:"" ,
familyName:""
},
nameSuffix: "",
nickname: "",
givenName: "",
middleName: ""
}
},
token: "",
transactionIdentifier: Simulated Identifier,
}

Again, for making the transaction successful, deducting user and assigning some product to user. We will create a Payment Intent and confirm the payment from that Intent and perform some operation:

Future<void> applePayResult(paymentResult) async {
try {
loadingDialog();
debugPrint('result: $paymentResult');
// 1. Get Stripe token from payment result
final token = await Stripe.instance.createApplePayToken(paymentResult);

// 2. fetch Intent Client Secret from backend
final response = await paymentIntentKey();
final clientSecret = response['client_secret'];

final params = PaymentMethodParams.cardFromToken(
paymentMethodData: PaymentMethodDataCardFromToken(
token: token.id,
),
);

// 3. Confirm Apple pay payment method
await Stripe.instance.confirmPayment(
paymentIntentClientSecret: clientSecret,
data: params,
);

debugPrint('payment successful');
await purchaseTicket();
} catch (e, trace) {
Get.back();
debugPrint('error: $e');
debugPrint('trace: $trace');
Get.showSnackbar(Ui.ErrorSnackBar(message: 'Error: $e'));
}
}

You can view how it will look like here.

https://youtube.com/shorts/2igGma76jjY

For making stripe work properly, we will use flutter_stripe package. Follow the platform instructions given by stripe.

  • Add stripe package in pubspec.yaml
dependencies:
flutter_stripe: latest

Initialize Stripe in the project, add following lines in main() function.

Stripe.publishableKey = STRIPE_PUBLIC_KEY;
Stripe.merchantIdentifier = Platform.isAndroid
? 'GOOGLE_PLAY_MERCHANT_ID'
: 'APPLE_MERCHANT_ID';
Stripe.urlScheme = 'flutterstripe';
await Stripe.instance.applySettings();

Conclusion

In conclusion, integrating Apple Pay and Google Pay via Stripe Flutter presents a compelling opportunity for developers to enhance the payment experience within their Flutter applications. By leveraging the robust capabilities of Stripe’s payment infrastructure and the flexibility of Flutter’s cross-platform framework, developers can deliver a seamless, secure, and user-friendly payment experience that drives customer satisfaction and business growth.

I included a step-by-step guide for integrating Apple Pay and Google Pay via Stripe Flutter, along with the benefits. Let me know if you want me to focus on any particular aspect or include more technical details!

--

--

Ayesha Iftikhar

I am professional software engineer with experience of around 4 years in Mobile Application Development using Flutter and overall 5 years of experience in IT.