Mobile elements - Apple Pay

1. Set up Stripe [Server Side] [Client Side]

First, you need a Stripe account. Register now.

Server-side

This integration requires endpoints on your server that talk to the Stripe API. Use one official libraries for access to the Stripe API from your server. Follow the steps here

Client-side

The Flutter SDK is open source, fully documented.

To install the SDK, follow these steps:

  • Run the commmand flutter pub add flutter_stripe
  • This will add a line like this to your project's pubspec.yaml with the latest package version

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository.

When your app starts, configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API. For Apple Pay, you also need to set the merchantIdentifier.

void main() async {
  Stripe.publishableKey = stripePublishableKey;
  Stripe.merchantIdentifier = 'merchant.com.your.app';
  runApp(const App());
}

Use your test mode keys while you test and develop, and your live mode keys when you publish your app.

2. Enable Apple Pay [Client Side]

To use Apple Pay you need to configure your app in Xcode:

  1. Open your project in Xcode
  2. Select your target and go to "Signing & Capabilities"
  3. Click "+ Capability" and add "Apple Pay"
  4. Enable the merchant IDs you want to use

You also need to register an Apple Merchant ID and configure it in your Stripe Dashboard. For detailed instructions, see: Stripe Apple Pay Setup.

3. Add an endpoint [Server Side]

Server-side

This integration uses three Stripe API objects:

  1. A PaymentIntent. Stripe uses this to represent your intent to collect payment from a customer, tracking your charge attempts and payment state changes throughout the process.

  2. A Customer (optional). To set up a card for future payments, it must be attached to a Customer. Create a Customer object when your customer creates an account with your business. If your customer is making a payment as a guest, you can create a Customer object before payment and associate it with your own internal representation of the customer's account later.

  3. A Customer Ephemeral Key (optional). Information on the Customer object is sensitive, and can't be retrieved directly from an app. An Ephemeral Key grants the SDK temporary access to the Customer.

If you never save cards to a Customer and don't allow returning Customers to reuse saved cards, you can omit the Customer and Customer Ephemeral Key objects from your integration.

For security reasons, your app can't create these objects. Instead, add an endpoint on your server that:

  1. Retrieves the Customer, or creates a new one.
  2. Creates an Ephemeral Key for the Customer.
  3. Creates a PaymentIntent, passing the Customer id.
  4. Returns the Payment Intent's client secret, the Ephemeral Key's secret, and the Customer's id to your app.

Check examples implementations for your server here

4a. Option1: Use the Stripe native buttons

First add the PlatformPayButton to your screen. See docs for all config options.

if (Stripe.instance.isPlatformPaySupportedListenable.value)
  PlatformPayButton(
    type: PlatformButtonType.buy,
    appearance: PlatformButtonStyle.whiteOutline,
    onPressed: () {
      startApplePay();
    },
  )
else
  Text('Apple Pay is not available on this device'),

Then add the startApplePay method to your screen.

Future<void> startApplePay() async {
  final scaffoldMessenger = ScaffoldMessenger.of(context);
  try {
    // 1. fetch Intent Client Secret from backend
    final response = await fetchPaymentIntentClientSecret();
    final clientSecret = response['clientSecret'];

    // 2. Confirm Apple Pay payment
    await Stripe.instance.confirmPlatformPayPaymentIntent(
      clientSecret: clientSecret,
      confirmParams: PlatformPayConfirmParams.applePay(
        applePay: ApplePayParams(
          cartItems: [
            ApplePayCartSummaryItem.immediate(
              label: 'Product Test',
              amount: '0.01',
            ),
          ],
          merchantCountryCode: 'US',
          currencyCode: 'USD',
        ),
      ),
    );

    if (context.mounted) {
      scaffoldMessenger.showSnackBar(
        const SnackBar(
            content: Text('Apple Pay payment successfully completed')),
      );
    }
  } catch (e) {
    if (context.mounted) {
      scaffoldMessenger.showSnackBar(
        SnackBar(content: Text('Error: $e')),
      );
    }
  }
}

