This technical report is non-normative.
1. Introduction
The [native-app-payment-handler] API defines an abstract functional model, the native payment app provider model, through which user agents can interact with native payment apps in order to treat them as native-app payment handlers that are accessible via the [payment-request] API.
This document describes one concrete implementation of that model, the Android payment app provider, which defines how a user agent running on the Android Operating System can interact with other applications on the same device that want to act as payment apps.
2. Android Payment App Provider
The Android Payment App Provider is a native payment app provider that defines how a user agent can access Android native payment apps. The necessary functionality is built on top of standard Android OS concepts for cross-application communication and collaboration:
An Android payment app is a native payment app representing an installed Android application capable of handling web payment requests. An Android payment app has the following properties:
-
package name: A string identifying the Android application package name.
-
pay activity name: A string identifying the exported Activity component responding to the
PAYintent. -
is-ready-to-pay service name: An optional string identifying the Service component responding to the
IS_READY_TO_PAYintent. -
update payment details service name: An optional string identifying the Service component responding to the
UPDATE_PAYMENT_DETAILSintent. -
label: A human-readable display label for the application.
-
icon: A graphical icon representing the application.
2.1. Payment app AndroidManifest.xml
In order to be visible to the user agent, the Android payment app defines the
following concepts in their AndroidManifest.xml file:
-
A mandatory
PAYAndroid activity, which will receive an intent when the user agent invokes the native payment app. -
An optional
IS_READY_TO_PAYAndroid service, which the user agent will bind to and call when determining if the app is able to handle a payment request. -
An optional
UPDATE_PAYMENT_DETAILSAndroid service, which the user agent will bind to when communicating dynamic updates with the invoked native payment app.
If an Android payment app specifies more than one activity or service that matches one of the above, the user agent behavior is undefined.
2.1.1. The PAY activity
The PAY activity will receive an intent from the user agent when the user
agent invokes the native payment app to handle a PaymentRequest. It
must be exported
and have:
-
An intent filter with an
org.chromium.intent.action.PAYaction -
An "
org.chromium.default_payment_method_name"<meta-data>tag. -
Optionally, an "
org.chromium.payment_method_names"<meta-data>tag. -
Optionally, an "
org.chromium.payment_supported_delegations"<meta-data>tag.
The "org.chromium.default_payment_method_name" tag must have an
android:value attribute containing a valid url-based payment method identifier. This will be used for determining the authoritative payment method identifier.
The "org.chromium.payment_method_names" tag, if present, must have an
android:resource attribute pointing at a <string-array> resource each of
which must be a valid url-based payment method identifier. This will be used
along with the authoritative payment method identifier for determining the
claimed payment method identifiers.
The "org.chromium.payment_supported_delegations" tag, if present, must have
an android:resource attribute pointing at a <string-array> resource listing
the supported delegations of the payment app.
The chromium.org namespace used here should be replaced by a w3.org one, but Chromium currently ships with chromium.org so it would need to be updated and a deprecation process followed, etc.
<activity
android:name=".PaymentActivity"
android:exported="true">
<intent-filter>
<action android:name="org.chromium.intent.action.PAY" />
</intent-filter>
<meta-data
android:name="org.chromium.default_payment_method_name"
android:value="https://sampleapp.example/pay" />
<meta-data
android:name="org.chromium.payment_method_names"
android:resource="@array/other_payment_method_names" />
<meta-data
android:name="org.chromium.payment_supported_delegations"
android:resource="@array/supported_delegations" />
</activity>
<resources>
<string-array name="other_payment_method_names">
<item>https://anotherapp.example/payment</item>
</string-array>
<string-array name="supported_delegations">
<item>shippingAddress</item>
<item>payerName</item>
<item>payerPhone</item>
<item>payerEmail</item>
</string-array>
</resources>
2.1.2. The IS_READY_TO_PAY service
The IS_READY_TO_PAY service receives calls from the user agent to
determine if the app is able to handle a payment request (such as
checking whether the user has an enrolled payment instrument ready to pay). If
present, it must be exported
and have an intent filter with an org.chromium.intent.action.IS_READY_TO_PAY
action.
<service
android:name=".IsReadyToPayService"
android:exported="true">
<intent-filter>
<action android:name="org.chromium.intent.action.IS_READY_TO_PAY" />
</intent-filter>
</service>
Communication with the service is defined by the following Android Interface Definition Language (AIDL) interfaces:
package org.chromium ; import org.chromium.IsReadyToPayServiceCallback ; // Implemented by the payment app. interface IsReadyToPayService { oneway void isReadyToPay ( IsReadyToPayServiceCallback callback , in Bundle parameters ); }
package org.chromium ; // Implemented by the user agent. interface IsReadyToPayServiceCallback { oneway void handleIsReadyToPay ( boolean isReadyToPay ); }
As with the intent actions and meta-data tags, the org.chromium package
namespace used by these AIDL interfaces should eventually be standardized
under a vendor-neutral namespace such as org.w3c.payments.
The user agent calls the isReadyToPay method with a callback on which it
expects to receive the result of the readiness check, along with transaction
metadata in the parameters bundle (when checking if the app is able to handle a payment request). The payment app then invokes handleIsReadyToPay
asynchronously when it is ready. This asynchronous flow ensures that an
unresponsive or slow payment application does not block or hang the user agent’s
execution threads.
2.1.3. The UPDATE_PAYMENT_DETAILS service
The UPDATE_PAYMENT_DETAILS service receives connections from the user agent
when the payment app is invoked to facilitate two-way communication for
dynamic updates (e.g. shipping address, shipping option,
or payment method changes). If present, it must be exported
and have an intent filter with an
org.chromium.intent.action.UPDATE_PAYMENT_DETAILS action.
<service
android:name=".PaymentDetailsUpdateService"
android:exported="true">
<intent-filter>
<action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS" />
</intent-filter>
</service>
Communication with the service is defined by the following Android Interface Definition Language (AIDL) interfaces:
package org.chromium.components.payments ; import android.os.Bundle ; import org.chromium.components.payments.IPaymentDetailsUpdateServiceCallback ; // Implemented by the user agent. interface IPaymentDetailsUpdateService { oneway void changePaymentMethod ( in Bundle paymentHandlerMethodData , IPaymentDetailsUpdateServiceCallback callback ); oneway void changeShippingOption ( in String shippingOptionId , IPaymentDetailsUpdateServiceCallback callback ); oneway void changeShippingAddress ( in Bundle shippingAddress , IPaymentDetailsUpdateServiceCallback callback ); }
package org.chromium.components.payments ; import android.os.Bundle ; import org.chromium.components.payments.IPaymentDetailsUpdateService ; // Implemented by the payment app. interface IPaymentDetailsUpdateServiceCallback { oneway void updateWith ( in Bundle updatedPaymentDetails ); oneway void paymentDetailsNotUpdated (); oneway void setPaymentDetailsUpdateService ( IPaymentDetailsUpdateService service ); }
As with the other intent actions and AIDL interfaces, the
org.chromium.components.payments package namespace used here should
eventually be standardized under a vendor-neutral namespace such as
org.w3c.payments.
When the user agent invokes the payment app’s
PAY activity, it concurrently binds to the app’s UPDATE_PAYMENT_DETAILS
service (which implements IPaymentDetailsUpdateServiceCallback). Upon
connecting, the user agent calls setPaymentDetailsUpdateService to hand the
payment app a Binder proxy to the user agent’s own
IPaymentDetailsUpdateService implementation.
When the user changes the payment method, shipping option, or shipping address
within the payment app UI, the payment app calls the corresponding method on
IPaymentDetailsUpdateService, passing the updated transaction parameters along
with a callback. The user agent then notifies the web merchant and, upon
receiving the merchant’s response, communicates the updated payment details back
to the payment app via updateWith or paymentDetailsNotUpdated.
2.2. User agent app AndroidManifest.xml
In order to be able to interact with Android native payment apps, the user
agent application declares
its package visibility needs for the relevant intent filters in its own
AndroidManifest.xml file, by defining a <queries> block that lists the
following actions:
-
"org.chromium.intent.action.PAY" -
"org.chromium.intent.action.IS_READY_TO_PAY" -
"org.chromium.intent.action.UPDATE_PAYMENT_DETAILS"
<queries>
<intent>
<action android:name="org.chromium.intent.action.PAY"/>
</intent>
<intent>
<action android:name="org.chromium.intent.action.IS_READY_TO_PAY"/>
</intent>
<intent>
<action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS"/>
</intent>
</queries>
2.3. Returning a list of native payment apps
In order to get the list of native payment apps, the user agent performs the following steps:
-
Let payIntent be a new Intent object targeting the
org.chromium.intent.action.PAYaction. -
Let readyToPayIntent be a new Intent object targeting the
org.chromium.intent.action.IS_READY_TO_PAYaction. -
Let updateIntent be a new Intent object targeting the
org.chromium.intent.action.UPDATE_PAYMENT_DETAILSaction. -
Query the Android OS for the list of activities matching payIntent (e.g. via PackageManager.queryIntentActivities), resulting in a list of
ResolveInfoobjects, payActivities. -
Query the Android OS for services matching readyToPayIntent (e.g. via PackageManager.queryIntentServices), and let readyToPayMap be a mapping from package name to service class name.
-
Query the Android OS for services matching updateIntent (e.g. via
PackageManager.queryIntentServices), and let updateMap be a mapping from package name to service class name. -
Let apps be an empty list.
-
For each
ResolveInfoinfo in payActivities:-
Let packageName be info’s package name (
info.activityInfo.packageName). -
Let activityName be info’s activity name (
info.activityInfo.name). -
Let label be the application label retrieved from the package manager. If label is empty, continue to the next iteration of this loop.
-
Let icon be the application icon retrieved from the package manager.
-
Let app be a new Android payment app with package name packageName, pay activity name activityName, label label, and icon icon.
-
If readyToPayMap contains an entry for packageName, set app’s is-ready-to-pay service name to that entry’s value.
-
If updateMap contains an entry for packageName, set app’s update payment details service name to that entry’s value.
-
Append app to apps.
-
-
Return apps.
2.4. Authoritative payment method identifier
The authoritative payment method identifier for an Android application, app, is
the android:value attribute of the org.chromium.default_payment_method_name
<meta-data> tag in app’s AndroidManifest.xml file.
2.5. Claimed payment method identifiers
The set of claimed payment method identifiers for an Android application, app, is created as follows:
-
Let claimedIdentifiers be a new empty set.
-
Let authoritativeIdentifier be app’s authoritative payment method identifier.
-
If authoritativeIdentifier is a valid url-based payment method identifier, add authoritativeIdentifier to claimedIdentifiers.
-
Let otherMethodIdentifiers be the string array resource referenced by the
org.chromium.payment_method_names<meta-data>tag in app’s AndroidManifest.xml file. -
If otherMethodIdentifiers is not null and is a string array:
-
For each string identifier in otherMethodIdentifiers:
-
If identifier is a valid url-based payment method identifier, add identifier to claimedIdentifiers.
-
-
-
Return claimedIdentifiers.
NOTE: The claimed payment method identifiers automatically include the
app’s authoritative payment method identifier, so payment applications do
not need to duplicate their primary method URL inside
org.chromium.payment_method_names.
2.6. Supported delegations
The set of supported delegations for an Android application, app, is
created from the org.chromium.payment_supported_delegations <meta-data> tag
in app’s PAY activity, as follows:
-
Let delegations be an empty set.
-
Let delegationNames be the string array resource referenced by the
org.chromium.payment_supported_delegations<meta-data>tag in app’sPAYactivity. -
If delegationNames is null or empty, return delegations.
-
For each string name in delegationNames:
-
If name is equal to
"shippingAddress", add"shippingAddress"to delegations. -
Else if name is equal to
"payerName", add"payerName"to delegations. -
Else if name is equal to
"payerPhone", add"payerPhone"to delegations. -
Else if name is equal to
"payerEmail", add"payerEmail"to delegations. -
Otherwise, ignore name.
-
-
Return delegations.
2.7. Checking if a native payment app is a related application for a web app manifest
In order to check if a given Android application, app, is a related application for a web app manifest, manifest, the user agent performs the following steps:
-
Let packageName be the package name of app (e.g. obtained via PackageInfo.packageName).
-
Let versionCode be the version code of app (e.g. obtained via PackageInfo.getLongVersionCode() or PackageInfo.versionCode).
-
Let appFingerprints be a new empty set of strings.
-
Let signatures be the cryptographic signing certificates for app obtained from the Android operating system (e.g. via SigningInfo.getSigningCertificateHistory() or PackageInfo.signatures).
-
If signatures is null or empty, return
false. -
For each signing certificate cert in signatures:
-
Let digest be the result of computing the SHA-256 hash of cert’s raw DER-encoded byte array.
-
Let hexString be the string produced by formatting each byte in digest as a two-digit lowercase hexadecimal number (e.g.
"%02x"without any delimiter, such as"308201dd30820146020101300d06092a864886f70d010105050030..."). -
Add hexString to appFingerprints.
-
-
For each external application resource, relatedApplication, in the related applications of manifest:
-
If relatedApplication’s platform is not equal to
"play", go to the next iteration of this loop. -
If packageName is not equal to relatedApplication’s id, go to the next iteration of this loop.
-
If relatedApplication specifies a minimum version:
-
Let minVersion be the result of parsing relatedApplication’s minimum version as an integer.
-
If versionCode is strictly less than minVersion, go to the next iteration of this loop.
-
-
Let manifestFingerprints be a new empty set of strings.
-
For each fingerprint object fp in relatedApplication’s fingerprints:
-
If fp’s
typeis equal to"sha256_cert":-
Let normalizedFingerprint be fp’s
valuewith any ASCII whitespace, colons (":"), or other delimiters removed, converted to ASCII lowercase. -
Add normalizedFingerprint to manifestFingerprints.
-
-
-
If manifestFingerprints is empty, go to the next iteration of this loop.
-
If appFingerprints is equal to manifestFingerprints (i.e. both sets contain the exact same fingerprint strings):
-
Return
true.
-
-
-
Return
false.
2.8. Determining if an Android native payment app can handle a PaymentRequest
In order to check if an Android application, app, is able to handle a payment request, request, the user agent performs the following steps:
-
If the user agent is operating in a private browsing mode, return
trueimmediately without binding to or querying theIS_READY_TO_PAYservice. -
If app’s is-ready-to-pay service name is null or not present, return
true.NOTE: The
IS_READY_TO_PAYservice is optional. If a payment app does not provide it, the user agent assumes the app is ready and delegates the payment decision to when the app is invoked via thePAYactivity. -
Let parameters be a new Android Bundle object.
-
Insert the Android package name of the user agent application into parameters with the key
"packageName". -
Let topOrigin be request’s relevant global object’s associated Document’s origin, formatted without a scheme (e.g.
"merchant.example"). Insert topOrigin into parameters with the key"topLevelOrigin". -
Let iframeOrigin be request’s relevant settings object’s origin, formatted without a scheme. Insert iframeOrigin with the key
"paymentRequestOrigin". -
If the top-level document was loaded over a secure HTTPS connection and a server certificate chain is available, let certArray be a Parcelable array containing each certificate’s raw DER-encoded byte array inside an Android Bundle with key
"certificate". Insert certArray into parameters with the key"topLevelCertificateChain". -
Let matchingMethodNames be a new empty list of strings.
-
Let methodDataBundle be a new Android Bundle object.
-
For each payment method identifier identifier supported by request:
-
If app is allowed to handle the payment method identifier identifier:
-
Append identifier to matchingMethodNames.
-
Let dataString be the JSON serialization of the method-specific data associated with identifier in request, or
"{}"if no data was supplied. -
Insert dataString into methodDataBundle with key identifier.
-
-
-
Insert matchingMethodNames (as an ArrayList of strings) into parameters with the key
"methodNames". -
Insert methodDataBundle into parameters with the key
"methodData". -
Let serviceName be app’s is-ready-to-pay service name.
-
Create a new Android Intent object targeting app’s package name, serviceName, and action
org.chromium.intent.action.IS_READY_TO_PAY. -
Bind to the service via Context.bindService using the
BIND_AUTO_CREATEflag. -
If binding fails or a user agent defined connection timeout expires before the service connects, unbind the service and return
false. -
On service connection, call
isReadyToPay(callback, parameters)on the boundIsReadyToPayServiceinterface.NOTE: Passing merchant origin and method data in the
isReadyToPayparameters bundle introduces potential tracking vectors. In private browsing modes, the user agent bypasses background queries entirely. Future iterations aim to minimize or remove identifying transaction metadata from background readiness checks. -
Wait asynchronously for the
handleIsReadyToPay(isReadyToPay)method to be invoked on the callback, subject to a user agent defined query timeout.-
If
handleIsReadyToPayis called with boolean isReadyToPay before the timeout, unbind the service and return isReadyToPay. -
If the query times out, the service disconnects, or an exception occurs, unbind the service and return
false.
-
2.9. Invoking an Android native payment app
In order to invoke the native payment app, app, for a PaymentRequest
request, the user agent performs the following steps:
-
Let packageName be app’s package name.
-
Let activityName be app’s pay activity name.
-
Let extras be the result of creating an Android bundle from request for app.
-
Create an Intent object, intent, whose:
-
Class name is set to the combination of packageName and activityName, e.g. via Intent.setClassName.
-
Action is set to
org.chromium.intent.action.PAY. -
Extras are set to extras, e.g. via Intent.putExtras.
-
-
If app declares an
UPDATE_PAYMENT_DETAILSservice in itsAndroidManifest.xml, establish the update service connection concurrently. -
Send intent to the Android OS in such a way that the user agent will be notified on result, e.g. via Activity.startActivityForResult.
2.10. Communicating with an invoked Android native payment app
An invoked native payment app, app, can communicate dynamic transaction
changes (such as shipping address, shipping option, or payment method changes)
back to the web merchant via the UPDATE_PAYMENT_DETAILS service (see
§ 2.1.3 The UPDATE_PAYMENT_DETAILS service).
2.10.1. Establishing the update service connection
The user agent performs the following steps to establish the update service connection for an invoked native payment app app:
-
Create a new Android Intent object targeting app’s package name, app’s
UPDATE_PAYMENT_DETAILSservice class name, and actionorg.chromium.intent.action.UPDATE_PAYMENT_DETAILS. -
Attempt to bind to the service via Context.bindService using the
BIND_AUTO_CREATEflag.-
If binding fails, unbind the service and abort these steps.
NOTE: The payment flow continues normally; the payment app will proceed without dynamic update capabilities.
-
-
When the service connection is established:
-
Obtain the
IPaymentDetailsUpdateServiceCallbackinterface from the returned IBinder. -
Call
setPaymentDetailsUpdateServiceon that interface, passing the user agent’sIPaymentDetailsUpdateServiceBinder instance.
-
-
Maintain this service connection active for the duration of the payment interaction. When app’s
PAYactivity finishes or is dismissed, unbind and terminate the service connection.-
If the connection is lost or dies unexpectedly while the payment interaction is still active, the user agent may attempt to reconnect. If the connection cannot be restored, the user agent unbinds the service and continues the payment flow without dynamic update capabilities.
-
2.10.2. Authorizing service requests
To authorize a dynamic update service request from an application app:
-
Let callerUid be the calling UID obtained via Binder.getCallingUid().
-
Query the Android OS for the package names and cryptographic signing certificates associated with callerUid.
-
If none of the packages matching callerUid have a package name equal to app’s package name, or if their signing certificates do not match app’s signing certificates, return
false. -
Return
true.
2.10.3. Handling payment method change
When changePaymentMethod is called on IPaymentDetailsUpdateService by
app, with paymentHandlerMethodData and callback:
-
If the user agent is unable to authorize a dynamic update service request from app, return.
-
If
paymentHandlerMethodDatais null, or if the string extra"methodName"inpaymentHandlerMethodDatais null or empty:-
Call
callback.updateWithwith a Bundle containing an error message, and return.
-
-
Let methodName be the string extra
"methodName"inpaymentHandlerMethodData. -
Let stringifiedDetails be the string extra
"details"inpaymentHandlerMethodData, or"{}"if absent. -
Let methodDetails be the result of parsing stringifiedDetails as a JSON object. If parsing throws an error, let methodDetails be null.
-
Inform the user agent that the user has changed their payment method with app, methodName, and methodDetails.
2.10.4. Handling shipping option change
When changeShippingOption is called on IPaymentDetailsUpdateService by
app with shippingOptionId and callback:
-
If the user agent is unable to authorize a dynamic update service request from app, return.
-
If
shippingOptionIdis null or empty:-
Call
callback.updateWithwith a Bundle containing an error message, and return.
-
-
Inform the user agent that the user has changed their shipping option with app and
shippingOptionId.
2.10.5. Handling shipping address change
When changeShippingAddress is called on IPaymentDetailsUpdateService by
app with shippingAddressBundle and callback:
-
If the user agent is unable to authorize a dynamic update service request from app, return.
-
If
shippingAddressBundleis null or empty:-
Call
callback.updateWithwith a Bundle containing an error message, and return.
-
-
Let shippingAddress be the result of parsing a shipping address Android bundle from
shippingAddressBundle. -
If shippingAddress is null:
-
Call
callback.updateWithwith a Bundle containing an error message, and return.
-
-
Inform the user agent that the user has changed their shipping address with app and shippingAddress.
2.10.6. Updating payment details
In order to update payment details in an invoked Android payment app,
app, given a PaymentRequestDetailsUpdate dictionary, update:
-
Let bundle be the result of creating an Android bundle from a PaymentRequestDetailsUpdate for update.
-
Call
callback.updateWith(bundle)on the activeIPaymentDetailsUpdateServiceCallback.
2.10.7. Notifying payment details not updated
In order to notify payment details not updated for app:
-
Call
callback.paymentDetailsNotUpdated()on the activeIPaymentDetailsUpdateServiceCallback.
2.10.8. Parsing a shipping address Android bundle
To parse a shipping address Android bundle from an Android Bundle, bundle:
-
Let country be the string extra
"countryCode"in bundle. -
If country is null, empty, or not a valid ISO 3166-1 alpha-2 country code, return null.
-
Let address be a new
ContactAddressobject with the following fields:-
countryset to country. -
addressLineset to the string array extra"addressLines"in bundle, or an empty list if absent. -
regionset to the string extra"region"in bundle, or""if absent. -
cityset to the string extra"city"in bundle, or""if absent. -
dependentLocalityset to the string extra"dependentLocality"in bundle, or""if absent. -
postalCodeset to the string extra"postalCode"in bundle, or""if absent. -
sortingCodeset to the string extra"sortingCode"in bundle, or""if absent. -
organizationset to the string extra"organization"in bundle, or""if absent. -
recipientset to the string extra"recipient"in bundle, or""if absent. -
phoneset to the string extra"phone"in bundle, or""if absent.
-
-
Return address.
2.10.9. Creating an Android bundle from a PaymentRequestDetailsUpdate
To create an Android bundle from a PaymentRequestDetailsUpdate from
a PaymentRequestDetailsUpdate, update:
-
Let bundle be a new Android Bundle object.
-
If update["total"] exists:
-
Let totalBundle be a new Android Bundle object containing:
-
"currency"set to update["total"]["currency"] -
"value"set to update["total"]["value"]
-
-
Insert totalBundle into bundle with key
"total".
-
-
If update["shippingOptions"] exists and is not empty:
-
Let optionsArray be an empty Parcelable array.
-
For each
PaymentShippingOptionoption in update["shippingOptions"]:-
Let optBundle be a new Android Bundle object containing:
-
"id"set to option["id"] -
"label"set to option["label"] -
"amount"set to a Bundle containing"currency"and"value"from option["amount"] -
"selected"set to option["selected"]
-
-
Append optBundle to optionsArray.
-
-
Insert optionsArray into bundle with key
"shippingOptions".
-
-
If update["error"] exists and is not empty:
-
Insert update["error"] into bundle with key
"error".
-
-
If update["stringifiedPaymentMethodErrors"] exists and is not empty:
-
Insert update["stringifiedPaymentMethodErrors"] into bundle with key
"stringifiedPaymentMethodErrors".
-
-
If update["shippingAddressErrors"] exists and is not null:
-
Let addrErrorsBundle be a new Android Bundle object.
-
For each key in update["shippingAddressErrors"] (such as
"country","addressLine","city","postalCode", etc.), insert the string error into addrErrorsBundle under that key. -
Insert addrErrorsBundle into bundle with key
"addressErrors".
-
-
Return bundle.
2.11. Indicating success of an invoked Android native payment app
In order for an invoked Android application, app, to indicate success back to the user agent, app performs the following steps:
-
Create a new Android Intent object, intent.
-
Insert the string identifier of the payment method selected by the user into intent with the key
"methodName". -
Insert a JSON-stringified object containing the payment-method-specific transaction details into intent with the key
"details". -
If app supports and was requested to provide payer information:
-
If payer name collection was delegated, insert the payer’s name string into intent with the key
"payerName". -
If payer email collection was delegated, insert the payer’s email string into intent with the key
"payerEmail". -
If payer phone collection was delegated, insert the payer’s phone string into intent with the key
"payerPhone".
-
-
If app supported and was requested to provide shipping information:
-
Insert the identifier of the selected shipping option into intent with the key
"shippingOptionId". -
Let addressBundle be an Android Bundle representing the selected shipping address, populated with the address keys defined in parse a shipping address Android bundle. Insert addressBundle into intent with the key
"shippingAddress".
-
-
Call Activity.setResult with Activity.RESULT_OK and intent.
-
Terminate app’s activity, e.g. via Activity.finish().
To retrieve the converted result details for an invoked Android payment app, app:
-
Let resultIntent be the result Intent returned when app indicated success.
-
Let paymentOptions be the requested
PaymentOptionspassed when app was invoked. -
If resultIntent is null or does not contain extras, return failure.
-
Let extras be the Bundle of extras from resultIntent.
-
Let details be the string extra
"details"in extras. -
If details is null or empty, return failure.
-
Let methodName be the string extra
"methodName"in extras. -
If methodName is null or empty, return failure.
-
Let response be a new
NativePaymentHandlerResponsedictionary with:-
response["methodName"] set to methodName
-
response["details"] set to details
-
-
If paymentOptions was supplied:
-
If paymentOptions["requestShipping"] is true:
-
Let addressBundle be the Bundle extra
"shippingAddress"in extras. -
If addressBundle is null or empty, return failure.
-
Let shippingAddress be the result of parsing a shipping address Android bundle from addressBundle.
-
If shippingAddress is null, return failure.
-
Set response["shippingAddress"] to shippingAddress.
-
Let shippingOptionId be the string extra
"shippingOptionId"in extras. -
If shippingOptionId is null or empty, return failure.
-
Set response["shippingOption"] to shippingOptionId.
-
-
If paymentOptions["requestPayerName"] is true:
-
Let payerName be the string extra
"payerName"in extras. -
If payerName is null or empty, return failure.
-
Set response["payerName"] to payerName.
-
-
If paymentOptions["requestPayerEmail"] is true:
-
Let payerEmail be the string extra
"payerEmail"in extras. -
If payerEmail is null or empty, return failure.
-
Set response["payerEmail"] to payerEmail.
-
-
If paymentOptions["requestPayerPhone"] is true:
-
Let payerPhone be the string extra
"payerPhone"in extras. -
If payerPhone is null or empty, return failure.
-
Set response["payerPhone"] to payerPhone.
-
-
-
Return response.
2.12. Indicating user cancel from an invoked Android native payment app
In order for an invoked Android application, app, to indicate user cancellation back to the user agent, app performs the following steps:
-
Call Activity.setResult with Activity.RESULT_CANCEL.
-
Terminate app’s activity, e.g. via Activity.finish().
2.13. Indicating internal error from an invoked Android native payment app
In order for an invoked Android application, app, to indicate internal error back to the user agent, app performs the following steps:
-
Let RESULT_INTERNAL_ERROR be equal to Activity.RESULT_FIRST_USER (integer value
1). -
Call Activity.setResult with RESULT_INTERNAL_ERROR.
-
Terminate app’s activity, e.g. via Activity.finish().
2.14. Creating an Android bundle
To create an Android bundle from a PaymentRequest, request, for
an application app:
-
Let bundle be a new Android Bundle object.
-
Insert request.
idinto bundle with the key"paymentRequestId". -
Insert the
titleof request’s relevant global object’s associated Document into bundle with the key"merchantName". -
Let totalItem be request.
[[details]].total. -
Let serializedTotal be a JSON-serialized object containing
"currency"set to totalItem.amount.currencyand"value"set to totalItem.amount.value. Insert serializedTotal into bundle with the key"total". -
If request.
[[details]].modifiersexists and is not empty:-
Let modifiersList be an empty JSON array.
-
For each
PaymentDetailsModifiermodifier in request.[[details]].modifiers:-
Let modObj be a new JSON object.
-
If modifier.
totalexists, set modObj["total"] to a JSON object with"currency"and"value"from modifier.total.amount. -
Set modObj["supportedMethods"] to a JSON array containing the single string modifier.
supportedMethods. -
Set modObj["data"] to the JSON-serialized string of modifier.
dataif present, or"{}"otherwise. -
Append modObj to modifiersList.
-
-
Insert the JSON-stringified serialization of modifiersList into bundle with the key
"modifiers".
-
-
Let options be request.
[[options]]. -
If options exists:
-
Let optionsBundle be a new Android Bundle object containing:
-
"requestPayerName"set to options.requestPayerName -
"requestPayerEmail"set to options.requestPayerEmail -
"requestPayerPhone"set to options.requestPayerPhone -
"requestShipping"set to options.requestShipping
-
-
If options.
shippingTypeexists and is not null:-
Insert options.
shippingTypeinto optionsBundle with the key"shippingType".
-
-
Insert optionsBundle into bundle with the key
"paymentOptions".
-
-
If options exists and options.
requestShippingis true:-
If request.
[[details]].shippingOptionsexists and is not empty:-
Let optionsArray be an empty Parcelable array.
-
For each
PaymentShippingOptionoption in request.[[details]].shippingOptions: -
Insert optionsArray into bundle with the key
"shippingOptions".
-
-
-
Let topOrigin be request’s relevant global object’s associated Document’s origin, formatted without a scheme (e.g.
"merchant.example"). Insert topOrigin into bundle with the key"topLevelOrigin". -
Let iframeOrigin be request’s relevant settings object’s origin, formatted without a scheme. Insert iframeOrigin into bundle with the key
"paymentRequestOrigin". -
If the top-level document was loaded over a secure HTTPS connection and a server certificate chain is available, let certArray be a Parcelable array containing each certificate’s raw DER-encoded byte array inside an Android Bundle with key
"certificate". Insert certArray into bundle with the key"topLevelCertificateChain". -
Let matchingMethodNames be a new empty list of strings.
-
Let methodDataBundle be a new Android Bundle object.
-
For each payment method identifier identifier supported by request:
-
If app is allowed to handle the payment method identifier identifier:
-
Append identifier to matchingMethodNames.
-
Let dataString be the JSON serialization of the method-specific data associated with identifier in request, or
"{}"if no data was supplied. -
Insert dataString into methodDataBundle with key identifier.
-
-
-
Insert matchingMethodNames (as an ArrayList of strings) into bundle with the key
"methodNames". -
Insert methodDataBundle into bundle with the key
"methodData". -
Return bundle.