Skip to content

Instantly share code, notes, and snippets.

@rsolomakhin
Last active July 19, 2022 03:24
Show Gist options
  • Save rsolomakhin/eba91c185028899883d2c7f37f357d7c to your computer and use it in GitHub Desktop.
Save rsolomakhin/eba91c185028899883d2c7f37f357d7c to your computer and use it in GitHub Desktop.
Minimal UI Payment Handlers

Minimal UI Payment Handlers

Objective

Provide a golden path, low friction experience for users that are enrolled with payment handlers that support a minimal UI.

Overview

The following conditions must be satisfied to trigger the flow.

  1. The payment handler must register for minimal UI.
try {
  // Feature detection:
  if (registration.paymentManager.setMinimalUITriggerThreshold) {
    await registration.paymentManager.setMinimalUITriggerThreshold({"USD": "2.00", "JPY": "200"});
  }
} catch (e) {
  Sentry.captureException(e);  // Upload errors to the server for analysis.
}
  1. Merchant must request payment with certain properties.
    1. A single URL-based payment method.
    2. Payment handler’s currency.
    3. Value below the minimal UI trigger threshold and account balance.
    4. Only total specified.
    5. No shipping address or contact information requested.
    6. No promise for updated details passed into the show() method.
    7. show() triggered on user gesture.
try {
  const request = new PaymentRequest(
    [{supportedMethods: "https://paypal.com"}],
    {total: {label: "Payment", amount: {currency: "USD", value: "1.00"}}});
  const response = await request.show();
  await response.complete("success");
} catch (e) {
  Sentry.captureException(e);  // Upload errors to the server for analysis.
}
  1. The payment handler must handshake in the Can Make Payment event.
self.addEventListener("canmakepayment", (evt) => {
  // Feature detection:
  if (evt.respondWithMinimalUI && evt.currency) {
    return evt.respondWithMinimalUI({
      canMakePayment: true,
      readyForMinimalUI: (evt.currency === "USD" || evt.currency == "JPY")
                          && !userNeedsToReAuthenticate,
      accountBalance: "18.00",
    });
  } else {
    return evt.respondWith(true);
  }
});

The minimal UI flow then asks the user to scan their fingerprint or tap the Pay button on the screen, if the biometrics hardware is not available. The fingerprint scan is purely a confirmation step and its data is not shared with either payment handler or merchant.

User agent disables the openWindow() method in this flow, because the payment handler always responds with the payment details directly.

The account balance from the payment handler is not shared with the merchant.

Sample UI

Proposed Android minimal UI screenshot

@matdehaast
Copy link

@rsolomakhin There is an incorrect bracket in

if {registration.paymentManager.setMicrotransactionLimit)

The first bracket needs to be (

@rsolomakhin
Copy link
Author

Thank you, @matdehaast! Fixed.

@danyao
Copy link

danyao commented Feb 12, 2020

@rsolomakhin What is the difference between canMakePayment and readyForMinimalUI in respondWithMinimalUI()?

@rsolomakhin
Copy link
Author

If the user has enrolled in the minimal UI flow with the payment handler, then the payment handler should respond with canMakePayment: true, readyForMinimalUI: true. Chrome will display the minimal UI. After the user has scanned their fingerprint, Chrome will fire the "paymentrequest" event in the payment handler.

If the user has not enrolled in the minimal UI flow yet, then the payment handler should respond with canMakePayment: true: readyForMinimalUI: false. Chrome will fire the "paymentrequest" event in the payment handler, so it can open the window to enroll the user.

If payment handler does not want to handle any payments at all, then it should respond with canMakePayment: false. Chrome will reject PaymentRequest.show() with NotSupportedError.

@estark37
Copy link

Someone pointed me to the Intent to Prototype for this feature. Can you elaborate on the security story a bit? In particular I notice there is no origin on the payment handler UI.

@rsolomakhin
Copy link
Author

Absence of origin was a deliberate choice at the time, but we're up for discussion. Our reasoning was:

  1. Payment handler cannot display HTML content.
  2. Merchant must request payment directly through a single payment app.
  3. Two user gestures is required for payment: one to trigger the browser UI and another one to confirm payment.
  4. Biometric information is not passed to either merchant or payment handler.

@maxlgu
Copy link

maxlgu commented Sep 13, 2021

Chrome is deleting the Minimal UI implementation in https://chromium-review.googlesource.com/c/chromium/src/+/3150591

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment