Mobile Payment Integration Guide for Australian Apps

Processing payments in a mobile app requires navigating a complex landscape of payment processors, platform requirements, and Australian regulations. Get it right, and you create a seamless checkout experience that converts. Get it wrong, and you lose customers at the most critical moment of their journey.

This guide covers the practical implementation of mobile payments for Australian apps, from choosing a payment processor to handling edge cases.

The Australian Payment Landscape

Australians are enthusiastic adopters of digital payments. Key facts:

  • Contactless payments account for over 90 percent of in-person card transactions
  • Apple Pay and Google Pay are widely used, with major Australian banks supporting both
  • BPAY remains a popular method for bill payments and invoicing
  • Buy Now, Pay Later services (Afterpay, Zip) are culturally significant
  • PayID and NPP (New Payments Platform) enable instant bank transfers

For mobile apps, the primary payment methods to support are:

  1. Credit/debit cards (Visa, Mastercard, American Express)
  2. Apple Pay (iOS)
  3. Google Pay (Android)
  4. Direct debit for subscriptions
  5. BPAY for invoicing apps

Choosing a Payment

Processor

Stripe is our default recommendation for Australian mobile apps. It offers:

  • Comprehensive Australian support (AUD settlement, local acquiring)
  • Excellent iOS and Android SDKs
  • Apple Pay and Google Pay integration
  • Subscription management (Stripe Billing)
  • Strong documentation and developer experience
  • Connect platform for marketplace payments
  • PCI compliance handled by Stripe

Pricing: 1.7 percent + AUD 0.30 per domestic card transaction. International cards: 2.9 percent + AUD 0.30.

Square

Good for apps that bridge online and in-person payments. The SDK supports card-present transactions via Square hardware. Australian pricing: 1.6 percent per tap/insert transaction.

PayPal / Braintree

Braintree (owned by PayPal) provides PayPal checkout alongside card processing. Useful if PayPal is important to your users. Australian pricing: 2.6 percent + AUD 0.30 per transaction.

Stripe Integration

Stripe Integration for Mobile Infographic for Mobile

iOS Integration

Add Stripe’s iOS SDK:

# Podfile
pod 'Stripe'

Configure Stripe in your app:

import Stripe

// AppDelegate or SwiftUI App init
STPAPIClient.shared.publishableKey = "pk_live_your_publishable_key"

Creating a Payment Intent (Server-Side)

Payments always start on your server. Never create payment intents from the client.

// Node.js server
const stripe = require('stripe')('sk_live_your_secret_key');

app.post('/create-payment-intent', async (req, res) => {
  const { amount, currency, customerId } = req.body;

  const paymentIntent = await stripe.paymentIntents.create({
    amount: amount, // Amount in cents (e.g., 1000 = $10.00 AUD)
    currency: currency || 'aud',
    customer: customerId,
    automatic_payment_methods: {
      enabled: true,
    },
    metadata: {
      order_id: req.body.orderId,
    },
  });

  res.json({
    clientSecret: paymentIntent.client_secret,
  });
});

Collecting Payment on iOS

Using Stripe’s prebuilt UI:

import Stripe

class CheckoutViewController: UIViewController {
    var paymentSheet: PaymentSheet?

    func preparePaymentSheet() {
        // Fetch PaymentIntent client secret from your server
        APIClient.shared.createPaymentIntent(amount: 2999) { clientSecret in
            var configuration = PaymentSheet.Configuration()
            configuration.merchantDisplayName = "Your App Name"
            configuration.applePay = .init(
                merchantId: "merchant.au.com.yourapp",
                merchantCountryCode: "AU"
            )
            configuration.defaultBillingDetails.address.country = "AU"

            self.paymentSheet = PaymentSheet(
                paymentIntentClientSecret: clientSecret,
                configuration: configuration
            )

            // Enable the pay button
            self.payButton.isEnabled = true
        }
    }

    @IBAction func payButtonTapped(_ sender: UIButton) {
        paymentSheet?.present(from: self) { result in
            switch result {
            case .completed:
                self.showSuccess()
            case .canceled:
                // User cancelled, do nothing
                break
            case .failed(let error):
                self.showError(error.localizedDescription)
            }
        }
    }
}

Android Integration

Add Stripe’s Android SDK:

// build.gradle
dependencies {
    implementation 'com.stripe:stripe-android:19.3.0'
}
import com.stripe.android.PaymentConfiguration
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.PaymentSheetResult

class CheckoutActivity : AppCompatActivity() {
    private lateinit var paymentSheet: PaymentSheet
    private var clientSecret: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        PaymentConfiguration.init(this, "pk_live_your_publishable_key")

        paymentSheet = PaymentSheet(this) { result ->
            onPaymentResult(result)
        }

