Introduction

Sometime ago, I was working on a marketplace app, and I needed to add Apple Pay to make purchases more easily. Here are a few steps on how I did it:

First Step

  • You need to add Apple Pay capability to your project.
  • You will need to Register a Merchant ID. I will skip this step; you can find info by following this link Setting up Apple Pay.

Second Step

  func initiateApplePay() {
        // Create payment request
        let paymentRequest = PKPaymentRequest()
        paymentRequest.merchantIdentifier = "your_merchant_identifier"
        paymentRequest.countryCode = "US"
        paymentRequest.currencyCode = "USD"
        paymentRequest.supportedNetworks = [.visa, .masterCard, .amex]
        paymentRequest.merchantCapabilities = .threeDSecure

        // Add payment items from cart
        for item in cartItems {
            let paymentItem = PKPaymentSummaryItem(label: item.name, amount: item.price)
            paymentRequest.paymentSummaryItems.append(paymentItem)
        }

        // Add total amount
        let totalItem = PKPaymentSummaryItem(label: "Total", amount: totalAmount)
        paymentRequest.paymentSummaryItems.append(totalItem)

        // Present Apple Pay sheet
        let paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
        paymentController.delegate = self
        paymentController.present(completion: nil)
    }

Third Step

Add UI and connect it with the view model.

UI

import SwiftUI

struct ContentView: View {
    @StateObject private var viewModel = MarketplaceViewModel()

    var body: some View {
        VStack {
            List(viewModel.products) { product in
                HStack {
                    Text(product.name)
                    Spacer()
                    Text(product.price.stringValue + "$")
                    Button(viewModel.inCart(product: product) ? "" : "Add to cart") {
                        viewModel.addToCart(product: product)
                    }
                }
            }
            Text("Total: \(viewModel.totalAmount)")
            Button("Pay with Apple Pay") {
                viewModel.initiateApplePay()
            }
            .padding()
        }
    }
}

#Preview {
    ContentView()
}

ViewModel

final class MarketplaceViewModel: NSObject, ObservableObject {

    private var cartItems: [Product] = []
    @Published private(set) var totalAmount: NSDecimalNumber = 0.0
    @Published private(set) var products: [Product] = []

    func fetchProducts() {
        Task {
            self.products = await ProductService.getProducts()
        }
    }

    func inCart(product: Product) -> Bool {
        cartItems.contains(product)
    }

    override init() {
        super.init()
        fetchProducts()
    }

    private func calculateTotalAmount() {
        totalAmount = cartItems.reduce(0) { $0.adding($1.price) }
    }

    func addToCart(product: Product) {
        cartItems.append(product)
        calculateTotalAmount()
    }

    func initiateApplePay() {
        // Create payment request
        let paymentRequest = PKPaymentRequest()
        paymentRequest.merchantIdentifier = "your_merchant_identifier"
        paymentRequest.countryCode = "US"
        paymentRequest.currencyCode = "USD"
        paymentRequest.supportedNetworks = [.visa, .masterCard, .amex]
        paymentRequest.merchantCapabilities = .threeDSecure

        // Add payment items from cart
        for item in cartItems {
            let paymentItem = PKPaymentSummaryItem(label: item.name, amount: item.price)
            paymentRequest.paymentSummaryItems.append(paymentItem)
        }

        // Add total amount
        let totalItem = PKPaymentSummaryItem(label: "Total", amount: totalAmount)
        paymentRequest.paymentSummaryItems.append(totalItem)

        // Present Apple Pay sheet
        let paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
        paymentController.delegate = self
        paymentController.present(completion: nil)
    }

}

// MARK: - PKPaymentAuthorizationControllerDelegate
extension MarketplaceViewModel: PKPaymentAuthorizationControllerDelegate {

    func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
        let paymentResult = PKPaymentAuthorizationResult(status: .success, errors: nil)
        completion(paymentResult)
    }

    func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
        controller.dismiss(completion: nil)
    }

}

Helpers

struct Product: Identifiable, Equatable {
    let id: UUID
    let name: String
    let price: NSDecimalNumber
}
final class ProductService {
    static func getProducts() async -> [Product]  {
        let products: [Product] = [
            Product(id: UUID(), name: "Product 1", price: 10.0),
            Product(id: UUID(), name: "Product 2", price: 20.0),
            Product(id: UUID(), name: "Product 3", price: 15.0)
        ]
        return products
    }
}

Thank you for reading! 😊