Skip to content
This repository has been archived by the owner on Mar 14, 2024. It is now read-only.

Update payment articles #9380

Merged
merged 3 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ information easier than ever before. The API can also invoke platform-specific p
apps.
{% BrowserCompat 'api.PaymentRequest' %}

<figure style="width:300px; margin:auto;">
<video controls autoplay loop muted>
<source src="https://storage.googleapis.com/web-dev-assets/payments/native-payment-app.webm" type="video/webm">
<source src="https://storage.googleapis.com/web-dev-assets/payments/native-payment-app.mp4" type="video/mp4">
</video>
<figure>
{% Video
src="video/YLflGBAPWecgtKJLqCJHSzHqe2J2/hzMuAwPAGpNmgxHpaHAq.mp4", autoplay="true", loop="true"
%}
<figcaption>Checkout flow with platform-specific Google Pay app that uses Web Payments.
</figcaption>
</figure>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,10 @@ of billing data for every purchase. See a demo below of how Google Pay leverages
Web Payments to build a seamless flow. The same can be achieved by any other
payment app:

<figure style="width:300px; margin:auto;">
<video controls autoplay loop muted>
<source src="https://storage.googleapis.com/web-dev-assets/payments/native-payment-app.webm" type="video/webm">
<source src="https://storage.googleapis.com/web-dev-assets/payments/native-payment-app.mp4" type="video/mp4">
</video>
<figure>
{% Video
src="video/YLflGBAPWecgtKJLqCJHSzHqe2J2/hzMuAwPAGpNmgxHpaHAq.mp4", autoplay="true", loop="true"
%}
<figcaption>Checkout flow with Google Pay and Web Payments.
</figcaption>
</figure>
Expand Down Expand Up @@ -220,11 +219,10 @@ In the video above, Google Pay is a platform-specific payment app.
* The web-based route is ideal for web services that have a large number of
customers with their card on file.

<figure style="width:300px; margin:auto;">
<video controls autoplay loop muted>
<source src="https://storage.googleapis.com/web-dev-assets/payments/web-based-payment-app.webm" type="video/webm">
<source src="https://storage.googleapis.com/web-dev-assets/payments/web-based-payment-app.mp4" type="video/mp4">
</video>
<figure>
{% Video
src="video/YLflGBAPWecgtKJLqCJHSzHqe2J2/kM6NP47k4Xzl4tHWISCm.mp4", autoplay="true", loop="true"
%}
<figcaption>
Checkout flow with a web-based payment app.
</figcaption>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ subhead: |
authors:
- agektmr
date: 2020-08-31
updated: 2021-09-14
updated: 2023-01-12
description: |
Once a web-based payment app is registered, it's ready to accept payment requests from merchants. This article teaches you how to orchestrate a payment transaction from a service worker during runtime.
tags:
Expand All @@ -19,8 +19,8 @@ feedback:
{% Aside 'warning' %}

