What’s New in the Payment Request API for Apple Pay

Since announcing last April that WebKit supports the W3C Payment Request API for Apple Pay, we’ve been hard at work adding even more features to the API. From support for phonetic names in the Apple Pay payment method to new standard features like the paymentmethodchange event and PaymentResponse.retry(), WebKit’s Payment Request implementation is now more capable than ever. Anything you could’ve done in the Apple Pay JS API can now be done directly with the Payment Request API using the Apple Pay payment method.

Let’s take a closer look at some of the improvements we’ve made to Payment Request in the past year.

The paymentmethodchange event

When the user selects a card in the Apple Pay payment sheet, WebKit now dispatches the paymentmethodchange event to your PaymentRequest object. WebKit populates the event’s methodDetails attribute with an ApplePayPaymentMethod dictionary, which you can use to determine information about the card such as its type.

Listening for paymentmethodchange is optional, but if you do, be sure to call the event’s updateWith() method with a promise for updated payment details.

const request = new PaymentRequest(...);
request.onpaymentmethodchange = (event) => {
    // Compute new details based on event.methodDetails
    const detailsUpdatePromise = computeDetails(event.methodDetails);
    event.updateWith(detailsUpdatePromise);
};

Whether or not you listen for the paymentmethodchange event, WebKit continues to support payment details modifiers, a declarative way to vary payment details based on the user’s selected card. See last year’s blog post for more information about modifiers.

Requesting Phonetic Names

The Apple Pay payment method now supports requesting a phonetic spelling of your customer’s name as part of shipping and billing contact information. Here’s an Apple Pay payment method that requests a shipping contact containing a localized name, a phonetic name, and a postal address:

const applePayMethod = {
    supportedMethods: "https://apple.com/apple-pay",
    data: {
        ...,
        requiredShippingContactFields: ["name", "phoneticName", "postalAddress"],
    },
};

When your customer authorizes payment, WebKit provides the phonetic name as part of the ApplePayPayment dictionary that’s stored in the PaymentResponse‘s details attribute.

const request = new PaymentRequest([applePayMethod], ...);
request.show().then((response) => {
    const shippingContact = response.details.shippingContact;
    const phoneticFamilyName = shippingContact.phoneticFamilyName;
    const phoneticGivenName = shippingContact.phoneticGivenName;
});

Reporting Errors

When handling contact information, you might encounter an error that needs to be resolved by the customer before they can proceed with the transaction. Common examples include invalid addresses, unserviceable addresses, and missing contact information.

So that you can report these errors to your customers, WebKit now supports Payment Request’s fine-grained error reporting capabilities in the PaymentDetailsUpdate dictionary. You can specify shippingAddressErrors for errors with shipping address attributes, payerErrors for errors with names, email addresses, and phone numbers, and paymentMethodErrors for errors specific to the selected payment method. The Apple Pay payment method treats paymentMethodErrors as a sequence of ApplePayError objects.

Here’s how you might report an error for an invalid postal code:

const request = new PaymentRequest([applePayMethod], details, { requestShipping: true });
request.onshippingaddresschange = (event) => {
    // Compute new details based on shippingAddress
    const detailsUpdate = computeDetails(request.shippingAddress);

    // Check for an invalid postal code
    if (!isValid(request.shippingAddress.postalCode))
        detailsUpdate.shippingAddressErrors = { postalCode: "Invalid postal code" };

    event.updateWith(detailsUpdate);
};

When you report errors, WebKit displays them in the Apple Pay payment sheet and prompts your customer to make corrections — perhaps by editing their shipping address or selecting from a list of stored address. Here’s what the shipping address error we specified above looks like on an iPhone.

When your customer taps on the erroneous shipping address, the Apple Pay sheet displays your custom error message. If they decide to edit their address, the fields you flagged with errors (postalCode, in this case) are highlighted to help guide them through making corrections.

Once the user finishes making corrections, WebKit fires the shippingaddresschange event again. If you are satisfied with the corrections, you can proceed with the transaction by calling the updateWith() method again without specifying errors.

When handling errors in shippingaddresschange, keep in mind that WebKit redacts some parts of the shipping contact before payment authorization. The rules vary by country, but in general, WebKit reveals only the contact information necessary to calculate tax and shipping costs. Once the customer authorizes payment, WebKit reveals all the requested contact information.

Retrying Authorizations

While many errors can be detected when handling events like shippingaddresschange, the redaction rules mentioned above mean that some errors can only be detected once the user has authorized payment. For instance, you might require an email address to send a purchase confirmation, only to find after authorization that the customer did not provide a valid one.

To handle these cases, WebKit now implements PaymentResponse‘s retry() method, allowing you to ask the user to retry the payment after correcting for errors.

When you detect an error in a PaymentResponse, you can call retry() with a PaymentValidationErrors dictionary. Like in PaymentDetailsUpdate, you can specify shippingAddress errors, payer errors, and paymentMethod errors. When you call retry(), it returns a promise that resolves once the PaymentResponse is re-authorized.

Here’s how you might handle an invalid email address:

const handleResponse = (response) => {
    if (!isValid(response.payerEmail)) {
        response.retry({
            payer: { email: "Invalid email address" },
        }).then(() => { handleResponse(response) });
        return;
    }
    response.complete("success");
};

const request = new PaymentRequest([applePayMethod], ...);
request.show().then(response => handleResponse(response));

Keep in mind that retry() is for errors that can be corrected by the user. If you detect a fatal error — for example, the card was declined or an item is no longer in stock — call PaymentResponse‘s complete() method with a PaymentComplete value of 'fail'. The Apple Pay payment sheet will display an appropriate error to the user and then dismiss itself.

Availability

These new Payment Request features are available starting in Safari 12.1 on macOS, Safari on iOS 12.2, and Safari Technology Preview.

Feedback

We’ve received some great developer feedback so far, and we look forward to more of your bug reports and feature requests. If you find a Payment Request bug in WebKit, 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.