        fetchPaymentIntent()
    }

    private fun fetchPaymentIntent() {
        // Call your server to create a PaymentIntent
        ApiClient.createPaymentIntent(amount = 2999) { secret ->
            clientSecret = secret
            payButton.isEnabled = true
        }
    }

    fun onPayButtonClicked() {
        val secret = clientSecret ?: return

        paymentSheet.presentWithPaymentIntent(
            secret,
            PaymentSheet.Configuration(
                merchantDisplayName = "Your App Name",
                googlePay = PaymentSheet.GooglePayConfiguration(
                    environment = PaymentSheet.GooglePayConfiguration.Environment.Production,
                    countryCode = "AU",
                    currencyCode = "AUD"
                )
            )
        )
    }

    private fun onPaymentResult(result: PaymentSheetResult) {
        when (result) {
            is PaymentSheetResult.Completed -> showSuccess()
            is PaymentSheetResult.Canceled -> { /* User cancelled */ }
            is PaymentSheetResult.Failed -> showError(result.error.message)
        }
    }
}

Apple Pay Integration Infographic

Apple Pay Integration

Apple Pay provides a fast, secure checkout experience. Australian users with supported bank cards can pay with Face ID or Touch ID.

Prerequisites

  1. Apple Developer Program membership
  2. Merchant ID configured in Apple Developer portal
  3. Payment processing certificate generated and shared with your payment processor

Implementation

import PassKit

class ApplePayManager: NSObject, PKPaymentAuthorizationViewControllerDelegate {

    func canMakePayments() -> Bool {
        return PKPaymentAuthorizationViewController.canMakePayments(
            usingNetworks: [.visa, .masterCard, .amex],
            capabilities: .capability3DS
        )
    }

    func startPayment(amount: NSDecimalNumber, description: String) {
        let request = PKPaymentRequest()
        request.merchantIdentifier = "merchant.au.com.yourapp"
        request.supportedNetworks = [.visa, .masterCard, .amex]
        request.merchantCapabilities = .capability3DS
        request.countryCode = "AU"
        request.currencyCode = "AUD"

        request.paymentSummaryItems = [
            PKPaymentSummaryItem(label: description, amount: amount),
            PKPaymentSummaryItem(label: "Your App Name", amount: amount)
        ]

        guard let controller = PKPaymentAuthorizationViewController(paymentRequest: request) else {
            return
        }
        controller.delegate = self
        presentingViewController?.present(controller, animated: true)
    }

    func paymentAuthorizationViewController(
        _ controller: PKPaymentAuthorizationViewController,
        didAuthorizePayment payment: PKPayment,
        handler completion: @escaping (PKPaymentAuthorizationResult) -> Void
    ) {
        // Send payment.token to your server for processing via Stripe
        APIClient.shared.processApplePayToken(payment.token) { success in
            if success {
                completion(PKPaymentAuthorizationResult(status: .success, errors: nil))
            } else {
                completion(PKPaymentAuthorizationResult(status: .failure, errors: nil))
            }
        }
    }

    func paymentAuthorizationViewControllerDidFinish(
        _ controller: PKPaymentAuthorizationViewController
    ) {
        controller.dismiss(animated: true)
    }
}

Google Pay Integration

Google Pay is the Android equivalent. Most Australian bank cards are supported.

import com.google.android.gms.wallet.*

class GooglePayManager(private val activity: Activity) {
    private val paymentsClient: PaymentsClient = Wallet.getPaymentsClient(
        activity,
        Wallet.WalletOptions.Builder()
            .setEnvironment(WalletConstants.ENVIRONMENT_PRODUCTION)
            .build()
    )

    fun isReadyToPay(callback: (Boolean) -> Unit) {
        val request = IsReadyToPayRequest.fromJson("""
            {
                "apiVersion": 2,
                "apiVersionMinor": 0,
                "allowedPaymentMethods": [{
                    "type": "CARD",
                    "parameters": {
                        "allowedAuthMethods": ["PAN_ONLY", "CRYPTOGRAM_3DS"],
                        "allowedCardNetworks": ["VISA", "MASTERCARD", "AMEX"]
                    }
                }]
            }
        """)

        paymentsClient.isReadyToPay(request).addOnCompleteListener { task ->
            callback(task.isSuccessful && task.result == true)
        }
    }

    fun requestPayment(amountCents: Long) {
        val request = PaymentDataRequest.fromJson("""
            {
                "apiVersion": 2,
                "apiVersionMinor": 0,
                "allowedPaymentMethods": [{
                    "type": "CARD",
                    "parameters": {
                        "allowedAuthMethods": ["PAN_ONLY", "CRYPTOGRAM_3DS"],
                        "allowedCardNetworks": ["VISA", "MASTERCARD", "AMEX"]
                    },
                    "tokenizationSpecification": {
                        "type": "PAYMENT_GATEWAY",
                        "parameters": {
                            "gateway": "stripe",
                            "stripe:publishableKey": "pk_live_your_key",
                            "stripe:version": "2020-08-27"
                        }
                    }
                }],
                "transactionInfo": {
                    "totalPriceStatus": "FINAL",
                    "totalPrice": "${amountCents / 100.0}",
                    "currencyCode": "AUD",
                    "countryCode": "AU"
                },
                "merchantInfo": {
                    "merchantName": "Your App Name"
                }
            }
        """)

        AutoResolveHelper.resolveTask(
            paymentsClient.loadPaymentData(request),
            activity,
            GOOGLE_PAY_REQUEST_CODE
        )
    }
}