Advanced options

The ApplePayParams class supports additional options:

ApplePayParams(
  cartItems: items,
  merchantCountryCode: 'US',
  currencyCode: 'USD',
  // Request shipping address
  requiredShippingAddressFields: [
    ApplePayContactFieldsType.name,
    ApplePayContactFieldsType.postalAddress,
    ApplePayContactFieldsType.emailAddress,
    ApplePayContactFieldsType.phoneNumber,
  ],
  // Provide shipping methods
  shippingMethods: [
    ApplePayShippingMethod(
      identifier: 'free',
      detail: 'Arrives by July 2',
      label: 'Free Shipping',
      amount: '0.0',
    ),
    ApplePayShippingMethod(
      identifier: 'standard',
      detail: 'Arrives by June 29',
      label: 'Standard Shipping',
      amount: '3.21',
    ),
  ],
  // Support coupon codes
  supportsCouponCode: true,
  couponCode: 'DISCOUNT',
)

Handling shipping contact and method changes

You can respond to shipping updates by providing callbacks to the PlatformPayButton:

PlatformPayButton(
  onShippingContactSelected: (contact) async {
    debugPrint('Shipping contact updated $contact');
    // Update the sheet with new totals based on shipping address
    await Stripe.instance.updatePlatformSheet(
      params: PlatformPaySheetUpdateParams.applePay(
        summaryItems: items,
        shippingMethods: shippingMethods,
        errors: [],
      ),
    );
  },
  onShippingMethodSelected: (method) async {
    debugPrint('Shipping method updated $method');
    // Update the sheet with new totals based on shipping method
    await Stripe.instance.updatePlatformSheet(
      params: PlatformPaySheetUpdateParams.applePay(
        summaryItems: items,
        shippingMethods: shippingMethods,
        errors: [],
      ),
    );
  },
  onPressed: () => startApplePay(),
)

4b. Option2: Use the Pay plugin

First read the docs thoroughly of package pay.

Add pay to your pubspec.yaml

dependencies:
  pay: ^1.1.2

Furthermore you need to create a json config file. See for an example our config.

{
  "provider": "apple_pay",
  "data": {
    "merchantIdentifier": "merchant.com.your.app",
    "displayName": "Your Store Name",
    "merchantCapabilities": ["3DS"],
    "supportedNetworks": ["amex", "visa", "discover", "masterCard"],
    "countryCode": "US",
    "currencyCode": "USD"
  }
}

Add the ApplePayButton to your screen.

pay.ApplePayButton(
  paymentConfiguration: pay.PaymentConfiguration.fromJsonString(
    _paymentProfile,
  ),
  paymentItems: _paymentItems,
  margin: const EdgeInsets.only(top: 15),
  onPaymentResult: onApplePayResult,
  loadingIndicator: const Center(
    child: CircularProgressIndicator(),
  ),
  childOnError: Text('Apple Pay is not available on this device'),
  onError: (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text(
            'There was an error while trying to perform the payment'),
      ),
    );
  },
),

In the callback, create a Stripe token from the Apple Pay result and confirm the payment.

Future<void> onApplePayResult(paymentResult) async {
  final scaffoldMessenger = ScaffoldMessenger.of(context);
  try {
    // 1. Get Stripe token from payment result
    final token = await Stripe.instance.createApplePayToken(paymentResult);

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

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

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

    if (context.mounted) {
      scaffoldMessenger.showSnackBar(
        const SnackBar(
            content: Text('Apple Pay payment successfully completed')),
      );
    }
  } catch (e) {
    if (context.mounted) {
      scaffoldMessenger.showSnackBar(
        SnackBar(content: Text('Error: $e')),
      );
    }
  }
}

5. Open Apple Pay Setup (Optional)

You can prompt users to set up Apple Pay if they haven't already:

await Stripe.instance.openApplePaySetup();

This opens the Apple Pay setup flow, allowing users to add a card to their Wallet.