Shipping and address support in [the Payment Request API is removed from the
specification](https://github.com/w3c/payment-request/pull/955) and is no longer
functional in web-based payment apps.
specification](https://github.com/w3c/payment-request/pull/955) and is planned
to be removed in web-based payment apps.
agektmr marked this conversation as resolved.
Show resolved Hide resolved

{% endAside %}

Expand All @@ -31,7 +31,7 @@ explains how a payment app can pass information about the payment method,
shipping address, or contact information to the merchant using a service worker.

<figure>
{% Img src="image/YLflGBAPWecgtKJLqCJHSzHqe2J2/4XRuSFGyEE2Cjwrmu0jb.png", alt="Handling optional payment information with a service worker", width="800", height="1344" %}
{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/kDteyNFNEVnJQyTPw5p8.png", alt="Handling optional payment information with a service worker", width="800", height="2335", class="w-screenshot" %}
<figcaption>
Handling optional payment information with a service worker
</figcaption>
Expand Down Expand Up @@ -200,13 +200,271 @@ object.
Use the object to update the UI on the frontend. See [Reflect the updated
payment details](#reflect-the-updated-payment-details).

## Inform the merchant of a shipping address change {: #shipping-address-changes }

Payment apps can provide the customer's shipping address to the merchant as part
of the result of a payment transaction.
agektmr marked this conversation as resolved.
Show resolved Hide resolved

This is useful for merchants because they can delegate the address collection to
payment apps. And, because the address data will be provided in [the standard
data format](https://w3c.github.io/payment-request/#dom-addressinit), the
merchant can expect to receive shipping addresses in consistent structure.
agektmr marked this conversation as resolved.
Show resolved Hide resolved

This is beneficial for customers as well because once they register their
address information to their favorite payment app, they can reuse it in
different shops.
agektmr marked this conversation as resolved.
Show resolved Hide resolved

<figure class="w-figure" style="width:300px; margin:auto;">
{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0ytdyaEC7tkPkBTv5rIu.png", alt="Shipping address picker UI", width="800", height="1600", class="w-screenshot" %}
<figcaption class="w-figcaption">
Shipping address picker UI
</figcaption>
</figure>

Payment apps can provide a UI to edit a shipping address or to select
pre-registered address information for the customer on a payment transaction.
When a shipping address is determined temporarily, the payment app can let the
merchant know of the redacted address information. This provides merchants with
multiple benefits:

* A merchant can determine whether the customer meets the regional restriction
to ship the item (for example, domestic only).
* A merchant can change the list of shipping options based on the region of the
shipping address (For example, international regular or express).
* A merchant can apply the new shipping cost based on the address and update the
total price.

With the Payment Handler API, the payment app can send a "shipping address
change" event to the merchant from the service worker to notify the new shipping
address. The service worker should invoke
[`PaymentRequestEvent.changeShippingAddress()`](https://w3c.github.io/payment-handler/#dom-paymentrequestevent-changeshippingaddress)
with the [new address
object](https://www.w3.org/TR/payment-request/#dom-addressinit).

<figure class="w-figure">
{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/aVv5d9OEcEjH1Z6nUQKb.png", alt="Inform the merchant of a shipping address change", width="800", height="675", class="w-screenshot" %}
<figcaption class="w-figcaption">
Inform the merchant of a shipping address change
</figcaption>
</figure>

{% Label %}
[payment handler] service-worker.js
{% endLabel %}

```js
...
// Received a message from the frontend
self.addEventListener('message', async e => {
let details;
try {
switch (e.data.type) {
case 'SHIPPING_ADDRESS_CHANGED':
const newAddress = e.data.shippingAddress;
details =
await payment_request_event.changeShippingAddress(newAddress);
```

{% Aside 'key-term' %}
**Redacted address**. Informing the full shipping address to the merchant in
this case is not necessary and risks customers' privacy. The merchant only
receives the parts of the address that they need to determine the shipping cost.
Specifically, the browser will clear the `organization`, `phone`, `recipient`,
`addressLine` fields from the payment app provided address before raising the
`shippingaddresschange` event in the merchant's DOM.
{% endAside %}

The merchant will receive a `shippingaddresschange` event from the Payment
Request API so they can respond with the updated [`PaymentDetailsUpdate`](https://w3c.github.io/payment-request/#paymentdetailsupdate-dictionary).

{% Label %}
[merchant]
{% endLabel %}

```js
request.addEventListener('shippingaddresschange', e => {
// Read the updated shipping address and update the request.
const addr = request.shippingAddress;
const details = getPaymentDetailsFromShippingAddress(addr);
// `updateWith()` sends back updated payment details
e.updateWith(details);
});
```

When the merchant responds, the promise
[`PaymentRequestEvent.changeShippingAddress()`](https://w3c.github.io/payment-handler/#dom-paymentrequestevent-changeshippingaddress)
returned will resolve with a
[`PaymentRequestDetailsUpdate`](https://w3c.github.io/payment-handler/#the-paymentrequestdetailsupdate)
object.

{% Label %}
[payment handler] service-worker.js
{% endLabel %}

```js
// Notify the merchant of the shipping address change
details = await payment_request_event.changeShippingAddress(newAddress);
// Provided the new payment details,
// send a message back to the frontend to update the UI
postMessage('UPDATE_REQUEST', details);
break;
```

Use the object to update the UI on the frontend. See [Reflect the updated
payment details](#reflect-the-updated-payment-details).

## Inform the merchant of a shipping option change

Shipping options are delivery methods merchants use to ship purchased items to a customer. Typical shipping options include:
* Free shipping
* Express shipping
* International shipping
* Premium international shipping

Each comes with its own cost. Usually faster methods/options are more expensive.

Merchants using the Payment Request API can delegate this selection to a payment
app. The payment app can use the information to construct a UI and let the
customer pick a shipping option.

<figure class="w-figure" style="width:300px; margin:auto;">
{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/VkHwTharTqX7oFK62RkA.png", alt="Shipping option picker UI", width="800", height="1600", class="w-screenshot" %}
<figcaption class="w-figcaption">
Shipping option picker UI
</figcaption>
</figure>

The list of shipping options specified in the merchant's Payment Request API is
propagated to the payment app's service worker as a property of
`PaymentRequestEvent`.

{% Label %}
[merchant]
{% endLabel %}

```js
const request = new PaymentRequest([{
supportedMethods: 'https://bobpay.xyz/pay',
data: { transactionId: '****' }
}], {
displayItems: [{
label: 'Anvil L/S Crew Neck - Grey M x1',
amount: { currency: 'USD', value: '22.15' }
}],
shippingOptions: [{
id: 'standard',
label: 'Standard',
amount: { value: '0.00', currency: 'USD' },
selected: true
}, {
id: 'express',
label: 'Express',
amount: { value: '5.00', currency: 'USD' }
}],
total: {
label: 'Total due',
amount: { currency: 'USD', value : '22.15' }
}
}, { requestShipping: true });
```

The payment app can let the merchant know which shipping option the customer
picked. This is important for both the merchant and the customer because
changing the shipping option changes the total price as well. The merchant needs
to be informed of the latest price for the payment verification later and the
customer also needs to be aware of the change.

With the Payment Handler API, the payment app can send a "shipping option
change" event to the merchant from the service worker. The service worker should
invoke
[`PaymentRequestEvent.changeShippingOption()`](https://w3c.github.io/payment-handler/#dom-paymentrequestevent-changeshippingoption)
with the new shipping option ID.

<figure class="w-figure">
{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/mERzHqvPGrjKWu29m5q1.png", alt="Inform the merchant of a shipping option change", width="800", height="667", class="w-screenshot" %}
<figcaption class="w-figcaption">
Inform the merchant of a shipping option change
</figcaption>
</figure>

{% Label %}
[payment handler] service-worker.js
{% endLabel %}

```js
// Received a message from the frontend
self.addEventListener('message', async e => {
let details;
try {
switch (e.data.type) {
case 'SHIPPING_OPTION_CHANGED':
const newOption = e.data.shippingOptionId;
details =
await payment_request_event.changeShippingOption(newOption);
```

The merchant will receive a `shippingoptionchange` event from the Payment
Request API. The merchant should use the information to update the total price
and then respond with the updated
[`PaymentDetailsUpdate`](https://w3c.github.io/payment-request/#paymentdetailsupdate-dictionary).

{% Label %}
[merchant]
{% endLabel %}

```js
request.addEventListener('shippingoptionchange', e => {
// selected shipping option
const shippingOption = request.shippingOption;
const newTotal = {
currency: 'USD',
label: 'Total due',
value: calculateNewTotal(shippingOption),
};
// `updateWith()` sends back updated payment details
e.updateWith({ total: newTotal });
});
```

When the merchant responds, the promise that
[`PaymentRequestEvent.changeShippingOption()`](https://w3c.github.io/payment-handler/#dom-paymentrequestevent-changeshippingoption)
returned will resolve with a
[`PaymentRequestDetailsUpdate`](https://w3c.github.io/payment-handler/#the-paymentrequestdetailsupdate)
object.

{% Label %}
[payment handler] service-worker.js
{% endLabel %}

```js
// Notify the merchant of the shipping option change
details = await payment_request_event.changeShippingOption(newOption);
// Provided the new payment details,
// send a message back to the frontend to update the UI
postMessage('UPDATE_REQUEST', details);
break;
```

Use the object to update the UI on the frontend. See [Reflect the updated
payment details](#reflect-the-updated-payment-details).

## Reflect the updated payment details {: #reflect-the-updated-payment-details }

Once the merchant finishes updating the payment details, the promises returned
from `.changePaymentMethod()`, will resolve with a
from `.changePaymentMethod()`, `.changeShippingAddress()` and
`.changeShippingOption()` will resolve with a common
[`PaymentRequestDetailsUpdate`](https://w3c.github.io/payment-handler/#the-paymentrequestdetailsupdate)
object. The payment handler can use the result to reflect updated total price in
the UI.
object. The payment handler can use the result to reflect updated total price
and shipping options to the UI.

{% Aside %}
The `PaymentRequestDetailsUpdate` object is passed from the merchant and it
Expand All @@ -216,11 +474,23 @@ the UI.
provide a JavaScript library to control the information, or both.
{% endAside %}

Merchants may return errors if the payment method is not acceptable. Use the
following properties to reflect the error status:
Merchants may return errors for a few reasons:

* The payment method is not acceptable.
* The shipping address is outside of their supported regions.
* The shipping address contains invalid information.
* The shipping option is not selectable for the provided shipping address or
some other reason.

Use the following properties to reflect the error status:

* **`error`**: Human readable error string. This is the best string to display
to customers.
* **`shippingAddressErrors`**:
[`AddressErrors`](https://www.w3.org/TR/payment-request/#dom-addresserrors)
object that contains in-detail error string per address property. This is
useful if you want to open a form that lets the customer edit their address
and you need to point them directly to the invalid fields.
* **`paymentMethodErrors`**: Payment-method-specific error object. You can ask
merchants to provide a structured error, but the Web Payments spec authors
recommend keeping it a simple string.
Expand Down
Loading