Handling Subscriptions

For recurring payments, use Stripe Billing or platform-specific IAP:

When to Use Stripe Billing vs In-App Purchases

Use In-App Purchases (Apple/Google) when:

  • The subscription unlocks digital content or features within the app
  • Apple and Google require it (digital goods and services must use IAP on their platforms)

Use Stripe Billing when:

  • The subscription is for physical goods or real-world services
  • The payment is for professional/enterprise services
  • You need more control over billing logic

Apple’s guidelines are clear: apps that sell digital content must use Apple’s IAP system. Using Stripe to bypass IAP for digital subscriptions will result in App Store rejection.

Stripe Subscription Implementation

// Server: Create subscription
app.post('/create-subscription', async (req, res) => {
  const { customerId, priceId } = req.body;

  const subscription = await stripe.subscriptions.create({
    customer: customerId,
    items: [{ price: priceId }],
    payment_behavior: 'default_incomplete',
    expand: ['latest_invoice.payment_intent'],
  });

  res.json({
    subscriptionId: subscription.id,
    clientSecret:
      subscription.latest_invoice.payment_intent.client_secret,
  });
});

// Handle subscription webhooks
app.post('/stripe-webhook', async (req, res) => {
  const event = stripe.webhooks.constructEvent(
    req.body,
    req.headers['stripe-signature'],
    process.env.STRIPE_WEBHOOK_SECRET
  );

  switch (event.type) {
    case 'invoice.paid':
      activateSubscription(event.data.object);
      break;
    case 'invoice.payment_failed':
      handleFailedPayment(event.data.object);
      break;
    case 'customer.subscription.deleted':
      deactivateSubscription(event.data.object);
      break;
  }

  res.json({ received: true });
});

Australian Regulatory Considerations

GST

The 10 percent GST applies to most goods and services sold in Australia. For digital products sold through the app stores, Apple and Google handle GST collection and remittance.

For payments processed through Stripe or other processors, you are responsible for GST:

  • Include GST in your displayed prices (Australian consumers expect inclusive pricing)
  • Issue tax invoices for business customers
  • Report and remit GST to the ATO

PCI Compliance

Payment Card Industry Data Security Standard (PCI DSS) compliance is mandatory when handling card data. Using Stripe’s SDK ensures your app never touches raw card numbers. Stripe handles PCI compliance for you.

Never:

  • Store card numbers in your database
  • Log card details
  • Send card numbers to your server (use Stripe’s tokenisation)

Consumer Protection

Australian Consumer Law requires:

  • Clear pricing before purchase
  • Refund mechanisms that comply with ACL
  • Transparent subscription terms (price, frequency, cancellation process)
  • No misleading conduct regarding pricing or features

Strong Customer Authentication

While PSD2/SCA is a European requirement, many Australian banks support 3D Secure authentication. Stripe handles 3DS challenges automatically when required by the issuing bank.

Error Handling and Edge Cases

Payment Failures

Handle payment failures gracefully:

func handlePaymentError(_ error: Error) {
    if let stripeError = error as? StripeError {
        switch stripeError {
        case .apiError(let apiError):
            switch apiError.code {
            case .cardDeclined:
                showMessage("Your card was declined. Please try a different payment method.")
            case .expiredCard:
                showMessage("Your card has expired. Please update your payment details.")
            case .insufficientFunds:
                showMessage("Insufficient funds. Please try a different card.")
            default:
                showMessage("Payment failed. Please try again.")
            }
        default:
            showMessage("An error occurred. Please try again.")
        }
    }
}

Network Failures

Payments must handle network interruptions:

  • Show a clear loading state during payment processing
  • If the network drops, inform the user and allow retry
  • Use Stripe’s idempotency keys to prevent duplicate charges on retry
  • Never assume a payment failed just because you did not receive a response; check with your server

Currency Formatting

Always format currency correctly for Australian users:

let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencyCode = "AUD"
formatter.locale = Locale(identifier: "en_AU")
let formatted = formatter.string(from: NSNumber(value: 29.99))
// Result: "$29.99"

Testing

Stripe Test Mode

Use Stripe’s test mode with test card numbers:

  • 4242 4242 4242 4242: Successful payment
  • 4000 0000 0000 0002: Card declined
  • 4000 0000 0000 3220: 3D Secure authentication required

Apple Pay Testing

Test Apple Pay in the sandbox environment using test cards in your Apple Developer sandbox account.

Google Pay Testing

Use Google Pay’s test environment. Set the environment to ENVIRONMENT_TEST during development.

Payment integration is one of the highest-stakes features in any app. Errors lose money and trust. At eawesome, we implement and test payment flows meticulously for our Australian clients, because every failed transaction is a lost customer.