2022 App Services
WWDC22 · 36 min · App Services
What’s new in Wallet and Apple Pay
Discover the latest updates to Wallet & Apple Pay. We’ll show you how to support Orders in Wallet for your apps and websites and securely validate someone’s age and identity with the Identity Verification API. We’ll also explore PassKit support for SwiftUI, and discuss how you how you can improve your Apple Pay experience with Automatic Payments.
Watch at developer.apple.com ↗Code shown on screen · 11 snippets
AddPassToWalletButton
var addedToWallet: Bool
private var airlineButton: some View {
if let pass = createAirlinePass() {
AddPassToWalletButton([pass]) { added in
addedToWallet = added
}
.frame(width: 250, height: 50)
.addPassToWalletButtonStyle(.blackOutline)
} else {
// Fallback
}
} PayWithApplePayButton
// Create a payment request
let paymentRequest = PKPaymentRequest()
// ...
// Create a payment authorization change method
func authorizationChange(phase: PayWithApplePayButtonPaymentAuthorizationPhase) { ... }
PayWithApplePayButton(
.plain,
request: paymentRequest,
onPaymentAuthorizationChange: authorizationChange
) {
// Fallback
}
.frame(width: 250, height: 50)
.payWithApplePayButtonStyle(.automatic) Multi-merchant payments
// Create a payment request
let paymentRequest = PKPaymentRequest()
// ...
// Set total amount
paymentRequest.paymentSummaryItems = [
PKPaymentSummaryItem(label: "Total", amount: 500)
]
// Create a multi token context for each additional merchant in the payment
let multiTokenContexts = [
PKPaymentTokenContext(
merchantIdentifier: "com.example.air-travel",
externalIdentifier: "com.example.air-travel",
merchantName: "Air Travel",
merchantDomain: "air-travel.example.com",
amount: 150
),
PKPaymentTokenContext(
merchantIdentifier: "com.example.hotel",
externalIdentifier: "com.example.hotel",
merchantName: "Hotel",
merchantDomain: "hotel.example.com",
amount: 300
),
PKPaymentTokenContext(
merchantIdentifier: "com.example.car-rental",
externalIdentifier: "com.example.car-rental",
merchantName: "Car Rental",
merchantDomain: "car-rental.example.com",
amount: 50
)
]
paymentRequest.multiTokenContexts = multiTokenContexts Automatic Payments - Recurring payment request
// Specify the amount and billing periods
let regularBilling = PKRecurringPaymentSummaryItem(label: "Membership", amount: 20)
let trialBilling = PKRecurringPaymentSummaryItem(label: "Trial Membership", amount: 10)
let trialEndDate = Calendar.current.date(byAdding: .month, value: 1, to: Date.now)
trialBilling.endDate = trialEndDate
regularBilling.startDate = trialEndDate
// Create a recurring payment request
let recurringPaymentRequest = PKRecurringPaymentRequest(
paymentDescription: "Book Club Membership",
regularBilling: regularBilling,
managementURL: URL(string: "https://www.example.com/managementURL")!
)
recurringPaymentRequest.trialBilling = trialBilling
recurringPaymentRequest.billingAgreement = """
50% off for the first month. You will be charged $20 every month after that until you cancel. \ You may cancel at any time to avoid future charges. To cancel, go to your Account and click \ Cancel Membership.
"""
recurringPaymentRequest.tokenNotificationURL = URL(
string: "https://www.example.com/tokenNotificationURL"
)!
// Update the payment request
let paymentRequest = PKPaymentRequest()
// ...
paymentRequest.recurringPaymentRequest = recurringPaymentRequest
// Include in the summary items
let total = PKRecurringPaymentSummaryItem(label: "Book Club", amount: 10)
total.endDate = trialEndDate
paymentRequest.paymentSummaryItems = [trialBilling, regularBilling, total] Automatic Payments - Automatic reload payment request
// Specify the reload amount and threshold
let automaticReloadBilling = PKAutomaticReloadPaymentSummaryItem(
label: "Coffee Shop Reload",
amount: 25
)
reloadItem.thresholdAmount = 5
// Create an automatic reload payment request
let automaticReloadPaymentRequest = PKAutomaticReloadPaymentRequest(
paymentDescription: "Coffee Shop",
automaticReloadBilling: automaticReloadBilling,
managementURL: URL(string: "https://www.example.com/managementURL")!
)
automaticReloadPaymentRequest.billingAgreement = """
Coffee Shop will add $25.00 to your card immediately, and will automatically reload your \
card with $25.00 whenever the balance falls below $5.00. You may cancel at any time to avoid \ future charges. To cancel, go to your Account and click Cancel Reload.
"""
automaticReloadPaymentRequest.tokenNotificationURL = URL(
string: "https://www.example.com/tokenNotificationURL"
)!
// Update the payment request
let paymentRequest = PKPaymentRequest()
// ...
paymentRequest.automaticReloadPaymentRequest = automaticReloadPaymentRequest
// Include in the summary items
let total = PKAutomaticReloadPaymentSummaryItem(
label: "Coffee Shop",
amount: 25
)
total.thresholdAmount = 5
paymentRequest.paymentSummaryItems = [total] Order Tracking (swift)
func onAuthorizationChange(phase: PayWithApplePayButtonPaymentAuthorizationPhase) {
switch phase {
// ...
case .didAuthorize(let payment, let resultHandler):
server.createOrder(with: payment) { serverResult in
guard case .success(let orderDetails) = serverResult else { /* handle error */ }
let result = PKPaymentAuthorizationResult(status: .success, errors: nil)
result.orderDetails = PKPaymentOrderDetails(
orderTypeIdentifier: orderDetails.orderTypeIdentifier,
orderIdentifier: orderDetails.orderIdentifier,
webServiceURL: orderDetails.webServiceURL,
authenticationToken: orderDetails.authenticationToken,
)
resultHandler(result)
}
}
} Order Tracking (JS)
paymentRequest.show().then((response) => {
server.createOrder(response).then((orderDetails) => {
let details = { };
if (response.methodName === "https://apple.com/apple-pay") {
details.data = {
orderDetails: {
orderTypeIdentifier: orderDetails.orderTypeIdentifier,
orderIdentifier: orderDetails.orderIdentifier,
webServiceURL: orderDetails.webServiceURL,
authenticationToken: orderDetails.authenticationToken,
},
};
}
response.complete("success", details);
});
}); VerifyIdentityWithWalletButton 2
var verifiyIdentityButton: some View {
VerifyIdentityWithWalletButton(
.verifyIdentity,
request: createRequest(),
) { result in
// ...
} fallback: {
// verify identity another way
}
} Create a PKIdentityRequest
func createRequest() -> PKIdentityRequest {
let descriptor = PKIdentityDriversLicenseDescriptor()
descriptor.addElements([.age(atLeast: 18)],
intentToStore: .willNotStore)
descriptor.addElements([.givenName, .familyName, .portrait],
intentToStore: .mayStore(days: 30))
let request = PKIdentityRequest()
request.descriptor = descriptor
request.merchantIdentifier = // configured in Developer account
request.nonce = // bound to user session
} VerifyIdentityWithWalletButton 3
var verifiyIdentityButton: some View {
VerifyIdentityWithWalletButton(
.verifyIdentity,
request: createRequest(),
) { result in
// ...
} fallback: {
// verify identity another way
}
} VerifyIdentityWithWalletButton 4
var verifiyIdentityButton: some View {
VerifyIdentityWithWalletButton(
.verifyIdentity,
request: createRequest(),
) { result in
switch result {
case .success(let document):
// send document to server for decryption and verification
case .failure(let error):
switch error {
case PKIdentityError.cancelled:
// handle cancellation
default:
// handle other errors
}
}
} fallback: {
// verify identity another way
}
} Resources
- Example Order Packages
- ApplePayPaymentRequest
- Apple Pay Merchant Token Management API
- Wallet Orders
- Human Interface Guidelines: Wallet
- Verifying Wallet identity requests
- Requesting identity data from a Wallet pass
- Apple Pay on the Web Interactive Demo
- Apple Pay on the Web
- PassKit (Apple Pay and Wallet)
- Apple Pay
Related sessions
-
34 min