Introducing the Payment Request API for Apple Pay | WebKit
Introducing the Payment Request API for Apple Pay
Apr 5, 2018
by
Andy Estes
We’re pleased to announce that Safari 11.1 on macOS and Safari on iOS 11.3 now support the
W3C Payment Request API
for conducting Apple Pay transactions on the web.
We first introduced
Apple Pay on the Web
in macOS Sierra and iOS 10. Apple Pay brought novel ease-of-use, security, and privacy to online transactions, so we’ve been encouraged to see it widely adopted by merchants in the years since. However, we recognize that merchants need to support multiple payment methods, and doing so comes at the cost of increased complexity. Payment Request aims to reduce this complexity by supporting payment methods across multiple browsers using a standard API.
Today I’ll take you step-by-step through an Apple Pay transaction using Payment Request. If you’ve already worked with Apple Pay JS, this should look familiar to you. If you haven’t, this is a great place to get started!
Prerequisites
This post assumes that you have already
registered a merchant identifier
and
set up your server environment
to process Apple Pay transactions. You can find detailed instructions for doing this in the
Apple Pay on the Web
documentation.
Showing Apple Pay Buttons
Apple Pay transactions are always initiated by your customer, so you’ll need to display an Apple Pay button for them to tap or click. For a consistent visual appearance, WebKit provides built-in support for rendering Apple Pay buttons. Here’s how to display the most basic type of Apple Pay button:
button
style
"-webkit-appearance: -apple-pay-button;"
button
There are several other types of buttons available in various styles. Here’s a few that you might have seen while shopping in Safari:
See
Displaying Apple Pay Buttons
for more information on the button types and styles available.
According to the
Apple Pay on the Web Human Interface Guidelines
, you should display Apple Pay buttons whenever your customer is using a
supported device
. To check if your customer’s device supports Apple Pay, call
ApplePaySession.canMakePayments()
if
window
ApplePaySession
ApplePaySession
canMakePayments
())
// Show Apple Pay button.
If your customer taps or clicks an Apple Pay button, you should always present the Apple Pay payment sheet. If they haven’t yet enrolled a payment card you can accept in Apple Pay, Safari prompts them to do so before continuing with your transaction. Since these rules are specific to Apple Pay, Apple Pay JS’s
ApplePaySession
API is required for this step. The remainder of the transaction can be conducted using standard Payment Request API.
Constructing a
PaymentRequest
When your customer taps or clicks an Apple Pay button, you initiate the transaction by
constructing a new
PaymentRequest
var
request
null
if
window
PaymentRequest
request
new
PaymentRequest
methods
details
options
);
else
// Consider using Apple Pay JS instead.
The
PaymentRequest
constructor takes three arguments: the payment methods you support, the details to show to your customer (like shipping options and total amount), and any options you require.
Payment Methods
Payment methods represent the means by which you can accept payments from your customer using Payment Request. You specify the payment methods you accept as a sequence of
PaymentMethodData
dictionaries, each of which contains an identifier (
supportedMethods
) and associated
data
To use Apple Pay with Payment Request, include it as a payment method. Apple Pay’s
URL-based
payment method identifier is
"https://apple.com/apple-pay"
, and its associated
data
is an
ApplePayRequest
dictionary. Here’s what a
PaymentMethodData
dictionary for Apple Pay might look like:
const
applePayMethod
supportedMethods
"https://apple.com/apple-pay"
data
version
merchantIdentifier
"merchant.com.example"
merchantCapabilities
"supports3DS"
"supportsCredit"
"supportsDebit"
],
supportedNetworks
"amex"
"discover"
"masterCard"
"visa"
],
countryCode
"US"
},
};
Safari only supports the Apple Pay payment method, but other browsers might support additional payment methods. When you specify multiple payment methods in a request, the browser decides which to present to your customer based on device availability and user preference.
Payment Details
Payment details are represented by the
PaymentDetailsInit
dictionary. It contains your transaction’s total amount, display items, shipping options, and payment method-specific modifiers (more on modifiers below). Here’s what valid payment details might look like for a $20 item plus tax and two shipping options:
const
paymentDetails
total
label
"My Merchant"
amount
value
"27.50"
currency
"USD"
},
},
displayItems
[{
label
"Tax"
amount
value
"2.50"
currency
"USD"
},
}, {
label
"Ground Shipping"
amount
value
"5.00"
currency
"USD"
},
}],
shippingOptions
[{
id
"ground"
label
"Ground Shipping"
amount
value
"5.00"
currency
"USD"
},
selected
true
}, {
id
"express"
label
"Express Shipping"
amount
value
"10.00"
currency
"USD"
},
}],
};
You can choose a default shipping option by setting the
selected
attribute to
true
as we did above for
"Ground Shipping"
. The total amount must not be negative, and when using Apple Pay, all payment amounts in your request must use the same
ISO 4217 currency code
. It is up to you to ensure the correctness of your payment details; Safari does not do any currency calculations on your behalf.
What about modifiers?
You can optionally include a sequence of
modifiers
in your payment details. Modifiers update your transaction’s display items and total when criteria you specify for a given payment method are satisfied. In Apple Pay, you can use modifiers to adjust the price based on the type of payment card selected in the Apple Pay payment sheet. For instance, the following modifier applies a $1.00 discount when your customer selects a debit card in Apple Pay:
const
debitModifier
supportedMethods
"https://apple.com/apple-pay"
data
paymentMethodType
"debit"
},
total
label
"My Merchant"
amount
value
"26.50"
currency
"USD"
},
},
additionalDisplayItems
[{
label
"Debit Card Discount"
amount
value
"-1.00"
currency
"USD"
},
}],
};
Modifiers provide some of the functionality present in the
paymentmethodselected
event from Apple Pay JS. See
ApplePayModifier
for more information.
Payment Options
Payment options are represented by the
PaymentOptions
dictionary. If you need to request your customer’s name, email address, or phone number – or request a certain type of shipping – you can do so here:
const
paymentOptions
requestPayerName
true
requestPayerEmail
true
requestPayerPhone
true
requestShipping
true
shippingType
"shipping"
};
If you set
requestShipping
to
true
, the shipping options you specified in
Payment Details
are presented in the payment sheet for your customer to choose between. You receive the requested information once your customer authorizes payment.
Exceptions
Safari might raise an exception when constructing a new
PaymentRequest
. Exceptions can occur for the following reasons:
The frame is not in a
secure context
The frame is a cross-origin subframe.
No payment methods were specified.
A payment method identifier is
invalid
Calling
JSON.stringify()
on the payment method data failed.
Invalid currency amounts were specified (e.g., negative total or multiple currencies).
canMakePayment()
method
Once you’ve constructed a
PaymentRequest
, you can ask it if your customer will be able to authorize a transaction given the payment methods you can accept. You do this by calling the
canMakePayment()
method
, which returns a promise that resolves to either
true
or
false
. In Safari, when Apple Pay is one of the payment methods,
canMakePayment()
resolves to
true
only if your customer has an active card enrolled in Apple Pay. This is the equivalent of how
ApplePaySession.canMakePaymentsWithActiveCard()
behaves in Apple Pay JS.
As we discussed in
Showing Apple Pay Buttons
, the Apple Pay Human Interface Guidelines require you to show an Apple Pay button whenever your customer is on supported hardware,
whether or not
they have an active card enrolled. Therefore, you should
not
hide Apple Pay buttons when
canMakePayment()
resolves to
false
. Always show an Apple Pay button if
ApplePaySession.canMakePayments()
returns
true
, and always present the Apple Pay payment sheet when your customer taps or clicks the button. Safari prompts your customer to enroll a payment card if they haven’t done so already before continuing with your transaction. For more information, see
Human Interface Guidelines > Apple Pay on the Web
show()
method
When your customer taps or clicks an Apple Pay button, you should present the Apple Pay payment sheet. You do this by calling the
show()
method, which returns a promise that resolves to a
PaymentResponse
once your customer authorizes payment. The promise is rejected with an
AbortError
if your customer cancels the transaction.
You can optionally call
show()
with a promise for a
PaymentDetailsUpdate
. Sometimes you might still be in the process of calculating payment details when your customer taps or clicks the Apple Pay button. In this case, you can construct a new
PaymentRequest
with placeholders for details, then call
show()
with a promise to provide up-to-date details later. When you resolve this promise, Safari displays the updated details in the Apple Pay payment sheet.
Safari might reject the promise returned by
show()
with an exception. Exceptions can occur for the following reasons:
show()
was not triggered by user activation (e.g., a tap or click).
The request has already been aborted.
An Apple Pay session is already active.
Payment method data is invalid (e.g., is missing required fields).
abort()
method
If you need to abort the presented transaction, you can call the
abort()
method
. When you do this, Safari dismisses the Apple Pay payment sheet and rejects the promise returned by
show()
with an
AbortError
. If the transaction has already been aborted, or
show()
has not yet been called, calling
abort()
throws an
InvalidStateError
Merchant Validation
Before Safari can present the Apple Pay payment sheet, you must acquire a payment session from Apple. This process is referred to as
merchant validation
Soon after you call
show()
, Safari dispatches the
merchantvalidation
event to your
PaymentRequest
object. The event defines a
validationURL
attribute representing the Apple URL your server contacts to receive a payment session. You must call the event’s
complete()
method with a promise that you resolve with this payment session once you receive it.
Here is what a
merchantvalidation
event handler might look like:
request
onmerchantvalidation
function
event
) {
const
sessionPromise
fetchPaymentSession
event
validationURL
);
event
complete
sessionPromise
);
};
You can learn more about merchant validation from
Requesting an Apple Pay Payment Session
Shipping Events
Once you’ve received a merchant session, Safari presents the Apple Pay payment sheet to your customer. If you’ve requested shipping, your customer is able to select between your shipping options and provide a shipping address. When they make these selections in the payment sheet, Safari dispatches a
shippingoptionchange
or
shippingaddresschange
event to your
PaymentRequest
object.
shippingoptionchange
When the user selects a shipping option, Safari dispatches the
shippingoptionchange
event. In your event handler, you can determine the selected shipping option by checking the
PaymentRequest
‘s
shippingOption
attribute. To update the payment details based on the selected shipping option, call
updateWith()
on the event object with a promise that resolves to a
PaymentDetailsUpdate
When requesting shipping with Apple Pay, you must always listen for
shippingoptionchange
and call
updateWith()
with a promise that resolves within 30 seconds, otherwise, the transaction will time out.
shippingaddresschange
When your customer selects a shipping address, Safari dispatches the
shippingaddresschange
event. In your event handler, you can determine the selected shipping address by checking the
PaymentRequest
‘s
shippingAddress
attribute. To update the payment details based on the selected shipping address, call
updateWith()
on the event object with a promise that resolves to a
PaymentDetailsUpdate
. If you are unable to ship to the selected address, you can provide an error message in your
PaymentDetailsUpdate
that Safari displays to your customer.
When using Apple Pay, Safari might redact some details from the shipping address. For instance, in the United States, only city, state, 5-digit ZIP code, and country are provided. Safari provides the full, un-redacted shipping address once your customer authorizes payment.
When requesting shipping with Apple Pay, you must always listen for
shippingaddresschange
and call
updateWith()
with a promise that resolves within 30 seconds, otherwise the transaction will time out.
Handling Payment Authorization
When your customer authorizes payment, Safari resolves the promise you received from calling
show()
with a
PaymentResponse
. Depending on what you requested in your
PaymentOptions
, the response might contain the selected shipping option, shipping address, name, email, and phone number of your customer.
The response also contains the payment method identifier used to conduct the transaction (
methodName
), along with its associated
details
. When Apple Pay is the selected payment method, the associated
details
is an
ApplePayPayment
dictionary.
ApplePayPayment
contains the Apple Pay
token
you use to process the payment authorization. It also includes your customer’s billing and shipping contact information as
ApplePayPaymentContact
if you required this in your
ApplePayRequest
When you have finished processing the payment authorization, you call the
complete()
method
on
PaymentResponse
to indicate the result of your processing. You can call
complete()
with a status of
"success"
or
"failure"
. At this point, the Apple Pay payment sheet is dismissed.
Demo
You now have all the pieces you need to conduct an Apple Pay transaction using Payment Request. Here’s what an Apple Pay session might look like using Payment Request:
async
function
applePayButtonClicked
()
// Consider falling back to Apple Pay JS if Payment Request is not available.
if
window
PaymentRequest
return
try
const
request
new
PaymentRequest
([
applePayMethod
],
paymentDetails
paymentOptions
);
request
onmerchantvalidation
function
event
) {
// Have your server fetch a payment session from event.validationURL.
const
sessionPromise
fetchPaymentSession
event
validationURL
);
event
complete
sessionPromise
);
};
request
onshippingoptionchange
function
event
) {
// Compute new payment details based on the selected shipping option.
const
detailsUpdatePromise
computeDetails
();
event
updateWith
detailsUpdatePromise
);
};
request
onshippingaddresschange
function
event
) {
// Compute new payment details based on the selected shipping address.
const
detailsUpdatePromise
computeDetails
();
event
updateWith
detailsUpdatePromise
);
};
const
response
await
request
show
();
const
status
processResponse
response
);
response
complete
status
);
catch
) {
// Handle errors
Let’s see how this works in a live demo. If you are viewing this post on a device capable of Apple Pay, you should see an Apple Pay button below. Feel free to click it! Don’t worry, no matter what you do in the payment sheet, your card won’t be charged anything.
Availability
The Payment Request API is available in Safari 11.1 on macOS Sierra and macOS High Sierra, Safari on iOS 11.3, and
Safari Technology Preview
More Information
Apple provides comprehensive documentation for Apple Pay on the Web. Here are a few links you might find useful:
Apple Pay on the Web Developer Documentation
Apple Pay on the Web Human Interface Guidelines
Apple Pay on the Web Acceptable Use Guidelines
Feedback
We’d love to hear your feedback! If you find a Payment Request bug, please report it at
bugs.webkit.org
. On Twitter, you can reach the WebKit team at
@webkit
, or our web technologies evangelist Jonathan Davis at
@jonathandavis
Next
New WebKit Features in Safari 11.1
Previously
Release Notes for Safari Technology Preview 53