Apple Pay is Apple’s mobile payment service. We can use Apple Pay to make a tap-to-pay in physical stores or purchase goods in apps. In addition, the Wallet app in iPhone can manage credit cards, so we don’t need to carry several credit cards. This article will introduce how to integrate Apple Pay into iOS app.
The complete code for this chapter can be found in .
Table of Contents
Creating Merchant ID
Before we start writing the code, we need to create a merchant ID. Login to Apple developer and enter the Identifiers
page. In the upper right corner of the page, select Merchant IDs
. Click Register a Merchant ID
.
Select Merchant IDs
.
In the field on the right, enter your merchant ID. Apple recommends using reverse-domain name style for your merchant ID.
After confirming them, click Register
.
The merchant IDs list now contains the merchant ID we just created. Click on it to see its details.
Click Create Certificate
under Apple Pay Payment Processing Certificate.
Select Yes
.
Here we are asked to upload a Certificate Signing Request.
Open Keychain Access
in your Mac OS and select Certificate Assistant -> Request a Certificate From a Certificate Authority…
.
Enter your email in User Email Address.
CA Email Address
can be left blank. After clicking Continue
, a CertificateSigningRequest.certSigningRequest will be stored.
Click Choose File
and upload the CertificateSigningRequest.certSigningRequest just created.
Finally, the Apple Pay Payment Processing Certificate has been created. Click Download
to download apple_pay.cer.
Returning to the merchant ID details, you can see that the Apple Pay Payment Processing Certificate has been created.
Adding Apple Pay to Capabilities
Open the project with Xcode and go to Signing & Capabilities
.
Click the +
in the upper right corner to add Apple Pay to Capabilities.
After adding Apple Pay, Xcode will automatically synchronize the merchant IDs. Select the merchant ID we just created.
Now the project is set up. Open the entitlements file in the project, and you can see that Xcode helps us add the selected merchant ID to the entitlements file.
Initializing PKPaymentButton
Declare a PKPaymentButton in the ViewController and choose the style you want. Then, put it on the view.
import UIKit import PassKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() initPaymentButton() } private func initPaymentButton() { var button: UIButton? = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .white) button?.addTarget(self, action: #selector(ViewController.onPayPressed), for: .touchUpInside) if let button = button { let constraints = [ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.centerYAnchor.constraint(equalTo: view.centerYAnchor) ] button.translatesAutoresizingMaskIntoConstraints = false view.addSubview(button) NSLayoutConstraint.activate(constraints) } } @objc private func onPayPressed(sender: AnyObject) { // TODO: start payment } }
Checking If Apple Pay is Ready
After setting up the PKPaymentButton, we need to check whether Apple Pay is ready. We only display the PKPaymentButton if Apple Pay is ready.
class ViewController: UIViewController { private let viewModel = ViewModel() override func viewDidLoad() { super.viewDidLoad() initPaymentButton() } private func initPaymentButton() { var button: UIButton? if viewModel.canMakePayment() { button = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .white) button?.addTarget(self, action: #selector(ViewController.onPayPressed), for: .touchUpInside) } if let button = button { let constraints = [ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.centerYAnchor.constraint(equalTo: view.centerYAnchor) ] button.translatesAutoresizingMaskIntoConstraints = false view.addSubview(button) NSLayoutConstraint.activate(constraints) } } @objc private func onPayPressed(sender: AnyObject) { // TODO: start payment } }
Use PKPaymentAuthorizationController.canMakePayments(usingNetworks:)
to check if this device can use Apple Pay.
class ViewModel { private static let supportedNetworks: [PKPaymentNetwork] = [ .amex, .discover, .masterCard, .visa ] func canMakePayment() -> Bool { return PKPaymentAuthorizationController.canMakePayments(usingNetworks: Self.supportedNetworks) } }
Executing Payment
The last step is payment. We must use PKPaymentAuthorizationController to display the Apple Pay payment view. So we tell it the payment amount, currency, shipping information, merchant ID, etc.
In addition, we also need to set PKPaymentAuthorizationControllerDelegate. When the payment is completed, we will receive a PKPayment. We can get the token from PKPayment and send the token to the server.
Finally, when the payment is completed, the Apple Pay payment view will not automatically dismiss. We have to manually call PKPaymentAuthorizationController.dismiss().
import UIKit import PassKit class ViewModel: NSObject { private static let merchantID = "merchant.com.waynestalk" private static let supportedNetworks: [PKPaymentNetwork] = [ .amex, .discover, .masterCard, .visa ] var paymentController: PKPaymentAuthorizationController? var paymentSummaryItems = [PKPaymentSummaryItem]() var paymentStatus = PKPaymentAuthorizationStatus.failure var completion: ((Bool) -> Void)? ... func startPayment(completion: @escaping (Bool) -> Void) { self.completion = completion let ticket = PKPaymentSummaryItem(label: "Cloth", amount: NSDecimalNumber(string: "9.99"), type: .final) let tax = PKPaymentSummaryItem(label: "Tax", amount: NSDecimalNumber(string: "1.00"), type: .final) let total = PKPaymentSummaryItem(label: "Total", amount: NSDecimalNumber(string: "10.99"), type: .final) paymentSummaryItems = [ticket, tax, total] let paymentRequest = PKPaymentRequest() paymentRequest.paymentSummaryItems = paymentSummaryItems paymentRequest.merchantIdentifier = Self.merchantID paymentRequest.merchantCapabilities = .threeDSecure paymentRequest.countryCode = "US" paymentRequest.currencyCode = "USD" paymentRequest.supportedNetworks = Self.supportedNetworks paymentRequest.shippingType = .storePickup paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest) paymentController?.delegate = self paymentController?.present(completion: { (presented: Bool) in if presented { debugPrint("Presented payment controller") } else { debugPrint("Failed to present payment controller") self.completion?(false) } }) } } extension ViewModel: PKPaymentAuthorizationControllerDelegate { func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) { // Perform basic validation on the provided contact information. var errors = [Error]() var status = PKPaymentAuthorizationStatus.success if payment.shippingContact?.postalAddress?.isoCountryCode != "US" { let pickupError = PKPaymentRequest.paymentShippingAddressUnserviceableError(withLocalizedDescription: "Sample App only available in the United States") let countryError = PKPaymentRequest.paymentShippingAddressInvalidError(withKey: CNPostalAddressCountryKey, localizedDescription: "Invalid country") errors.append(pickupError) errors.append(countryError) status = .failure } else if let token = String(data: payment.token.paymentData, encoding: .utf8) { // Send the payment token to your server or payment provider to process here. // Once processed, return an appropriate status in the completion handler (success, failure, and so on). debugPrint(token) } self.paymentStatus = status completion(PKPaymentAuthorizationResult(status: status, errors: errors)) } func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) { controller.dismiss { DispatchQueue.main.async { if self.paymentStatus == .success { self.completion?(true) } else { self.completion?(false) } } } } }
import UIKit import PassKit class ViewController: UIViewController { private let viewModel = ViewModel() ... @objc private func onPayPressed(sender: AnyObject) { viewModel.startPayment { if $0 { debugPrint("payment has succeeded") } } } }
Conclusion
Apple Pay integration isn’t difficult. On the contrary, the steps to create a merchant ID are more troublesome. Also, we cannot directly add PKPaymentButton in Storyboard. When the view is complex, it is not so easy to manually add PKPaymentButton to the view.
Reference
- PassKit (Apple Pay and Wallet), Apple Developer.