blob: 85e89de202bbd5609c355b3e7770312e0025c5d7 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview JavaScript implementation of the Payment Request API. When
* loaded, installs the API onto the window object. Conforms
* to https://w3c.github.io/payment-request/. Note: This is a work in progress.
*/
/**
* This class implements the DOM level 2 EventTarget interface. The
* Implementation is copied form src/ui/webui/resources/js/cr/event_target.js.
* This code should be removed once there is a plan to move event_target.js out
* of WebUI and reuse in iOS.
* @constructor
* @implements {EventTarget}
*/
__gCrWeb.EventTarget =
function() {
this.listeners_ = null;
}
// Store EventTarget namespace object in a global __gCrWeb object referenced
// by a string, so it does not get renamed by closure compiler during the
// minification.
__gCrWeb['EventTarget'] = __gCrWeb.EventTarget;
/* Beginning of anonymous object. */
(function() {
__gCrWeb.EventTarget.prototype = {
/** @override */
addEventListener: function(type, handler) {
if (!this.listeners_)
this.listeners_ = Object.create(null);
if (!(type in this.listeners_)) {
this.listeners_[type] = [handler];
} else {
var handlers = this.listeners_[type];
if (handlers.indexOf(handler) < 0)
handlers.push(handler);
}
},
/** @override */
removeEventListener: function(type, handler) {
if (!this.listeners_)
return;
if (type in this.listeners_) {
var handlers = this.listeners_[type];
var index = handlers.indexOf(handler);
if (index >= 0) {
// Clean up if this was the last listener.
if (handlers.length == 1)
delete this.listeners_[type];
else
handlers.splice(index, 1);
}
}
},
/** @override */
dispatchEvent: function(event) {
var type = event.type;
var prevented = 0;
if (!this.listeners_)
return true;
// Override the DOM Event's 'target' property.
Object.defineProperty(event, 'target', {value: this});
if (type in this.listeners_) {
// Clone to prevent removal during dispatch
var handlers = this.listeners_[type].concat();
for (var i = 0, handler; handler = handlers[i]; i++) {
if (handler.handleEvent)
prevented |= handler.handleEvent.call(handler, event) === false;
else
prevented |= handler.call(this, event) === false;
}
}
return !prevented && !event.defaultPrevented;
}
};
}()); // End of anonymous object
/**
* PromiseResolver is a helper class that allows creating a Promise that will be
* fulfilled (resolved or rejected) some time later. The implementation is
* copied from src/ui/webui/resources/js/promise_resolver.js. This code should
* be removed once there is a plan to move promise_resolver.js out of WebUI and
* reuse in iOS.
* @constructor
* @template T
*/
__gCrWeb.PromiseResolver =
function() {
/** @private {function(T=): void} */
this.resolve_;
/** @private {function(*=): void} */
this.reject_;
/** @private {!Promise<T>} */
this.promise_ = new Promise(function(resolve, reject) {
this.resolve_ = resolve;
this.reject_ = reject;
}.bind(this));
}
// Store PromiseResolver namespace object in a global __gCrWeb object referenced
// by a string, so it does not get renamed by closure compiler during the
// minification.
__gCrWeb['PromiseResolver'] = __gCrWeb.PromiseResolver;
/* Beginning of anonymous object. */
(function() {
__gCrWeb.PromiseResolver.prototype = {
/** @return {!Promise<T>} */
get promise() { return this.promise_; },
/** @return {function(T=): void} */
get resolve() { return this.resolve_; },
/** @return {function(*=): void} */
get reject() { return this.reject_; },
};
}()); // End of anonymous object
/**
* A simple object representation of |PaymentRequest| meant for
* communication to the app side.
* @typedef {{
* id: string,
* methodData: !Array<!window.PaymentMethodData>,
* details: !window.PaymentDetails,
* options: (window.PaymentOptions|undefined)
* }}
*/
var SerializedPaymentRequest;
/**
* A simple object representation of |PaymentResponse| meant for
* communication to the app side.
* @typedef {{
* paymentRequestId: string,
* methodName: string,
* details: Object,
* shippingAddress: (!window.PaymentAddress|undefined),
* shippingOption: (string|undefined),
* payerName: (string|undefined),
* payerEmail: (string|undefined),
* payerPhone: (string|undefined)
* }}
*/
var SerializedPaymentResponse;
/* Beginning of anonymous object. */
(function() {
// Namespace for all PaymentRequest implementations. __gCrWeb must have
// already
// been defined.
__gCrWeb.paymentRequestManager = {};
/** @const {number} */
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH = 1024;
/** @const {number} */
__gCrWeb['paymentRequestManager'].MAX_JSON_STRING_LENGTH = 1048576;
/** @const {number} */
__gCrWeb['paymentRequestManager'].MAX_LIST_SIZE = 1024;
/** @const {number} */
__gCrWeb['paymentRequestManager'].SHOW_PROMISE_TIMEOUT_MS =
10000; // 10 seconds.
/**
* Returns a Promise that either resolves with the result of the passed in
* Promise or rejects after the given duration.
* @param {number} duration Duration after which the returned promise rejects.
* @param {!Promise} promise Promise that must settle in the given duration.
* @return {!Promise}
*/
__gCrWeb['paymentRequestManager'].settleOrTimeout = function(
duration, promise) {
// Create a Promise that rejects in |duration| milliseconds.
var timeout = new Promise(function(resolve, reject) {
window.setTimeout(function() {
reject('Timed out after ' + duration + 'milliseconds.')
}, duration);
});
// Return a race between the timeout Promise and the passed in Promise.
return Promise.race([promise, timeout]);
};
/**
* Validates a PaymentCurrencyAmount which is used to supply monetary amounts.
* https://w3c.github.io/payment-request/#dfn-valid-decimal-monetary-value
* @param {!window.PaymentCurrencyAmount} amount
* @param {string} amountName The name of |amount| to use in the error.
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validatePaymentCurrencyAmount = function(
amount, amountName) {
// Convert the value to String if it isn't already one.
amount.value = String(amount.value);
if (amount.value.length >
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
throw new TypeError(
amountName + ' value cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH + ' characters');
}
if (!/^-?[0-9]+(\.[0-9]+)?$/.test(amount.value)) {
throw new TypeError(
amountName + ' value is not a valid decimal monetary value');
}
if (typeof amount.currency !== 'string')
throw new TypeError(amountName + ' currency must be a string');
if (amount.currency.length >
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
throw new TypeError(
amountName + ' currency cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH + ' characters');
}
if (!/^[a-zA-Z]{3}$/.test(amount.currency)) {
throw new RangeError(
amountName + ' currency is not a valid ISO 4217 currency code');
}
};
/**
* Validates a window.PaymentItem which indicates a monetary amount along with
* a human-readable description of the amount.
* @param {!window.PaymentItem} paymentItem
* @param {string} paymentItemName The name of |paymentItem| to use in the
* error.
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validatePaymentItem = function(
paymentItem, paymentItemName) {
if (typeof paymentItem.label !== 'string')
throw new TypeError(paymentItemName + ' label must be a string');
if (paymentItem.label.length >
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
throw new TypeError(
paymentItemName + ' label cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH + ' characters');
}
if (!paymentItem.amount)
throw new TypeError(paymentItemName + ' amount is missing');
__gCrWeb['paymentRequestManager'].validatePaymentCurrencyAmount(
paymentItem.amount, paymentItemName + ' amount');
};
/**
* Validates a Payment method identifier according to:
* https://w3c.github.io/payment-method-id/
* @param {string} identifier Payment method identifier to validate.
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validatePaymentMethodIdentifier = function(
identifier) {
if (typeof identifier !== 'string')
throw new TypeError('A payment method identifier must be a string');
if (identifier.length >
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
throw new TypeError(
'A payment method identifier cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH + ' characters');
}
try {
var url = new URL(identifier);
var invalidIdentifier =
(url.protocol != 'https:' || url.username || url.password);
} catch (error) {
invalidIdentifier =
!/^[a-z]+[0-9a-z]*(-[a-z]+[0-9a-z]*)*$/.test(identifier);
} finally {
if (invalidIdentifier) {
throw new RangeError(
'A payment method identifier must either be valid URL with a ' +
'https scheme and empty username and password or a lower-case ' +
'alphanumeric string with optional hyphens');
}
}
};
/**
* Validates the supportedMethods property and its associated data property in
* a window.PaymentMethodData or window.PaymentDetailsModifier.
* @param {(!Array<string>|string)} supportedMethods
* @param {(window.BasicCardData|Object)} data
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validateSupportedMethods = function(
supportedMethods, data) {
// The |supportedMethods| was changed from array to string, but the name was
// left as a plural to maintain compatibility with existing content on the
// Web. So, if it is an array type, we only supports when its length is 1
// for backward-compatibility.
if (supportedMethods instanceof Array && supportedMethods.length == 1) {
supportedMethods = supportedMethods.toString();
}
if (typeof supportedMethods !== 'string') {
throw new TypeError('A payment method identifier must be a string');
} else {
__gCrWeb['paymentRequestManager'].validatePaymentMethodIdentifier(
supportedMethods);
}
if (typeof data === 'undefined')
return;
if (!(data instanceof Object)) {
throw new TypeError('Payment method data must be of type \'Object\'');
}
if (JSON.stringify(data).length >
__gCrWeb['paymentRequestManager'].MAX_JSON_STRING_LENGTH) {
throw new TypeError(
'JSON serialization of payment method data should be no longer ' +
'than ' + __gCrWeb['paymentRequestManager'].MAX_JSON_STRING_LENGTH +
' characters');
}
// Validate basic-card data.
if (supportedMethods == 'basic-card') {
// Validate basic-card supportedNetworks.
if (data.supportedNetworks) {
if (!(data.supportedNetworks instanceof Array)) {
throw new TypeError('basic-card supportedNetworks must be an array');
}
if (data.supportedNetworks.length >
__gCrWeb['paymentRequestManager'].MAX_LIST_SIZE) {
throw new TypeError(
'basic-card supportedNetworks cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_LIST_SIZE + ' elements');
}
for (var i = 0; i < data.supportedNetworks.length; i++) {
if (typeof data.supportedNetworks[i] !== 'string') {
throw new TypeError(
'A basic-card supported network must be a string');
}
if (data.supportedNetworks[i].length >
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
throw new TypeError(
'A basic-card supported network cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH +
' characters');
}
}
}
// Validate basic-card supportedTypes.
if (data.supportedTypes) {
if (!(data.supportedTypes instanceof Array)) {
throw new TypeError('basic-card supportedTypes must be an array');
}
if (data.supportedTypes.length >
__gCrWeb['paymentRequestManager'].MAX_LIST_SIZE) {
throw new TypeError(
'basic-card supportedTypes cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_LIST_SIZE + ' elements');
}
for (var i = 0; i < data.supportedTypes.length; i++) {
if (typeof data.supportedTypes[i] !== 'string') {
throw new TypeError('A basic-card supported type must be a string');
}
if (data.supportedTypes[i].length >
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
throw new TypeError(
'A basic-card supported type cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH +
' characters');
}
}
}
}
};
/**
* Validates a list of window.PaymentDetailsModifier instances.
* @param {!Array<!window.PaymentDetailsModifier>} modifiers
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validatePaymentDetailsModifiers = function(
modifiers) {
if (!(modifiers instanceof Array))
throw new TypeError('Modifiers must be an array');
if (modifiers.length > __gCrWeb['paymentRequestManager'].MAX_LIST_SIZE) {
throw new TypeError(
'At most ' + __gCrWeb['paymentRequestManager'].MAX_LIST_SIZE +
' modifiers allowed');
}
for (var i = 0; i < modifiers.length; i++) {
if (!modifiers[i].supportedMethods) {
throw new TypeError(
'Must specify at least one payment method identifier');
}
// Validate PaymentDetailsModifier.supportedMethods and
// PaymentDetailsModifier.data.
__gCrWeb['paymentRequestManager'].validateSupportedMethods(
modifiers[i].supportedMethods, modifiers[i].data);
if (typeof modifiers[i].total !== 'undefined') {
__gCrWeb['paymentRequestManager'].validatePaymentItem(
modifiers[i].total, 'Modifier total');
if (String(modifiers[i].total.amount.value)[0] == '-')
throw new TypeError('Modifier total value should be non-negative');
}
if (typeof modifiers[i].additionalDisplayItems === 'undefined')
continue;
if (!(modifiers[i].additionalDisplayItems instanceof Array)) {
throw new TypeError('additionalDisplayItems must be an array');
}
if (modifiers[i].additionalDisplayItems.length >
__gCrWeb['paymentRequestManager'].MAX_LIST_SIZE) {
throw new TypeError(
'At most ' + __gCrWeb['paymentRequestManager'].MAX_LIST_SIZE +
' additionalDisplayItems allowed');
}
for (var j = 0; j < modifiers[i].additionalDisplayItems.length; j++) {
__gCrWeb['paymentRequestManager'].validatePaymentItem(
modifiers[i].additionalDisplayItems[j],
'Additional display items in modifier');
}
}
};
/**
* Validates the shipping options passed to the PaymentRequest constructor and
* returns the validate values.
* @param {(Array<!window.PaymentShippingOption>|undefined)} shippingOptions
* @param {window.PaymentOptions=} opt_options payment request options.
* @return {!Array<!window.PaymentShippingOption>} Validated shipping options.
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validateShippingOptions = function(
shippingOptions, opt_options) {
if (!shippingOptions || !(shippingOptions instanceof Array)) {
return [];
}
if (shippingOptions.length >
__gCrWeb['paymentRequestManager'].MAX_LIST_SIZE) {
throw new TypeError(
'At most ' + __gCrWeb['paymentRequestManager'].MAX_LIST_SIZE +
' shipping options allowed');
}
var uniqueIDS = {};
for (var i = 0; i < shippingOptions.length; i++) {
// Validate window.PaymentShippingOption.
if (typeof shippingOptions[i].id !== 'string') {
throw new TypeError('Shipping option ID must be a string');
}
if (shippingOptions[i].id.length >
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
throw new TypeError(
'Shipping option ID cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH +
' characters');
}
if (opt_options && opt_options.requestShipping &&
uniqueIDS[shippingOptions[i].id]) {
throw new TypeError('Shipping option IDs mus tbe unique');
}
uniqueIDS[shippingOptions[i].id] = true;
if (typeof shippingOptions[i].label !== 'string') {
throw new TypeError('Shipping option label must be a string');
}
if (!shippingOptions[i].amount)
throw new TypeError('Shipping option amount is missing');
__gCrWeb['paymentRequestManager'].validatePaymentCurrencyAmount(
shippingOptions[i].amount, 'Shipping option amount');
}
return shippingOptions;
};
/**
* Validates an instance of PaymentDetailsBase and returns the validated
* shipping options.
* @param {!window.PaymentDetails} details
* @param {window.PaymentOptions=} opt_options payment request options.
* @return {!Array<!window.PaymentShippingOption>} The validated shipping
* options.
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validatePaymentDetailsBase = function(
details, opt_options) {
// Validate the details.displayItems.
if (typeof details.displayItems !== 'undefined') {
if (!(details.displayItems instanceof Array))
throw new TypeError('display items must be an array');
if (details.displayItems.length >
__gCrWeb['paymentRequestManager'].MAX_LIST_SIZE) {
throw new TypeError(
'At most ' + __gCrWeb['paymentRequestManager'].MAX_LIST_SIZE +
'display items allowed');
}
for (var i = 0; i < details.displayItems.length; i++) {
__gCrWeb['paymentRequestManager'].validatePaymentItem(
details.displayItems[i], 'display items');
}
}
// Validate the details.modifiers.
if (typeof details.modifiers !== 'undefined') {
__gCrWeb['paymentRequestManager'].validatePaymentDetailsModifiers(
details.modifiers);
}
// Validate the details.shippingOptions.
return __gCrWeb['paymentRequestManager'].validateShippingOptions(
details.shippingOptions, opt_options);
};
/**
* Validates an instance of PaymentDetailsInit and returns the validated
* shipping options.
* @param {!window.PaymentDetails} details
* @param {window.PaymentOptions=} opt_options payment request options.
* @return {!Array<!window.PaymentShippingOption>} The validated shipping
* options.
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validatePaymentDetailsInit = function(
details, opt_options) {
// Validate details.total.
if (!details.total)
throw new TypeError('Total is missing.');
__gCrWeb['paymentRequestManager'].validatePaymentItem(
details.total, 'Total');
if (String(details.total.amount.value)[0] == '-')
throw new TypeError('Total value should be non-negative');
return __gCrWeb['paymentRequestManager'].validatePaymentDetailsBase(
details, opt_options);
};
/**
* Validates an instance of PaymentDetailsUpdate and returns the validated
* shipping options.
* @param {!window.PaymentDetails} details
* @return {!Array<!window.PaymentShippingOption>} The validated shipping
* options.
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validatePaymentDetailsUpdate = function(
details) {
// Validate details.total.
if (details.total) {
__gCrWeb['paymentRequestManager'].validatePaymentItem(
details.total, 'Total');
if (String(details.total.amount.value)[0] == '-')
throw new TypeError('Total value should be non-negative');
}
// Validate details.error.
if (details.errorMessage) {
if (typeof details.error !== 'string') {
throw new TypeError('Error message must be a string');
}
if (details.error.length >
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH) {
throw new TypeError(
'Error message cannot be longer than ' +
__gCrWeb['paymentRequestManager'].MAX_STRING_LENGTH +
' characters');
}
}
return __gCrWeb['paymentRequestManager'].validatePaymentDetailsBase(
details);
};
/**
* Validates the array of PaymentMethodData instances passed to the
* PaymentRequest constructor.
* @param {!Array<!window.PaymentMethodData>} methodData
* @throws {TypeError|RangeError}
*/
__gCrWeb['paymentRequestManager'].validatePaymentMethodData = function(
methodData) {
if (!(methodData instanceof Array))
throw new TypeError('methodData must be an array');
if (methodData.length == 0) {
throw new TypeError('Needs to include at least one payment method');
}
if (methodData.length > __gCrWeb['paymentRequestManager'].MAX_LIST_SIZE) {
throw new TypeError(
'At most ' + __gCrWeb['paymentRequestManager'].MAX_LIST_SIZE +
' payment methods are supported');
}
for (var i = 0; i < methodData.length; i++) {
if (!methodData[i].supportedMethods) {
throw new TypeError(
'Each payment method needs to include at least one payment ' +
'method identifier');
}
// Validate PaymentMethodData.supportedMethods and PaymentMethodData.data.
__gCrWeb['paymentRequestManager'].validateSupportedMethods(
methodData[i].supportedMethods, methodData[i].data);
}
};
/**
* Generates a random string identfier resembling a GUID.
* @return {string}
*/
__gCrWeb['paymentRequestManager'].guid = function() {
/**
* Generates a stringified random 4 digit hexadecimal number.
* @return {string}
*/
var s4 = function() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
};
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() +
s4() + s4();
};
// Store paymentRequestManager namespace object in a global __gCrWeb object
// referenced by a string, so it does not get renamed by closure compiler
// during
// the minification.
__gCrWeb['paymentRequestManager'] = __gCrWeb.paymentRequestManager;
/**
* Wether the origin is secure. The default is true unless it is set to false.
* If false, PaymentRequest constructor throws a SecurityError DOMException.
* @type {boolean}
*/
__gCrWeb['paymentRequestManager'].isContextSecure = true;
/**
* The PaymentRequest object, if any. This object is provided by the page and
* only updated by the app side.
* @type {PaymentRequest}
*/
__gCrWeb['paymentRequestManager'].pendingRequest = null;
/**
* The PromiseResolver object used to resolve or reject the promise returned
* by PaymentRequest.prototype.show, if any.
* @type {__gCrWeb.PromiseResolver}
*/
__gCrWeb['paymentRequestManager'].requestPromiseResolver = null;
/**
* The PromiseResolver object used to resolve or reject the promise returned
* by PaymentRequest.prototype.abort, if any.
* @type {__gCrWeb.PromiseResolver}
*/
__gCrWeb['paymentRequestManager'].abortPromiseResolver = null;
/**
* The PromiseResolver object used to resolve the promise returned by
* PaymentResponse.prototype.complete, if any.
* @type {PaymentRequest}
*/
__gCrWeb['paymentRequestManager'].responsePromiseResolver = null;
/**
* Parses |paymentResponseData| into a PaymentResponse object.
* @param {!SerializedPaymentResponse} paymentResponseData
* @return {PaymentResponse}
*/
__gCrWeb['paymentRequestManager'].parsePaymentResponseData = function(
paymentResponseData) {
var response = new PaymentResponse();
response.requestId = paymentResponseData['requestId'];
response.methodName = paymentResponseData['methodName'];
response.details = paymentResponseData['details'];
if (paymentResponseData['shippingAddress'])
response.shippingAddress = paymentResponseData['shippingAddress'];
if (paymentResponseData['shippingOption'])
response.shippingOption = paymentResponseData['shippingOption'];
if (paymentResponseData['payerName'])
response.payerName = paymentResponseData['payerName'];
if (paymentResponseData['payerEmail'])
response.payerEmail = paymentResponseData['payerEmail'];
if (paymentResponseData['payerPhone'])
response.payerPhone = paymentResponseData['payerPhone'];
return response;
};
/**
* The event that enables the web page to update the details of the payment
* request in response to a user interaction.
* @type {Event}
*/
__gCrWeb['paymentRequestManager'].updateEvent = null;
/**
* Handles invocation of updateWith() on the updateEvent object.
* @param {!Promise<!window.PaymentDetails>|!window.PaymentDetails}
* detailsOrPromise
* @throws {DOMException}
*/
__gCrWeb['paymentRequestManager'].updateEventUpdateWith = function(
detailsOrPromise) {
if (!this || this != __gCrWeb['paymentRequestManager'].updateEvent)
return;
// if |detailsOrPromise| is not an instance of a Promise, wrap it in a
// Promise that fulfills with |detailsOrPromise|.
if (!detailsOrPromise || !(detailsOrPromise.then instanceof Function) ||
!(detailsOrPromise.catch instanceof Function)) {
detailsOrPromise = Promise.resolve(detailsOrPromise);
}
__gCrWeb['paymentRequestManager']
.updateWith(detailsOrPromise)
.then(function() {
__gCrWeb['paymentRequestManager'].updateEvent = null;
});
};
/**
* Updates the payment details. Throws an error if |detailsPromise| is not a
* valid instance of window.PaymentDetails.
* @param {!Promise<!window.PaymentDetails>} detailsPromise
* @return {!Promise} A completion Promise that is always resolved whether
* detailsPromise is resolved or rejected.
* @throws {DOMException}
* @suppress {checkTypes} Required for DOMException's constructor.
*/
__gCrWeb['paymentRequestManager'].updateWith = function(detailsPromise) {
if (!__gCrWeb['paymentRequestManager'].pendingRequest ||
__gCrWeb['paymentRequestManager'].pendingRequest.state !=
PaymentRequestState.INTERACTIVE) {
return;
}
if (__gCrWeb['paymentRequestManager'].pendingRequest.updating) {
throw new DOMException(
'Failed to update PaymentRequest details' +
': \'Cannot update details twice',
'InvalidStateError');
}
__gCrWeb['paymentRequestManager'].pendingRequest.updating = true;
var message = {
'command': 'paymentRequest.setPendingRequestUpdating',
'updating': true,
};
__gCrWeb.message.invokeOnHost(message);
return detailsPromise
.then(function(paymentDetails) {
if (!paymentDetails)
throw new TypeError(
'An instance of PaymentDetails must be provided.');
// Validate details and update the shipping options.
var validatedShippingOptions =
__gCrWeb['paymentRequestManager'].validatePaymentDetailsUpdate(
paymentDetails);
paymentDetails.shippingOptions = validatedShippingOptions;
var message = {
'command': 'paymentRequest.updatePaymentDetails',
'payment_details': paymentDetails,
};
__gCrWeb.message.invokeOnHost(message);
__gCrWeb['paymentRequestManager'].pendingRequest.updating = false;
})
.catch(function() {
var message = {
'command': 'paymentRequest.requestAbort',
};
__gCrWeb.message.invokeOnHost(message);
});
};
/**
* Resolves the pending PaymentRequest.
* @param {!SerializedPaymentResponse} paymentResponseData The response to
* provide to the resolve function of the Promise.
*/
__gCrWeb['paymentRequestManager'].resolveRequestPromise = function(
paymentResponseData) {
if (!__gCrWeb['paymentRequestManager'].requestPromiseResolver) {
throw new Error('Internal PaymentRequest error: No Promise to resolve.');
}
if (!paymentResponseData) {
__gCrWeb['paymentRequestManager'].rejectRequestPromise(
'Internal PaymentRequest error: PaymentResponse missing.');
}
var paymentResponse = null;
try {
paymentResponse =
__gCrWeb['paymentRequestManager'].parsePaymentResponseData(
paymentResponseData);
} catch (e) {
__gCrWeb['paymentRequestManager'].rejectRequestPromise(
'Internal PaymentRequest error: failed to parse PaymentResponse.');
}
__gCrWeb['paymentRequestManager'].responsePromiseResolver =
new __gCrWeb.PromiseResolver();
__gCrWeb['paymentRequestManager'].requestPromiseResolver.resolve(
paymentResponse);
__gCrWeb['paymentRequestManager'].requestPromiseResolver = null;
__gCrWeb['paymentRequestManager'].pendingRequest = null;
__gCrWeb['paymentRequestManager'].updateEvent = null;
};
/**
* Rejects the pending PaymentRequest.
* @param {string} message An error message explaining why the Promise is
* being rejected.
*/
__gCrWeb['paymentRequestManager'].rejectRequestPromise = function(message) {
if (!__gCrWeb['paymentRequestManager'].requestPromiseResolver) {
throw new Error(
'Internal PaymentRequest error: No Promise to reject. ',
'Message was: ', message);
}
__gCrWeb['paymentRequestManager'].requestPromiseResolver.reject(message);
__gCrWeb['paymentRequestManager'].requestPromiseResolver = null;
__gCrWeb['paymentRequestManager'].pendingRequest = null;
__gCrWeb['paymentRequestManager'].updateEvent = null;
};
/**
* Resolves the promise returned by calling PaymentRequest.prototype.abort.
* This method also gets called when the request is aborted through rejecting
* the promise passed to PaymentRequestUpdateEvent.prototype.updateWith.
* Therefore, it does nothing if there is no abort promise to resolve.
*/
__gCrWeb['paymentRequestManager'].resolveAbortPromise = function() {
if (!__gCrWeb['paymentRequestManager'].abortPromiseResolver)
return;
__gCrWeb['paymentRequestManager'].abortPromiseResolver.resolve();
__gCrWeb['paymentRequestManager'].abortPromiseResolver = null;
__gCrWeb['paymentRequestManager'].pendingRequest = null;
__gCrWeb['paymentRequestManager'].updateEvent = null;
};
/**
* Resolves the promise returned by calling canMakePayment on the current
* PaymentRequest.
* @param {boolean} value The response to provide to the resolve function of
* the Promise.
*/
__gCrWeb['paymentRequestManager'].resolveCanMakePaymentPromise = function(
value) {
if (!__gCrWeb['paymentRequestManager'].canMakePaymentPromiseResolver) {
throw new Error('Internal PaymentRequest error: No Promise to resolve.');
}
__gCrWeb['paymentRequestManager'].canMakePaymentPromiseResolver.resolve(
value);
__gCrWeb['paymentRequestManager'].canMakePaymentPromiseResolver = null;
};
/**
* Rejects the promise returned by calling canMakePayment on the current
* PaymentRequest.
* @param {string} message An error message explaining why the Promise is
* being rejected.
*/
__gCrWeb['paymentRequestManager'].rejectCanMakePaymentPromise = function(
message) {
if (!__gCrWeb['paymentRequestManager'].canMakePaymentPromiseResolver) {
throw new Error(
'Internal PaymentRequest error: No Promise to reject. ',
'Message was: ', message);
}
__gCrWeb['paymentRequestManager'].canMakePaymentPromiseResolver.reject(
message);
__gCrWeb['paymentRequestManager'].canMakePaymentPromiseResolver = null;
};
/**
* Serializes |paymentRequest| to a SerializedPaymentRequest object.
* @param {PaymentRequest} paymentRequest
* @return {SerializedPaymentRequest}
*/
__gCrWeb['paymentRequestManager'].serializePaymentRequest = function(
paymentRequest) {
var serialized = {
'id': paymentRequest.id,
'methodData': paymentRequest.methodData,
'details': paymentRequest.details,
};
if (paymentRequest.options)
serialized['options'] = paymentRequest.options;
return serialized;
};
/**
* Resolves the pending PaymentResponse.
*/
__gCrWeb['paymentRequestManager'].resolveResponsePromise = function() {
if (!__gCrWeb['paymentRequestManager'].responsePromiseResolver) {
throw new Error('Internal PaymentRequest error: No Promise to resolve.');
}
__gCrWeb['paymentRequestManager'].responsePromiseResolver.resolve();
__gCrWeb['paymentRequestManager'].responsePromiseResolver = null;
};
/**
* Updates the shipping_option property of the PaymentRequest object to
* |shippingOptionID| and dispatches a shippingoptionchange event.
* @param {string} shippingOptionID
*/
__gCrWeb['paymentRequestManager'].updateShippingOptionAndDispatchEvent =
function(shippingOptionID) {
var pendingRequest = __gCrWeb['paymentRequestManager'].pendingRequest;
if (!pendingRequest ||
pendingRequest.state != PaymentRequestState.INTERACTIVE ||
pendingRequest.updating) {
return;
}
pendingRequest.shippingOption = shippingOptionID;
__gCrWeb['paymentRequestManager'].updateEvent = new Event(
'shippingoptionchange', {'bubbles': true, 'cancelable': false});
Object.defineProperty(
__gCrWeb['paymentRequestManager'].updateEvent, 'updateWith',
{value: __gCrWeb['paymentRequestManager'].updateEventUpdateWith});
// setTimeout() is used in order to return immediately. Otherwise the
// dispatchEvent call waits for all event handlers to return, which could
// cause a ReentryGuard failure.
window.setTimeout(function() {
pendingRequest.dispatchEvent(
__gCrWeb['paymentRequestManager'].updateEvent);
}, 0);
};
/**
* Updates the shipping_address property of the PaymentRequest object to
* |shippingAddress| and dispatches a shippingaddresschange event.
* @param {!window.PaymentAddress} shippingAddress
*/
__gCrWeb['paymentRequestManager'].updateShippingAddressAndDispatchEvent =
function(shippingAddress) {
var pendingRequest = __gCrWeb['paymentRequestManager'].pendingRequest;
if (!pendingRequest ||
pendingRequest.state != PaymentRequestState.INTERACTIVE ||
pendingRequest.updating) {
return;
}
pendingRequest.shippingAddress = shippingAddress;
__gCrWeb['paymentRequestManager'].updateEvent = new Event(
'shippingaddresschange', {'bubbles': true, 'cancelable': false});
Object.defineProperty(
__gCrWeb['paymentRequestManager'].updateEvent, 'updateWith',
{value: __gCrWeb['paymentRequestManager'].updateEventUpdateWith});
// setTimeout() is used in order to return immediately. Otherwise the
// dispatchEvent call waits for all event handlers to return, which could
// cause a ReentryGuard failure.
window.setTimeout(function() {
pendingRequest.dispatchEvent(
__gCrWeb['paymentRequestManager'].updateEvent);
}, 0);
};
}()); // End of anonymous object
/**
* Possible values for the state of the payment request according to:
* https://w3c.github.io/payment-request/#dfn-x%5B%5Bstate%5D%5D
* @enum {string}
*/
var PaymentRequestState = {
CREATED: 'created',
INTERACTIVE: 'interactive',
CLOSED: 'closed'
};
/**
* A request to make a payment.
* @param {!Array<!window.PaymentMethodData>} methodData Payment method
* identifiers for the payment methods that the web site accepts and any
* associated payment method specific data.
* @param {!window.PaymentDetails} details Information about the transaction
* that the user is being asked to complete such as the line items in an
* order.
* @param {window.PaymentOptions=} opt_options Information about what
* options the web page wishes to use from the payment request system.
* @constructor
* @implements {EventTarget}
* @extends {__gCrWeb.EventTarget}
* @throws {DOMException|TypeError|RangeError}
* @suppress {checkTypes} Required for DOMException's constructor.
*/
function PaymentRequest(methodData, details, opt_options) {
__gCrWeb.EventTarget.call(this);
if (window.top != window.self) {
throw new DOMException(
'Failed to construct \'PaymentRequest\': Must be in top-level context',
'SecurityError');
}
// https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
var isOriginPotentiallyTrustworthy = window.location.origin !== 'null' &&
(window.location.protocol === 'https:' ||
window.location.hostname === '127.0.0.1' ||
window.location.hostname === '::1' ||
window.location.hostname === 'localhost' ||
window.location.protocol === 'file:');
if (!__gCrWeb['paymentRequestManager'].isContextSecure ||
!isOriginPotentiallyTrustworthy) {
throw new DOMException(
'Failed to construct \'PaymentRequest\': Must be in a secure context',
'SecurityError');
}
// Initialize these properties to make them own (vs. inherited) properties.
this.shippingAddress = null;
this.shippingOption = null;
this.shippingType = null;
// Tracks the event handler registered via
// PaymentRequest.prototype.onshippingaddresschange.
this.shippingAddressChangeHandler = null;
Object.defineProperty(this, 'onshippingaddresschange', {
set(handler) {
if (this.shippingAddressChangeHandler) {
this.removeEventListener(
'shippingaddresschange', this.shippingAddressChangeHandler);
}
this.shippingAddressChangeHandler = handler;
this.addEventListener('shippingaddresschange', handler);
},
configurable: true
});
// Tracks the event handler registered via
// PaymentRequest.prototype.onshippingoptionchange.
this.shippingOptionChangeHandler = null;
Object.defineProperty(this, 'onshippingoptionchange', {
set(handler) {
if (this.shippingOptionChangeHandler) {
this.removeEventListener(
'shippingoptionchange', this.shippingOptionChangeHandler);
}
this.shippingOptionChangeHandler = handler;
this.addEventListener('shippingoptionchange', handler);
},
configurable: true
});
/**
* The state of this request, used to govern its lifecycle.
* @type {PaymentRequestState}
* @private
*/
this.state = PaymentRequestState.CREATED;
/**
* True if there is a pending updateWith call to update the payment request
* and false otherwise.
* @type {boolean}
* @private
*/
this.updating = false;
// Validate methodData.
__gCrWeb['paymentRequestManager'].validatePaymentMethodData(methodData);
/**
* @type {!Array<!window.PaymentMethodData>}
* @private
*/
this.methodData = methodData;
// Validate details and update the shipping options.
var validatedShippingOptions =
__gCrWeb['paymentRequestManager'].validatePaymentDetailsInit(
details, opt_options);
details.shippingOptions = validatedShippingOptions;
this.id = details.id ? details.id : __gCrWeb['paymentRequestManager'].guid();
/**
* @type {!window.PaymentDetails}
* @private
*/
this.details = details;
// Pick the last selected shipping option as the selected shipping option.
if (opt_options && opt_options.requestShipping) {
for (var i = 0; i < validatedShippingOptions.length; i++) {
if (validatedShippingOptions[i].selected)
this.shippingOption = validatedShippingOptions[i].id;
}
}
// Validate the opt_options.shippingType.
if (opt_options && opt_options.shippingType &&
opt_options.shippingType != PaymentShippingType.SHIPPING &&
opt_options.shippingType != PaymentShippingType.DELIVERY &&
opt_options.shippingType != PaymentShippingType.PICKUP) {
throw new TypeError(
'The provided shipping type is not a valid enum value of type ' +
'PaymentShippingType');
}
// Use the provided shipping type. Otherwise default to "shipping".
if (opt_options && opt_options.requestShipping) {
this.shippingType = opt_options.shippingType ? opt_options.shippingType :
PaymentShippingType.SHIPPING;
}
/**
* @type {?window.PaymentOptions}
* @private
*/
this.options = opt_options || null;
var message = {
'command': 'paymentRequest.createPaymentRequest',
'payment_request':
__gCrWeb['paymentRequestManager'].serializePaymentRequest(this),
};
__gCrWeb.message.invokeOnHost(message);
};
PaymentRequest.prototype.__proto__ = __gCrWeb.EventTarget.prototype;
/**
* A provided or generated ID for the this Payment Request instance.
* @type {string}
*/
PaymentRequest.prototype.id = '';
/**
* Shipping address selected by the user.
* @type {?window.PaymentAddress}
*/
PaymentRequest.prototype.shippingAddress = null;
/**
* Shipping option selected by the user.
* @type {?string}
*/
PaymentRequest.prototype.shippingOption = null;
/**
* Set to the value of shippingType property of |opt_options| if it is a valid
* PaymentShippingType. Otherwise set to PaymentShippingType.SHIPPING.
* @type {?PaymentShippingType}
*/
PaymentRequest.prototype.shippingType = null;
/**
* Presents the PaymentRequest UI to the user. If |opt_detailsPromise| is a
* valid Promise, disables the UI and waits until the Promise settles. If the
* Promise is resolved with valid payment details, enables the UI with the new
* details. Otherwise, the request is aborted.
* @param {Promise<!window.PaymentDetails>=} opt_detailsPromise
* @return {!Promise<PaymentResponse>} A promise to notify the caller
* whether the user accepted or rejected the request.
* @suppress {checkTypes} Required for DOMException's constructor.
*/
PaymentRequest.prototype.show = function(opt_detailsPromise) {
if (!(this instanceof PaymentRequest)) {
return Promise.reject(new DOMException(
'show() must be called on an instance of PaymentRequest.',
'InvalidStateError'));
}
if (this.state != PaymentRequestState.CREATED) {
return Promise.reject(
new DOMException('Already called show() once', 'InvalidStateError'));
}
if (__gCrWeb['paymentRequestManager'].pendingRequest ||
__gCrWeb['paymentRequestManager'].requestPromiseResolver) {
return Promise.reject(new DOMException(
'Only one PaymentRequest may be shown at a time', 'AbortError'));
}
__gCrWeb['paymentRequestManager'].pendingRequest = this;
__gCrWeb['paymentRequestManager'].requestPromiseResolver =
new __gCrWeb.PromiseResolver();
this.state = PaymentRequestState.INTERACTIVE;
var waitForShowPromise = false;
// if |opt_detailsPromise| is an instance of Promise, the UI should be
// disabled until the Promise is settled.
if (opt_detailsPromise && opt_detailsPromise.then instanceof Function &&
opt_detailsPromise.catch instanceof Function) {
waitForShowPromise = true;
}
var message = {
'command': 'paymentRequest.requestShow',
'payment_request':
__gCrWeb['paymentRequestManager'].serializePaymentRequest(this),
'waitForShowPromise': waitForShowPromise,
};
__gCrWeb.message.invokeOnHost(message);
// if |opt_detailsPromise| is an instance of Promise, update the payment
// details with it. The UI will be re-enabled with the new details or the flow
// will be aborted depending on how this Promise settles.
if (waitForShowPromise) {
__gCrWeb['paymentRequestManager'].updateWith(
__gCrWeb['paymentRequestManager'].settleOrTimeout(
__gCrWeb['paymentRequestManager'].SHOW_PROMISE_TIMEOUT_MS,
opt_detailsPromise));
}
return __gCrWeb['paymentRequestManager'].requestPromiseResolver.promise;
};
/**
* May be called if the web page wishes to tell the user agent to abort the
* payment request and to tear down any user interface that might be shown.
* @return {!Promise<undefined>} A promise to notify the caller whether the user
* agent was able to abort the payment request.
* @suppress {checkTypes} Required for DOMException's constructor.
*/
PaymentRequest.prototype.abort = function() {
if (!(this instanceof PaymentRequest)) {
return Promise.reject(new DOMException(
'abort() must be called on an instance of PaymentRequest.',
'InvalidStateError'));
}
if (!__gCrWeb['paymentRequestManager'].pendingRequest ||
!__gCrWeb['paymentRequestManager'].requestPromiseResolver) {
return Promise.reject(new DOMException(
'Never called show(), so nothing to abort', 'InvalidStateError'));
}
if (__gCrWeb['paymentRequestManager'].abortPromiseResolver) {
return Promise.reject(new DOMException(
'Cannot abort() again until the previous abort() has resolved or ' +
'rejected'));
}
__gCrWeb['paymentRequestManager'].abortPromiseResolver =
new __gCrWeb.PromiseResolver();
this.state = PaymentRequestState.CLOSED;
var message = {
'command': 'paymentRequest.requestAbort',
};
__gCrWeb.message.invokeOnHost(message);
return __gCrWeb['paymentRequestManager'].abortPromiseResolver.promise;
};
/**
* May be called before calling show() to determine if the PaymentRequest object
* can be used to make a payment.
* @return {!Promise<boolean>} A promise to notify the caller whether the
* PaymentRequest object can be used to make a payment.
* @suppress {checkTypes} Required for DOMException's constructor.
*/
PaymentRequest.prototype.canMakePayment = function() {
if (!(this instanceof PaymentRequest)) {
return Promise.reject(new DOMException(
'canMakePayment() must be called on an instance of PaymentRequest.',
'InvalidStateError'));
}
if (this.state != PaymentRequestState.CREATED) {
return Promise.reject(
new DOMException('Cannot query payment request', 'InvalidStateError'));
}
var message = {
'command': 'paymentRequest.requestCanMakePayment',
'payment_request':
__gCrWeb['paymentRequestManager'].serializePaymentRequest(this),
};
__gCrWeb.message.invokeOnHost(message);
__gCrWeb['paymentRequestManager'].canMakePaymentPromiseResolver =
new __gCrWeb.PromiseResolver();
return __gCrWeb['paymentRequestManager'].canMakePaymentPromiseResolver
.promise;
};
/**
* @typedef {{
* supportedNetworks: !Array<string>,
* supportedTypes: !Array<string>
* }}
*/
window.BasicCardData;
/**
* @typedef {{
* supportedMethods: (!Array<string>|string),
* data: (!window.BasicCardData|Object)
* }}
*/
window.PaymentMethodData;
/**
* @typedef {{
* currency: string,
* value: string,
* }}
*/
window.PaymentCurrencyAmount;
/**
* @typedef {{
* id: (string|undefined),
* total: (window.PaymentItem|undefined),
* displayItems: (!Array<!window.PaymentItem>|undefined),
* shippingOptions: (!Array<!window.PaymentShippingOption>|undefined),
* modifiers: (!Array<!window.PaymentDetailsModifier>|undefined),
* error: (string|undefined)
* }}
*/
window.PaymentDetails;
/**
* @typedef {{
* supportedMethods: (!Array<string>|string),
* total: (window.PaymentItem|undefined),
* additionalDisplayItems: (!Array<!window.PaymentItem>|undefined),
* data: (!window.BasicCardData|Object)
* }}
*/
window.PaymentDetailsModifier;
/**
* Contains the possible values for affecting the payment request user interface
* for gathering the shipping address if window.PaymentOptions.requestShipping
* is set to true.
* @enum {string}
*/
var PaymentShippingType = {
SHIPPING: 'shipping',
DELIVERY: 'delivery',
PICKUP: 'pickup'
};
/**
* @typedef {{
* requestPayerName: (boolean|undefined),
* requestPayerEmail: (boolean|undefined),
* requestPayerPhone: (boolean|undefined),
* requestShipping: (boolean|undefined),
* shippingType: (!PaymentShippingType|undefined)
* }}
*/
window.PaymentOptions;
/**
* @typedef {{
* label: string,
* amount: window.PaymentCurrencyAmount
* }}
*/
window.PaymentItem;
/**
* @typedef {{
* country: string,
* addressLine: !Array<string>,
* region: string,
* city: string,
* dependentLocality: string,
* postalCode: string,
* sortingCode: string,
* languageCode: string,
* organization: string,
* recipient: string,
* phone: string
* }}
*/
window.PaymentAddress;
/**
* @typedef {{
* id: string,
* label: string,
* amount: window.PaymentCurrencyAmount,
* selected: boolean
* }}
*/
window.PaymentShippingOption;
/**
* A response to a request to make a payment. Represents the choices made by the
* user and provided to the web page through the resolve function of the Promise
* returned by PaymentRequest.show().
* https://w3c.github.io/browser-payment-api/#paymentresponse-interface
* @constructor
* @private
*/
function PaymentResponse(){
// Initialize these properties to make them own (vs. inherited) properties.
this.requestId = '';
this.methodName = '';
this.details = {};
this.shippingAddress = null;
this.shippingOption = null;
this.payerName = null;
this.payerEmail = null;
this.payerPhone = null;
};
/**
* The same identifier present in the original PaymentRequest.
* @type {string}
*/
PaymentResponse.prototype.requestId = '';
/**
* The payment method identifier for the payment method that the user selected
* to fulfil the transaction.
* @type {string}
*/
PaymentResponse.prototype.methodName = '';
/**
* An object that provides a payment method specific message used by the
* merchant to process the transaction and determine successful fund transfer.
* the payment request.
* @type {Object}
*/
PaymentResponse.prototype.details = {};
/**
* If the requestShipping flag was set to true in the window.PaymentOptions
* passed to the PaymentRequest constructor, this will be the full and
* final shipping address chosen by the user.
* @type {?window.PaymentAddress}
*/
PaymentResponse.prototype.shippingAddress = null;
/**
* If the requestShipping flag was set to true in the window.PaymentOptions
* passed to the PaymentRequest constructor, this will be the id
* attribute of the selected shipping option.
* @type {?string}
*/
PaymentResponse.prototype.shippingOption = null;
/**
* If the requestPayerName flag was set to true in the window.PaymentOptions
* passed to the PaymentRequest constructor, this will be the name
* provided by the user.
* @type {?string}
*/
PaymentResponse.prototype.payerName = null;
/**
* If the requestPayerEmail flag was set to true in the window.PaymentOptions
* passed to the PaymentRequest constructor, this will be the email
* address chosen by the user.
* @type {?string}
*/
PaymentResponse.prototype.payerEmail = null;
/**
* If the requestPayerPhone flag was set to true in the window.PaymentOptions
* passed to the PaymentRequest constructor, this will be the phone
* number chosen by the user.
* @type {?string}
*/
PaymentResponse.prototype.payerPhone = null;
/**
* Contains the possible values for the string argument accepted by
* PaymentResponse.prototype.complete.
* @enum {string}
*/
var PaymentComplete = {
FAIL: 'fail',
SUCCESS: 'success',
UNKNOWN: 'unknown'
};
/**
* Communicates the result of processing the payment.
* @param {PaymentComplete=} opt_result Indicates whether payment was
* successfully processed.
* @return {!Promise} A promise to notify the caller when the user interface has
* been closed.
*/
PaymentResponse.prototype.complete = function(opt_result) {
if (opt_result != PaymentComplete.UNKNOWN &&
opt_result != PaymentComplete.SUCCESS &&
opt_result != PaymentComplete.FAIL) {
opt_result = PaymentComplete.UNKNOWN;
}
if (!__gCrWeb['paymentRequestManager'].responsePromiseResolver) {
throw new Error('Internal PaymentRequest error: No Promise to return.');
}
var message = {
'command': 'paymentRequest.responseComplete',
'result': opt_result,
};
__gCrWeb.message.invokeOnHost(message);
return __gCrWeb['paymentRequestManager'].responsePromiseResolver.promise;
};