Apple Pay Integration Guide
This guide walks you through integrating Apple Pay payments into your Flutter application using the Moyasar Flutter SDK. Apple Pay enables users to pay securely using Face ID or Touch ID on supported iOS devices.
Prerequisites
Before you begin, ensure you have:
- Moyasar account — Sign up if you haven't already
- Publishable API key — Get your API keys
- Apple Developer Account — With an active subscription
- iOS device or simulator — Apple Pay requires a real device for testing (simulator has limited support)
- macOS with Xcode — Required for iOS configuration
Apple Pay is iOS only. The Apple Pay button will not appear on Android. Use Samsung Pay for Android wallet payments.
Apple Pay Setup
Before integrating in your app, complete the Apple Pay setup:
- Create a Merchant ID in your Apple Developer Account
- Configure the Payment Processing Certificate with Moyasar. The flow involves four steps (download → upload → download → upload):
- Step 1 — Download CSR from Moyasar: Go to Moyasar Dashboard → Settings → Apple Pay - Certificate → Request CSR → Download CSR. Save the
.csrfile to your computer. - Step 2 — Upload CSR to Apple: In Apple Developer → your Merchant ID → Apple Pay Payment Processing Certificate → Create Certificate → upload the
.csrfile you downloaded. When asked about China Mainland, select No. - Step 3 — Download signed certificate from Apple: After Apple signs it, Download the
.cerfile to your computer. - Step 4 — Upload to Moyasar: Go back to Moyasar Dashboard → Apple Pay - Certificate → Upload File → select the
apple_pay.cerfile → Upload. Once it matches, the certificate will be active.
- Step 1 — Download CSR from Moyasar: Go to Moyasar Dashboard → Settings → Apple Pay - Certificate → Request CSR → Download CSR. Save the
- Enable Apple Pay in Xcode for your app:
- Add the Apple Pay capability to your app
- Select your Merchant ID
- Ensure your App ID has Apple Pay enabled in the Apple Developer Portal
- Note your Merchant ID — you will need it for
ApplePayConfig
When creating the Payment Processing Certificate in Apple Developer, if asked whether the merchant is in China Mainland, select No. Moyasar does not support the RSA algorithm used for China.
Installation
Add the moyasar package to your pubspec.yaml:
dependencies:
moyasar: ^3.0.5 # or the latest version
Then run:
flutter pub get
iOS Configuration
Apple Pay will not work without the following configuration. The Apple Pay button will not appear if the capability is not enabled.
Enable Apple Pay Capability in Xcode
- Open your Flutter project in Xcode:
ios/Runner.xcworkspace - Select the Runner target → Signing & Capabilities
- Click + Capability and add Apple Pay
- Under Merchant IDs, add your Merchant ID (e.g.
merchant.com.yourapp) - Ensure the Merchant ID matches the one configured in Moyasar Dashboard and your Apple Developer Account
Verify Entitlements
Your ios/Runner/Runner.entitlements file should include:
<key>com.apple.developer.in-app-payments</key>
<array>
<string>merchant.com.yourapp</string>
</array>
Replace merchant.com.yourapp with your actual Merchant ID.
Implementation
1. Configure ApplePayConfig
Add ApplePayConfig to your PaymentConfig:
import 'package:flutter/material.dart';
import 'package:moyasar/moyasar.dart';
final paymentConfig = PaymentConfig(
publishableApiKey: 'YOUR_PUBLISHABLE_API_KEY',
amount: 25758, // SAR 257.58 (amount in minor unit: halalas)
description: 'Order #1324',
metadata: {'size': '250g'},
applePay: ApplePayConfig(
merchantId: 'merchant.com.yourapp',
label: 'YOUR_STORE_NAME',
manual: false,
saveCard: false,
),
);
| Parameter | Description |
|---|---|
merchantId | Your Apple Pay Merchant ID (must match Xcode and Apple Developer) |
label | Store name displayed in the Apple Pay sheet |
manual | Set to true for manual payment capture (authorize without charging) |
saveCard | Set to true to tokenize the card for later use |
merchantCapabilities | Optional. Default: ["3DS", "debit", "credit"] |
supportedCountries | Optional. Default: ["SA"]. ISO 3166 country codes. |
2. Add the ApplePay Widget
Use the built-in ApplePay widget to display the Apple Pay button:
class PaymentMethods extends StatelessWidget {
PaymentMethods({super.key});
final paymentConfig = PaymentConfig(
publishableApiKey: 'YOUR_PUBLISHABLE_API_KEY',
amount: 25758,
description: 'Order #1324',
metadata: {'size': '250g'},
applePay: ApplePayConfig(
merchantId: 'merchant.com.yourapp',
label: 'YOUR_STORE_NAME',
manual: false,
saveCard: false,
),
);
void onPaymentResult(result) {
if (result is PaymentResponse) {
switch (result.status) {
case PaymentStatus.paid:
// Payment successful — verify on your backend
break;
case PaymentStatus.failed:
// Payment failed
break;
}
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
ApplePay(
config: paymentConfig,
onPaymentResult: onPaymentResult,
),
const Text("or"),
CreditCard(
config: paymentConfig,
onPaymentResult: onPaymentResult,
),
],
);
}
}
Always verify the payment on your backend using the Fetch Payment API before fulfilling the order. Never rely solely on the client-side result.
3. Button Visibility
The ApplePay widget manages its own visibility — you do not need to wrap it in a platform check. It decides what to render based on the device:
| Situation | What the widget renders |
|---|---|
| Not running on iOS (e.g. Android) | Nothing |
| Device cannot support Apple Pay | Nothing |
| iOS 15+, no card in Wallet | The payment button — the user adds a card from inside the Apple Pay sheet |
| iOS 14 and below, no card in Wallet | Apple's Set Up Apple Pay button, which opens Wallet to add a card |
| A supported card is available | The payment button |
Because the widget renders nothing when Apple Pay is unavailable, you can place it directly in a shared checkout column alongside the other payment methods:
Column(
children: [
ApplePay(config: paymentConfig, onPaymentResult: onPaymentResult),
CreditCard(config: paymentConfig, onPaymentResult: onPaymentResult),
],
)
To add SamsungPay to the same column, your PaymentConfig must also include a samsungPay configuration — the widget asserts that it is present. See the Samsung Pay Integration Guide.
4. Customizing the Button
buttonType sets the wording Apple shows on the button, and buttonStyle sets its colors. Both are optional.
ApplePay(
config: paymentConfig,
onPaymentResult: onPaymentResult,
buttonType: ApplePayButtonType.buy,
buttonStyle: ApplePayButtonStyle.automatic,
)
| Parameter | Values | Default |
|---|---|---|
buttonType | plain, buy, setUp, inStore, donate, checkout, book, subscribe, reload, addMoney, topUp, order, rent, support, contribute, tip | inStore |
buttonStyle | white, whiteOutline, black, automatic | black |
Use ApplePayButtonStyle.automatic to have the button follow the system light/dark appearance, and pick the buttonType that matches the action the user is completing (buy for a purchase, subscribe for a subscription, donate for a donation).
Custom Apple Pay UI
If you need a custom UI instead of the built-in button, use Moyasar.pay with ApplePayPaymentRequestSource. See UI Customizations for details.
Testing
Test Environment
- Use sandbox API keys (
pk_test_...) for testing - Test on a real iOS device — Apple Pay has limited support on simulators
- Add a real card to Apple Wallet on your test device (or use a sandbox Apple ID with test cards)
On iOS 15 and later, a device with no card still shows the payment button — tapping it opens the Apple Pay sheet where the card can be added. This is worth testing, since it is what a first-time customer sees.
Test Amounts
Apple Pay testing uses amount-based simulation. Different amounts return different results:
| Amount (Minor Unit) | Amount (SAR) | Status | Response |
|---|---|---|---|
| 20000 to 30000 | 200.00 to 300.00 | paid | APPROVED |
| 100000 to 110000 | 1000.00 to 1100.00 | failed | UNSPECIFIED FAILURE |
| 110100 to 120000 | 1101.00 to 1200.00 | failed | INSUFFICIENT FUNDS |
| 120100 to 130000 | 1201.00 to 1300.00 | failed | DECLINED: LOST CARD |
| 130100 to 140000 | 1301.00 to 1400.00 | failed | DECLINED |
| 140100 to 150000 | 1401.00 to 1500.00 | failed | DECLINED: EXPIRED CARD |
See the full Apple Pay Testing guide for all test amounts.
Use amounts between 20000–30000 (SAR 200–300) to simulate successful payments in the sandbox.
Running the Example App
The moyasar-flutter example app uses product flavors. For Apple Pay:
flutter run --flavor default_
See the example CONFIGURATION_GUIDE for details.
Production Deployment
Before going live:
- Replace test API keys with live keys (
pk_live_...) - Ensure your Merchant ID in Xcode matches the one in Moyasar Dashboard and Apple Developer
- Verify the Payment Processing Certificate is active in Moyasar Dashboard
- Verify payment status on your backend for every transaction
- Test the full flow on a production device
Troubleshooting
| Issue | Solution |
|---|---|
| Apple Pay button not showing | The widget hides itself when Apple Pay is unavailable. Check that you're on a real iOS device, the Apple Pay capability is enabled in Xcode, and the Merchant ID matches everywhere. |
| Button shows "Set Up Apple Pay" | Expected on iOS 14 and below when Wallet has no supported card. Tapping it opens Wallet; the button becomes the payment button once a card is added. |
| "Invalid merchant" error | Verify Merchant ID matches across Xcode, Apple Developer Portal, and Moyasar Dashboard |
| Payment fails immediately | Check that the Payment Processing Certificate is uploaded and active in Moyasar Dashboard |
| Certificate errors | Re-upload the signed certificate from Apple Developer to Moyasar. Ensure you selected "No" for China Mainland. |
| Apple Pay not available on simulator | Use a real device for full Apple Pay testing |
Support
For integration help or Apple Pay setup: