mirror of
https://github.com/google/nomulus.git
synced 2025-07-21 02:06:00 +02:00
Delete all Braintree code
We never launched this, don't planning on launching it now anyway, and it's rotted over the past two years anyway. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=202993577
This commit is contained in:
parent
7023b818b7
commit
32b3563126
67 changed files with 28 additions and 3352 deletions
|
@ -10,7 +10,6 @@ exports_files(["globals.txt"])
|
|||
filegroup(
|
||||
name = "runfiles",
|
||||
srcs = glob(["assets/**"]) + [
|
||||
"brain_bin.js",
|
||||
"registrar_bin.js",
|
||||
"//java/google/registry/ui/css:registrar_bin.css",
|
||||
"//java/google/registry/ui/html:html_files",
|
||||
|
@ -27,7 +26,6 @@ filegroup(
|
|||
filegroup(
|
||||
name = "runfiles_debug",
|
||||
srcs = [
|
||||
":brain_bin",
|
||||
":deps",
|
||||
":registrar_bin",
|
||||
":registrar_dbg",
|
||||
|
@ -63,7 +61,6 @@ zip_file(
|
|||
out = "ui_debug.war",
|
||||
data = [":runfiles_debug"],
|
||||
mappings = {
|
||||
"domain_registry/java/google/registry/ui/brain_bin.js.map": "assets/js/brain_bin.js.map",
|
||||
"domain_registry/java/google/registry/ui/registrar_bin.js.map": "assets/js/registrar_bin.js.map",
|
||||
"domain_registry/java/google/registry/ui/registrar_dbg.js": "assets/js/registrar_dbg.js",
|
||||
"domain_registry/java/google/registry/ui/css/registrar_dbg.css": "assets/css/registrar_dbg.css",
|
||||
|
@ -77,7 +74,6 @@ java_library(
|
|||
srcs = glob(["*.java"]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//java/google/registry/config",
|
||||
"@com_google_appengine_api_1_0_sdk",
|
||||
"@com_google_code_findbugs_jsr305",
|
||||
"@com_google_dagger",
|
||||
|
@ -124,16 +120,3 @@ closure_js_binary(
|
|||
"//java/google/registry/ui/js/registrar",
|
||||
],
|
||||
)
|
||||
|
||||
################################################################################
|
||||
## Braintree Payment Method Frame (Brainframe)
|
||||
|
||||
closure_js_binary(
|
||||
name = "brain_bin",
|
||||
entry_points = ["goog:registry.registrar.BrainFrame.main"],
|
||||
output_wrapper = "%output%//# sourceMappingURL=brain_bin.js.map",
|
||||
deps = [
|
||||
"//java/google/registry/ui/externs",
|
||||
"//java/google/registry/ui/js/registrar",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui;
|
||||
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
|
||||
/** Dagger module for UI configuration. */
|
||||
@Module
|
||||
public final class ConsoleConfigModule { // TODO(b/26829015): Move to config package.
|
||||
|
||||
@Provides
|
||||
static ConsoleDebug provideConsoleDebug() {
|
||||
return ConsoleDebug.get();
|
||||
}
|
||||
|
||||
/** URL of Braintree iframe sandbox iframe static HTML file. */
|
||||
@Provides
|
||||
@Config("brainframe")
|
||||
static String provideBrainframe(
|
||||
ConsoleDebug debug,
|
||||
UserService userService,
|
||||
@Config("projectId") String projectId) {
|
||||
switch (debug) {
|
||||
case PRODUCTION:
|
||||
return String.format("https://%s.storage.googleapis.com/%s",
|
||||
projectId,
|
||||
userService.isUserLoggedIn() && userService.isUserAdmin()
|
||||
? "brainframe-map.html"
|
||||
: "brainframe.html");
|
||||
case DEBUG:
|
||||
case RAW:
|
||||
case TEST:
|
||||
return "/assets/html/insecure-brainframe.html";
|
||||
default:
|
||||
throw new AssertionError(debug.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
package google.registry.ui;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
/** Enum defining which JS/CSS files get rendered in a soy templates. */
|
||||
public enum ConsoleDebug {
|
||||
|
||||
|
@ -44,4 +47,14 @@ public enum ConsoleDebug {
|
|||
public static void set(ConsoleDebug value) {
|
||||
System.setProperty(PROPERTY, value.toString());
|
||||
}
|
||||
|
||||
/** Dagger module for ConsoleDebug. */
|
||||
@Module
|
||||
public static final class ConsoleConfigModule {
|
||||
|
||||
@Provides
|
||||
static ConsoleDebug provideConsoleDebug() {
|
||||
return ConsoleDebug.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<!doctype html>
|
||||
<!-- gsutil cp -a public-read -z html brainframe-alpha.html gs://domain-registry-alpha/brainframe.html -->
|
||||
<!-- curl https://domain-registry-alpha.storage.googleapis.com/brainframe.html -->
|
||||
<script src="https://js.braintreegateway.com/v2/braintree.js"></script>
|
||||
<script src="https://domain-registry-alpha.appspot.com/assets/js/brain_bin.js"></script>
|
||||
<body style="margin:0">
|
||||
<form><div id="brainframe"></div></form>
|
||||
<script>registry.registrar.BrainFrame.main('https://domain-registry-alpha.appspot.com', 'brainframe');</script>
|
|
@ -1,14 +0,0 @@
|
|||
<!doctype html>
|
||||
<!-- Production iframe sandbox for Braintree iframe -->
|
||||
<!-- -->
|
||||
<!-- In order to securely embed Braintree's iframe, this iframe must be -->
|
||||
<!-- served from a separate origin. To do this, we manually deploy this -->
|
||||
<!-- as a static HTML file to a cloud storage bucket, as follows: -->
|
||||
<!-- -->
|
||||
<!-- gsutil cp -a public-read -z html brainframe.html gs://domain-registry/brainframe.html -->
|
||||
<!-- curl https://domain-registry.storage.googleapis.com/brainframe.html -->
|
||||
<script src="https://js.braintreegateway.com/v2/braintree.js"></script>
|
||||
<script src="https://domain-registry.appspot.com/assets/js/brain_bin.js"></script>
|
||||
<body style="margin:0">
|
||||
<form><div id="brainframe"></div></form>
|
||||
<script>registry.registrar.BrainFrame.main('https://domain-registry.appspot.com', 'brainframe');</script>
|
|
@ -1,11 +0,0 @@
|
|||
<!doctype html>
|
||||
<!-- This can only be used by admins in a testing environment. -->
|
||||
<!-- This iframe provides no isolation of Braintree from Console. -->
|
||||
<script src="https://js.braintreegateway.com/v2/braintree.js"></script>
|
||||
<script>var CLOSURE_NO_DEPS = true;</script>
|
||||
<script src="/assets/sources/closure_library/closure/goog/base.js"></script>
|
||||
<script src="/assets/sources/domain_registry/java/google/registry/ui/deps.js"></script>
|
||||
<body style="margin:0">
|
||||
<form><div id="brainframe"></div></form>
|
||||
<script>goog.require('registry.registrar.BrainFrame.main');</script>
|
||||
<script>registry.registrar.BrainFrame.main('*', 'brainframe');</script>
|
|
@ -1,4 +0,0 @@
|
|||
<!doctype html>
|
||||
<!-- Brainframe for integration testing. -->
|
||||
<form><div id="brainframe"></div></form>
|
||||
<script>window.parent.postMessage('{"type": "payment_method", "method": {"type": "CreditCard", "nonce": "imanonce", "details": {"cardType": "Visa", "lastTwo": "00"}}}', '*');</script>
|
|
@ -75,28 +75,3 @@ div.domain-registrar-contact div.tooltip .pointer {
|
|||
padding-left: 1em;
|
||||
list-style: disc inside;
|
||||
}
|
||||
|
||||
.reg-payment p {
|
||||
max-width: 45em;
|
||||
}
|
||||
|
||||
/* Note: Empty definitions are for class name minimization. */
|
||||
|
||||
.reg-payment-form {}
|
||||
|
||||
.reg-payment-form-method {}
|
||||
|
||||
.reg-payment-form-method.kd-formerror {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.reg-payment-form-method-info {}
|
||||
|
||||
.reg-payment-form-submit {}
|
||||
|
||||
.reg-payment-form-loader {
|
||||
vertical-align: middle;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.reg-payment-again {}
|
||||
|
|
|
@ -1,456 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Braintree JS SDK v2 externs. This file tells the Closure
|
||||
* Compiler how Braintree's API is defined, which allows us to use it with type
|
||||
* safety and dot-notation.
|
||||
* @externs
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @type {!braintreepayments.Braintree}
|
||||
*/
|
||||
var braintree;
|
||||
|
||||
|
||||
/**
|
||||
* Fake namespace for pure Closure Compiler types not defined by the SDK.
|
||||
*/
|
||||
var braintreepayments = {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
braintreepayments.Braintree = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} clientTokenFromServer
|
||||
* @param {string} integrationType Either 'dropin' or 'custom'.
|
||||
* @param {{container: (string|!Element|undefined),
|
||||
* dataCollector: (!Object|undefined),
|
||||
* enableCORS: (boolean|undefined),
|
||||
* form: (string|undefined),
|
||||
* hostedFields: (!Object|undefined),
|
||||
* id: (string|undefined),
|
||||
* onError: (function(!braintreepayments.Error)|undefined),
|
||||
* onPaymentMethodReceived:
|
||||
* (function(!braintreepayments.PaymentMethod)|undefined),
|
||||
* onReady: (function(!braintreepayments.Integrator)|undefined),
|
||||
* paypal: (undefined|{
|
||||
* amount: (number|undefined),
|
||||
* container: (string|!Element),
|
||||
* currency: (string|undefined),
|
||||
* displayName: (string|undefined),
|
||||
* enableBillingAddress: (boolean|undefined),
|
||||
* enableShippingAddress: (boolean|undefined),
|
||||
* headless: (boolean|undefined),
|
||||
* locale: (string|undefined),
|
||||
* onCancelled: (function()|undefined),
|
||||
* onPaymentMethodReceived:
|
||||
* (function(!braintreepayments.PaymentMethod)|undefined),
|
||||
* onUnsupported: (function()|undefined),
|
||||
* paymentMethodNonceInputField: (string|!Element|undefined),
|
||||
* shippingAddressOverride: (undefined|{
|
||||
* recipientName: string,
|
||||
* streetAddress: string,
|
||||
* extendedAddress: (string|undefined),
|
||||
* locality: string,
|
||||
* countryCodeAlpha2: string,
|
||||
* postalCode: string,
|
||||
* region: string,
|
||||
* phone: (string|undefined),
|
||||
* editable: boolean
|
||||
* }),
|
||||
* singleUse: (boolean|undefined)
|
||||
* })
|
||||
* }} options
|
||||
* @see https://developers.braintreepayments.com/guides/client-sdk/javascript/v2#global-setup
|
||||
*/
|
||||
braintreepayments.Braintree.prototype.setup =
|
||||
function(clientTokenFromServer, integrationType, options) {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
* @see https://developers.braintreepayments.com/guides/drop-in/javascript/v2#onerror
|
||||
*/
|
||||
braintreepayments.Error = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Describes type of error that occurred (e.g. "CONFIGURATION", "VALIDATION".)
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.Error.prototype.type;
|
||||
|
||||
|
||||
/**
|
||||
* Human-readable string describing the error.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.Error.prototype.message;
|
||||
|
||||
|
||||
/**
|
||||
* @type {(!braintreepayments.ErrorDetails|undefined)}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.Error.prototype.details;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
braintreepayments.ErrorDetails = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @type {(!Array<!braintreepayments.ErrorField>|undefined)}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.ErrorDetails.prototype.invalidFields;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
braintreepayments.ErrorField = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Field which failed validation. It will match one of the following: "number",
|
||||
* "cvv", "expiration", or "postalCode".
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.ErrorField.prototype.fieldKey;
|
||||
|
||||
|
||||
/**
|
||||
* This will be `true` if the associated input is empty.
|
||||
* @type {(boolean|undefined)}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.ErrorField.prototype.isEmpty;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
braintreepayments.Integrator = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.Integrator.prototype.deviceData;
|
||||
|
||||
|
||||
/**
|
||||
* @type {!braintreepayments.PaypalIntegrator}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.Integrator.prototype.paypal;
|
||||
|
||||
|
||||
/**
|
||||
* @param {function()=} opt_callback
|
||||
* @see https://developers.braintreepayments.com/guides/client-sdk/javascript/v2#teardown
|
||||
*/
|
||||
braintreepayments.Integrator.prototype.teardown = function(opt_callback) {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
braintreepayments.PaypalIntegrator = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
*/
|
||||
braintreepayments.PaypalIntegrator.prototype.closeAuthFlow = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
*/
|
||||
braintreepayments.PaypalIntegrator.prototype.initAuthFlow = function() {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
braintreepayments.PaymentMethod = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethod.prototype.nonce;
|
||||
|
||||
|
||||
/**
|
||||
* Either 'CreditCard' or 'PayPalAccount'.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethod.prototype.type;
|
||||
|
||||
|
||||
/**
|
||||
* @type {(!braintreepayments.PaymentMethodDetailsCard|
|
||||
* !braintreepayments.PaymentMethodDetailsPaypal)}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethod.prototype.details;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
* @see https://developers.braintreepayments.com/guides/client-sdk/javascript/v2#payment-method-details
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsCard = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Can be 'Visa', 'MasterCard', 'Discover', 'Amex', or 'JCB'.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsCard.prototype.cardType;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsCard.prototype.lastTwo;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
* @see https://developers.braintreepayments.com/guides/paypal/client-side/javascript/v2#options
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsPaypal = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsPaypal.prototype.email;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsPaypal.prototype.firstName;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsPaypal.prototype.lastName;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsPaypal.prototype.phone;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsPaypal.prototype.payerID;
|
||||
|
||||
|
||||
/**
|
||||
* @type {!braintreepayments.PaypalShippingAddress}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsPaypal.prototype.shippingAddress;
|
||||
|
||||
|
||||
/**
|
||||
* @type {(!braintreepayments.PaypalBillingAddress|undefined)}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaymentMethodDetailsPaypal.prototype.billingAddress;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.id;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.type;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.recipientName;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.streetAddress;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.extendedAddress;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.locality;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.region;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.postalCode;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.countryCodeAlpha;
|
||||
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.defaultAddress;
|
||||
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalShippingAddress.prototype.preferredAddress;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @final
|
||||
*/
|
||||
braintreepayments.PaypalBillingAddress = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalBillingAddress.prototype.streetAddress;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalBillingAddress.prototype.extendedAddress;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalBillingAddress.prototype.locality;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalBillingAddress.prototype.region;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalBillingAddress.prototype.postalCode;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
braintreepayments.PaypalBillingAddress.prototype.countryCodeAlpha2;
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview RegistrarPaymentAction JSON RPC definitions.
|
||||
* @externs
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
var registry = {};
|
||||
|
||||
|
||||
/**
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
registry.rpc = {};
|
||||
registry.rpc.Payment = {};
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* currency: string,
|
||||
* amount: string,
|
||||
* paymentMethodNonce: string
|
||||
* }}
|
||||
*/
|
||||
registry.rpc.Payment.Request;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {registry.json.Response.<!registry.rpc.Payment.Result>}
|
||||
*/
|
||||
registry.rpc.Payment.Response;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
registry.rpc.Payment.Result = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
registry.rpc.Payment.Result.prototype.id;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
registry.rpc.Payment.Result.prototype.formattedAmount;
|
|
@ -1,68 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview RegistrarPaymentSetupAction JSON RPC definitions.
|
||||
* @externs
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
var registry = {};
|
||||
|
||||
|
||||
/**
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
registry.rpc = {};
|
||||
registry.rpc.PaymentSetup = {};
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object}
|
||||
*/
|
||||
registry.rpc.PaymentSetup.Request;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {registry.json.Response.<!registry.rpc.PaymentSetup.Result>}
|
||||
*/
|
||||
registry.rpc.PaymentSetup.Response;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
registry.rpc.PaymentSetup.Result = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
registry.rpc.PaymentSetup.Result.prototype.token;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
registry.rpc.PaymentSetup.Result.prototype.brainframe;
|
||||
|
||||
|
||||
/**
|
||||
* @type {!Array.<string>}
|
||||
*/
|
||||
registry.rpc.PaymentSetup.Result.prototype.currencies;
|
|
@ -1,267 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.BrainFrame');
|
||||
goog.provide('registry.registrar.BrainFrame.main');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.style');
|
||||
|
||||
goog.forwardDeclare('goog.events.BrowserEvent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sandboxed iframe for Braintree JS SDK v2 iframe.
|
||||
*
|
||||
* <p>This class adds an additional layer of security between the Registrar
|
||||
* Console and JavaScript loaded from Braintree's web server.
|
||||
*
|
||||
* <p>The main function for this class is compiled into a separate binary,
|
||||
* which is loaded within an iframe that's hosted on a different domain than
|
||||
* the production environment. This ensures that cross origin browser security
|
||||
* policies take effect.
|
||||
*
|
||||
* @param {string} origin FQDN of production environment.
|
||||
* @param {string} containerId ID of Braintree container element.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventHandler}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.BrainFrame = function(origin, containerId) {
|
||||
registry.registrar.BrainFrame.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* Hostname of production registry, e.g. domain-registry.appspot.com.
|
||||
* @private {string}
|
||||
* @const
|
||||
*/
|
||||
this.origin_ = origin;
|
||||
|
||||
/**
|
||||
* Div that wraps Braintree iframe.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.container_ = goog.dom.getRequiredElement(containerId);
|
||||
|
||||
/**
|
||||
* Last known height of `container_`.
|
||||
* @private {number}
|
||||
*/
|
||||
this.containerHeight_ = 0;
|
||||
|
||||
/**
|
||||
* Timer polling for changes in Braintree iframe height.
|
||||
* @private {!goog.Timer}
|
||||
* @const
|
||||
*/
|
||||
this.resizeTimer_ = new goog.Timer(1000 / 30);
|
||||
this.registerDisposable(this.resizeTimer_);
|
||||
this.listen(this.resizeTimer_, goog.Timer.TICK, this.onResizeTimer_);
|
||||
|
||||
/**
|
||||
* Form that wraps `container_`.
|
||||
* @private {?Element}
|
||||
* @const
|
||||
*/
|
||||
this.form_ = goog.dom.getAncestorByTagNameAndClass(this.container_,
|
||||
goog.dom.TagName.FORM);
|
||||
goog.asserts.assert(this.form_ != null);
|
||||
|
||||
/**
|
||||
* State indicating if we're submitting at behest of parent.
|
||||
* @private {boolean}
|
||||
*/
|
||||
this.isSubmitting_ = false;
|
||||
|
||||
this.listen(goog.global.window,
|
||||
goog.events.EventType.MESSAGE,
|
||||
this.onMessage_);
|
||||
};
|
||||
goog.inherits(registry.registrar.BrainFrame, goog.events.EventHandler);
|
||||
|
||||
|
||||
/**
|
||||
* Runs Braintree sandbox environment.
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.run = function() {
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.TOKEN_REQUEST);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles message from parent iframe which sends Braintree token.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onMessage_ = function(e) {
|
||||
var msg = /** @type {!MessageEvent.<string>} */ (e.getBrowserEvent());
|
||||
if (msg.source != goog.global.window.parent) {
|
||||
return;
|
||||
}
|
||||
if (this.origin_ != '*' && this.origin_ != msg.origin) {
|
||||
throw new Error(
|
||||
'Message origin is "' + msg.origin + '" but wanted: ' + this.origin_);
|
||||
}
|
||||
var data = /** @type {!Object} */ (JSON.parse(msg.data));
|
||||
switch (goog.object.get(data, 'type')) {
|
||||
case registry.registrar.BrainFrame.MessageType.TOKEN_RESPONSE:
|
||||
goog.global.braintree.setup(goog.object.get(data, 'token'), 'dropin', {
|
||||
container: this.container_,
|
||||
onPaymentMethodReceived: goog.bind(this.onPaymentMethod_, this),
|
||||
onReady: goog.bind(this.onReady_, this),
|
||||
onError: goog.bind(this.onError_, this)
|
||||
});
|
||||
this.resizeTimer_.start();
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.SUBMIT_REQUEST:
|
||||
this.isSubmitting_ = true;
|
||||
// Trigger Braintree JS SDK submit event listener. It does not appear to
|
||||
// be possible to do this using the Closure Library. This is IE 9+ only.
|
||||
this.form_.dispatchEvent(new Event(goog.events.EventType.SUBMIT));
|
||||
break;
|
||||
default:
|
||||
throw Error('Unexpected message: ' + msg.data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Polls for resizes of Braintree iframe and propagates them to the parent
|
||||
* frame which will then use it to resize this iframe.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onResizeTimer_ = function() {
|
||||
var height = goog.style.getSize(this.container_).height;
|
||||
if (height != this.containerHeight_) {
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.RESIZE_REQUEST,
|
||||
'height', height);
|
||||
this.containerHeight_ = height;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback Braintree iframe has fully loaded.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onReady_ = function() {
|
||||
this.send_('type', registry.registrar.BrainFrame.MessageType.READY);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback Braintree says an error happened.
|
||||
* @param {!braintreepayments.Error} error
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onError_ = function(error) {
|
||||
this.isSubmitting_ = false;
|
||||
this.send_('type', registry.registrar.BrainFrame.MessageType.SUBMIT_ERROR,
|
||||
'message', error.message);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback when user successfully gave Braintree payment details.
|
||||
* @param {!braintreepayments.PaymentMethod} pm
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onPaymentMethod_ = function(pm) {
|
||||
// TODO(b/26829319): The Braintree JS SDK does not seem to recognize the
|
||||
// enter key while embedded inside our sandbox iframe. So
|
||||
// at this time, this callback will only be invoked after
|
||||
// we've submitted the form manually at the behest of
|
||||
// payment.js, which means isSubmitting_ will be true.
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.PAYMENT_METHOD,
|
||||
'submit', this.isSubmitting_,
|
||||
'method', pm);
|
||||
this.isSubmitting_ = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends message to parent iframe.
|
||||
* @param {...*} var_args Passed along to `goog.object.create`.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.send_ = function(var_args) {
|
||||
goog.asserts.assert(arguments[0] == 'type');
|
||||
registry.registrar.BrainFrame.postMessage_(
|
||||
goog.json.serialize(goog.object.create.apply(null, arguments)),
|
||||
this.origin_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to `window.parent.postMessage`. This method exists because
|
||||
* IE will not allow us to mock methods on the window object.
|
||||
* @param {string} message
|
||||
* @param {string} origin
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.postMessage_ = function(message, origin) {
|
||||
goog.global.window.parent.postMessage(message, origin);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Message types passed between brainframe and payment page.
|
||||
* @enum {string}
|
||||
*/
|
||||
registry.registrar.BrainFrame.MessageType = {
|
||||
|
||||
/** Brainframe asks payment page for Braintree token. */
|
||||
TOKEN_REQUEST: 'token_request',
|
||||
|
||||
/** Payment page sends brainframe Braintree token. */
|
||||
TOKEN_RESPONSE: 'token_response',
|
||||
|
||||
/** Brainframe asks payment page to be resized. */
|
||||
RESIZE_REQUEST: 'resize_request',
|
||||
|
||||
/** Brainframe tells payment page it finished loading. */
|
||||
READY: 'ready',
|
||||
|
||||
/** Payment page asks brainframe to submit Braintree payment method form. */
|
||||
SUBMIT_REQUEST: 'submit_request',
|
||||
|
||||
/** Brainframe tells payment page it failed to submit. */
|
||||
SUBMIT_ERROR: 'submit_error',
|
||||
|
||||
/** Brainframe gives payment method info and nonce to payment page. */
|
||||
PAYMENT_METHOD: 'payment_method'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Entrypoint for {@link registry.registrar.BrainFrame}.
|
||||
* @param {string} origin
|
||||
* @param {string} containerId
|
||||
* @export
|
||||
*/
|
||||
registry.registrar.BrainFrame.main = function(origin, containerId) {
|
||||
new registry.registrar.BrainFrame(origin, containerId).run();
|
||||
};
|
|
@ -26,7 +26,6 @@ goog.require('registry.registrar.Dashboard');
|
|||
goog.require('registry.registrar.Domain');
|
||||
goog.require('registry.registrar.EppSession');
|
||||
goog.require('registry.registrar.Host');
|
||||
goog.require('registry.registrar.Payment');
|
||||
goog.require('registry.registrar.Resources');
|
||||
goog.require('registry.registrar.SecuritySettings');
|
||||
goog.require('registry.registrar.WhoisSettings');
|
||||
|
@ -86,7 +85,6 @@ registry.registrar.Console = function(params) {
|
|||
this.pageMap['contact-us'] = registry.registrar.ContactUs;
|
||||
this.pageMap['resources'] = registry.registrar.Resources;
|
||||
this.pageMap['contact'] = registry.registrar.Contact;
|
||||
this.pageMap['payment'] = registry.registrar.Payment;
|
||||
this.pageMap['domain'] = registry.registrar.Domain;
|
||||
this.pageMap['host'] = registry.registrar.Host;
|
||||
this.pageMap[''] = registry.registrar.Dashboard;
|
||||
|
|
|
@ -1,415 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Payment');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.soy');
|
||||
goog.require('registry.Component');
|
||||
goog.require('registry.MenuButton');
|
||||
goog.require('registry.Session');
|
||||
goog.require('registry.forms');
|
||||
goog.require('registry.registrar.BrainFrame');
|
||||
goog.require('registry.soy.registrar.console');
|
||||
goog.require('registry.soy.registrar.payment');
|
||||
goog.require('registry.util');
|
||||
|
||||
goog.forwardDeclare('goog.events.BrowserEvent');
|
||||
goog.forwardDeclare('registry.registrar.Console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Page allowing registrar to send money to registry.
|
||||
*
|
||||
* <p>This page contains a form that asks the user to enter an arbitrary amount
|
||||
* and a payment method, which can be credit card or PayPal. Multiple
|
||||
* currencies are supported.
|
||||
*
|
||||
* <h3>PCI Compliance</h3>
|
||||
*
|
||||
* <p>We don't have any access whatsoever to the credit card information. We
|
||||
* embed an iframe run by Braintree Payments. The user can then provide his
|
||||
* credit card (or PayPal) details directly to Braintree. Braintree then gives
|
||||
* us a nonce value representing the payment method, which we can use to issue
|
||||
* the transaction.
|
||||
*
|
||||
* <h3>Bidirectional Protection</h3>
|
||||
*
|
||||
* <p>To use Braintree's iframe, we need to load a script from their server. We
|
||||
* don't want that script to have access to the Registrar Console. If Braintree
|
||||
* got pwnd, the attacker would be able to issue EPP commands as a registrar.
|
||||
*
|
||||
* <p>We fix this problem by embedding the Braintree iframe inside another
|
||||
* sandbox iframe that's hosted from a Cloud Storage bucket. This frame is
|
||||
* defined by `brainframe.html`. It's basically an empty shell that sends
|
||||
* a request back to the production environment for `brainframe.js`.
|
||||
*
|
||||
* <p>The importance of the Cloud Storage bucket is that it is served from a
|
||||
* separate domain. This causes the browser to forbid the iframe from accessing
|
||||
* the contents of the parent frame. The HTML5 `sandbox` attribute does
|
||||
* this too, but we can't use it, because the Venmo functionality in the
|
||||
* Braintree JS SDK needs to be able to access `document.cookie`, which
|
||||
* is forbidden in a sandbox environment. This HTML5 `sandbox` feature is
|
||||
* also not available in older versions of Internet Explorer.
|
||||
*
|
||||
* <h3>Business Logic</h3>
|
||||
*
|
||||
* <p>This page starts off as a loading glyph, while we issue an RPC to the
|
||||
* backend. We ask for a Braintree token, which currencies are available, and
|
||||
* the location of the brainframe HTML file. Once we get that data, we render
|
||||
* the form.
|
||||
*
|
||||
* <p>Once the sandbox iframe inside that form has loaded, it'll send us a
|
||||
* message asking for the token. We give it the token, which it uses to load
|
||||
* the Braintree iframe.
|
||||
*
|
||||
* <p>To make sure the sandbox iframe is the same size as the Braintree iframe,
|
||||
* the sandbox iframe will send us messages on occasion asking to be resized.
|
||||
*
|
||||
* <p>The disabled state of the submit button is managed judiciously. It's
|
||||
* disabled initially, until we get a READY message from the sandbox iframe,
|
||||
* indicating that the Braintree iframe is fully loaded. We also disable the
|
||||
* submit button during the submit process.
|
||||
*
|
||||
* <p>When the user presses the submit button, we send a message to the sandbox
|
||||
* iframe asking it to submit the Braintree iframe. When the Braintree iframe
|
||||
* is submitted, it gives the sandbox iframe the the payment method nonce,
|
||||
* which it passes along to us. Then we pass the form data to the backend via
|
||||
* the payment RPC, which invokes the Braintree Java API to issue the
|
||||
* transaction.
|
||||
*
|
||||
* <p>If the payment RPC fails, we'll either show an error on a field, or in a
|
||||
* bloody butterbar. If it succeeds, then the backend will give us the
|
||||
* transaction ID assigned by Braintree, which we then render on a success
|
||||
* page.
|
||||
*
|
||||
* <p>The success page contains a "Make Another Payment" button which, if
|
||||
* clicked, will reset the state of this page back to the beginning.
|
||||
*
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {string} xsrfToken Security token to pass back to the server.
|
||||
* @constructor
|
||||
* @extends {registry.Component}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Payment = function(console, xsrfToken) {
|
||||
registry.registrar.Payment.base(this, 'constructor', console);
|
||||
|
||||
/**
|
||||
* Element in which this page is rendered.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.content_ = goog.dom.getRequiredElement('reg-content');
|
||||
|
||||
/**
|
||||
* Braintree API nonce token generated by the backend. This value is a
|
||||
* prerequisite to rendering the Braintree iframe.
|
||||
* @private {string}
|
||||
*/
|
||||
this.token_ = '';
|
||||
|
||||
/**
|
||||
* Braintree API nonce value for payment method selected by user.
|
||||
* @private {string}
|
||||
*/
|
||||
this.paymentMethodNonce_ = '';
|
||||
|
||||
/**
|
||||
* Currency drop-down widget in form.
|
||||
* @private {?registry.MenuButton}
|
||||
*/
|
||||
this.currencyMenu_ = null;
|
||||
|
||||
/**
|
||||
* XHR client to `RegistrarPaymentSetupAction`.
|
||||
* @private {!registry.Session.<!registry.rpc.PaymentSetup.Request,
|
||||
* !registry.rpc.PaymentSetup.Response>}
|
||||
* @const
|
||||
*/
|
||||
this.setupRpc_ =
|
||||
new registry.Session(new goog.Uri('/registrar-payment-setup'),
|
||||
xsrfToken,
|
||||
registry.Session.ContentType.JSON);
|
||||
|
||||
/**
|
||||
* XHR client to `RegistrarPaymentAction`.
|
||||
* @private {!registry.Session.<!registry.rpc.Payment.Request,
|
||||
* !registry.rpc.Payment.Response>}
|
||||
* @const
|
||||
*/
|
||||
this.paymentRpc_ =
|
||||
new registry.Session(new goog.Uri('/registrar-payment'),
|
||||
xsrfToken,
|
||||
registry.Session.ContentType.JSON);
|
||||
|
||||
this.listen(goog.global.window,
|
||||
goog.events.EventType.MESSAGE,
|
||||
this.onMessage_);
|
||||
};
|
||||
goog.inherits(registry.registrar.Payment, registry.Component);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Payment.prototype.bindToDom = function(id) {
|
||||
registry.registrar.Payment.base(this, 'bindToDom', id);
|
||||
if (!goog.isNull(goog.dom.getElement('reg-app-buttons'))) {
|
||||
goog.dom.removeChildren(goog.dom.getElement('reg-app-buttons'));
|
||||
}
|
||||
if (!registry.registrar.Payment.isBrowserSupported_()) {
|
||||
goog.soy.renderElement(this.content_,
|
||||
registry.soy.registrar.payment.unsupported);
|
||||
return;
|
||||
}
|
||||
goog.soy.renderElement(this.content_, registry.soy.registrar.console.loading);
|
||||
this.setupRpc_.sendXhrIo({}, goog.bind(this.onSetup_, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler invoked when we receive information from our backend, such as a
|
||||
* Braintree token, which is necessary for us to render the payment form.
|
||||
* @param {!registry.rpc.PaymentSetup.Response} response
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.onSetup_ = function(response) {
|
||||
if (response.status != 'SUCCESS') {
|
||||
if (response.message == 'not-using-cc-billing') {
|
||||
goog.soy.renderElement(this.content_,
|
||||
registry.soy.registrar.payment.notUsingCcBilling);
|
||||
} else {
|
||||
registry.forms.displayError(response.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var result = response.results[0];
|
||||
this.token_ = result.token;
|
||||
this.paymentMethodNonce_ = '';
|
||||
goog.soy.renderElement(this.content_,
|
||||
registry.soy.registrar.payment.form,
|
||||
result);
|
||||
this.listen(
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('reg-payment-form')),
|
||||
goog.events.EventType.SUBMIT,
|
||||
this.onSubmit_);
|
||||
this.currencyMenu_ =
|
||||
new registry.MenuButton(goog.dom.getRequiredElement('currency'));
|
||||
this.registerDisposable(this.currencyMenu_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler invoked when payment form is submitted.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.onSubmit_ = function(e) {
|
||||
e.preventDefault();
|
||||
this.submit_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Submits payment form.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.submit_ = function() {
|
||||
registry.forms.resetErrors();
|
||||
registry.registrar.Payment.setEnabled_(false);
|
||||
if (this.paymentMethodNonce_ == '') {
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.SUBMIT_REQUEST);
|
||||
return;
|
||||
}
|
||||
this.paymentRpc_.sendXhrIo(
|
||||
{
|
||||
amount: goog.dom.getRequiredElement('amount').value,
|
||||
currency: this.currencyMenu_.getValue(),
|
||||
paymentMethodNonce: this.paymentMethodNonce_
|
||||
},
|
||||
goog.bind(this.onPayment_, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback for backend payment RPC that issues the transaction.
|
||||
* @param {!registry.rpc.Payment.Response} response
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.onPayment_ = function(response) {
|
||||
registry.registrar.Payment.setEnabled_(true);
|
||||
if (response.status != 'SUCCESS') {
|
||||
registry.forms.displayError(response.message, response.field);
|
||||
return;
|
||||
}
|
||||
goog.soy.renderElement(this.content_,
|
||||
registry.soy.registrar.payment.success,
|
||||
response.results[0]);
|
||||
this.listenOnce(
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('reg-payment-again')),
|
||||
goog.events.EventType.CLICK,
|
||||
this.bindToDom);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler invoked when `brainframe.js` sends us a message.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.onMessage_ = function(e) {
|
||||
var msg = /** @type {!MessageEvent.<string>} */ (e.getBrowserEvent());
|
||||
var brainframe =
|
||||
goog.dom.getElementByClass(goog.getCssName('reg-payment-form-method'));
|
||||
if (brainframe == null ||
|
||||
msg.source != goog.dom.getFrameContentWindow(brainframe)) {
|
||||
return;
|
||||
}
|
||||
var data;
|
||||
try {
|
||||
data = /** @type {!Object} */ (JSON.parse(msg.data));
|
||||
} catch (ex) {
|
||||
// TODO(b/26876003): Figure out why it's possible that the Braintree iframe
|
||||
// is able to propagate messages up to our level.
|
||||
registry.util.log(ex, msg.source, msg.data);
|
||||
return;
|
||||
}
|
||||
switch (goog.object.get(data, 'type')) {
|
||||
case registry.registrar.BrainFrame.MessageType.TOKEN_REQUEST:
|
||||
goog.asserts.assert(this.token_ != '');
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.TOKEN_RESPONSE,
|
||||
'token', this.token_);
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.RESIZE_REQUEST:
|
||||
brainframe.height = goog.object.get(data, 'height');
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.READY:
|
||||
registry.registrar.Payment.setEnabled_(true);
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.SUBMIT_ERROR:
|
||||
registry.registrar.Payment.setEnabled_(true);
|
||||
registry.forms.displayError(goog.object.get(data, 'message'), 'method');
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.PAYMENT_METHOD:
|
||||
registry.registrar.Payment.setEnabled_(true);
|
||||
this.setPaymentMethod_(
|
||||
/** @type {!braintreepayments.PaymentMethod} */ (
|
||||
goog.object.get(data, 'method')));
|
||||
if (goog.object.get(data, 'submit')) {
|
||||
this.submit_();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw Error('Unexpected message: ' + msg.data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates UI to display selected payment method.
|
||||
*
|
||||
* <p>We remove the iframe from the page as soon as this happens, because the
|
||||
* UI would be busted otherwise. The Braintree UI for changing the payment
|
||||
* method (after it's been entered) does not appear to stop respond to submit
|
||||
* events. It also causes ugly scroll bars to appear inside the iframe.
|
||||
*
|
||||
* <p>This approach is also advantageous for screenshot testing. We do not want
|
||||
* our continuous integration testing system to talk to Braintree's servers. So
|
||||
* we mock out the brainframe with `integration-test-brainframe.html`
|
||||
* which <i>only</i> sends us a METHOD message, which we then render ourselves.
|
||||
*
|
||||
* @param {!braintreepayments.PaymentMethod} pm
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.setPaymentMethod_ = function(pm) {
|
||||
registry.forms.resetErrors();
|
||||
goog.dom.removeNode(
|
||||
goog.dom.getElementByClass(goog.getCssName('reg-payment-form-method')));
|
||||
var paymentMethodInfoBox =
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('reg-payment-form-method-info'));
|
||||
switch (pm.type) {
|
||||
case 'CreditCard':
|
||||
goog.soy.renderElement(
|
||||
paymentMethodInfoBox,
|
||||
registry.soy.registrar.payment.methodInfoCard,
|
||||
{cardType: pm.details.cardType, lastTwo: pm.details.lastTwo});
|
||||
break;
|
||||
case 'PayPalAccount':
|
||||
goog.soy.renderElement(
|
||||
paymentMethodInfoBox,
|
||||
registry.soy.registrar.payment.methodInfoPaypal,
|
||||
{email: pm.details.email});
|
||||
break;
|
||||
default:
|
||||
throw Error('Unknown payment method: ' + pm.type);
|
||||
}
|
||||
registry.util.setVisible(paymentMethodInfoBox, true);
|
||||
this.paymentMethodNonce_ = pm.nonce;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends message to brainframe.
|
||||
* @param {...*} var_args Passed along to `goog.object.create`.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.send_ = function(var_args) {
|
||||
goog.asserts.assert(arguments[0] == 'type');
|
||||
var brainframeWindow =
|
||||
goog.dom.getFrameContentWindow(
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('reg-payment-form-method')));
|
||||
// We send a string value to support older versions of IE.
|
||||
brainframeWindow.postMessage(
|
||||
goog.json.serialize(goog.object.create.apply(null, arguments)),
|
||||
'*');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables submit button and hides mini loading glyph.
|
||||
* @param {boolean} enabled
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.setEnabled_ = function(enabled) {
|
||||
registry.forms.setEnabled(
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('reg-payment-form-submit')), enabled);
|
||||
registry.util.setVisible(
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('reg-payment-form-loader')), !enabled);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns `true` if browser has all the features we need.
|
||||
* @return {boolean}
|
||||
* @private
|
||||
* @see "http://caniuse.com/#feat=dispatchevent"
|
||||
*/
|
||||
registry.registrar.Payment.isBrowserSupported_ = function() {
|
||||
// dispatchEvent is used by brainframe.js and is IE 9+.
|
||||
return goog.object.containsKey(
|
||||
goog.dom.createElement(goog.dom.TagName.FORM),
|
||||
'dispatchEvent');
|
||||
};
|
|
@ -12,7 +12,6 @@ java_library(
|
|||
"//java/google/registry/ui/css:registrar_dbg.css.js",
|
||||
],
|
||||
deps = [
|
||||
"//java/google/registry/braintree",
|
||||
"//java/google/registry/config",
|
||||
"//java/google/registry/export/sheet",
|
||||
"//java/google/registry/flows",
|
||||
|
|
|
@ -137,7 +137,6 @@ public final class ConsoleUiAction implements Runnable {
|
|||
Registrar.loadByClientIdCached(clientId), "Registrar %s does not exist", clientId);
|
||||
data.put("xsrfToken", xsrfTokenManager.generateToken(userAuthInfo.user().getEmail()));
|
||||
data.put("clientId", clientId);
|
||||
data.put("showPaymentLink", registrar.getBillingMethod() == Registrar.BillingMethod.BRAINTREE);
|
||||
data.put("requireFeeExtension", registrar.getPremiumPriceAckRequired());
|
||||
|
||||
String payload = TOFU_SUPPLIER.get()
|
||||
|
|
|
@ -1,350 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.base.Verify.verify;
|
||||
import static google.registry.security.JsonResponseHelper.Status.ERROR;
|
||||
import static google.registry.security.JsonResponseHelper.Status.SUCCESS;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import com.braintreegateway.BraintreeGateway;
|
||||
import com.braintreegateway.Result;
|
||||
import com.braintreegateway.Transaction;
|
||||
import com.braintreegateway.TransactionRequest;
|
||||
import com.braintreegateway.ValidationError;
|
||||
import com.braintreegateway.ValidationErrors;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.re2j.Pattern;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.JsonActionRunner.JsonAction;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.security.JsonResponseHelper;
|
||||
import google.registry.ui.forms.FormField;
|
||||
import google.registry.ui.forms.FormFieldException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.IllegalCurrencyException;
|
||||
import org.joda.money.Money;
|
||||
|
||||
/**
|
||||
* Action handling submission of customer payment form.
|
||||
*
|
||||
* <h3>Request Object</h3>
|
||||
*
|
||||
* <p>The request payload is a JSON object with the following fields:
|
||||
*
|
||||
* <dl>
|
||||
* <dt>amount
|
||||
* <dd>String containing a fixed point value representing the amount of money the registrar
|
||||
* customer wishes to send the registry. This amount is arbitrary and entered manually by the
|
||||
* customer in the payment form, as there is currently no integration with the billing system.
|
||||
* <dt>currency
|
||||
* <dd>String containing a three letter ISO currency code, which is used to look up the Braintree
|
||||
* merchant account ID to which payment should be posted.
|
||||
* <dt>paymentMethodNonce
|
||||
* <dd>UUID nonce string supplied by the Braintree JS SDK representing the selected payment
|
||||
* method.
|
||||
* </dl>
|
||||
*
|
||||
* <h3>Response Object</h3>
|
||||
*
|
||||
* <p>The response payload will be a JSON response object (as defined by {@link JsonResponseHelper})
|
||||
* which, if successful, will contain a single result object with the following fields:
|
||||
*
|
||||
* <dl>
|
||||
* <dt>id
|
||||
* <dd>String containing transaction ID returned by Braintree gateway.
|
||||
* <dt>formattedAmount
|
||||
* <dd>String containing amount paid, which can be displayed to the customer on a success page.
|
||||
* </dl>
|
||||
*
|
||||
* <p><b>Note:</b> These definitions corresponds to Closure Compiler extern {@code
|
||||
* registry.rpc.Payment} which must be updated should these definitions change.
|
||||
*
|
||||
* <h3>PCI Compliance</h3>
|
||||
*
|
||||
* <p>The request object will not contain credit card information, but rather a {@code
|
||||
* payment_method_nonce} field that's populated by the Braintree JS SDK iframe.
|
||||
*
|
||||
* @see RegistrarPaymentSetupAction
|
||||
*/
|
||||
@Action(
|
||||
path = "/registrar-payment",
|
||||
method = Action.Method.POST,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN
|
||||
)
|
||||
public final class RegistrarPaymentAction implements Runnable, JsonAction {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final FormField<String, BigDecimal> AMOUNT_FIELD =
|
||||
FormField.named("amount")
|
||||
.trimmed()
|
||||
.emptyToNull()
|
||||
.required()
|
||||
.matches(Pattern.compile("-?\\d+(?:\\.\\d+)?"), "Invalid number.")
|
||||
.transform(
|
||||
BigDecimal.class,
|
||||
value -> {
|
||||
BigDecimal result = new BigDecimal(value);
|
||||
if (result.signum() != 1) {
|
||||
throw new FormFieldException("Must be a positive number.");
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.build();
|
||||
|
||||
private static final FormField<String, CurrencyUnit> CURRENCY_FIELD =
|
||||
FormField.named("currency")
|
||||
.trimmed()
|
||||
.emptyToNull()
|
||||
.required()
|
||||
.matches(Pattern.compile("[A-Z]{3}"), "Invalid currency code.")
|
||||
.transform(
|
||||
CurrencyUnit.class,
|
||||
value -> {
|
||||
try {
|
||||
return CurrencyUnit.of(value);
|
||||
} catch (IllegalCurrencyException ignored) {
|
||||
throw new FormFieldException("Unknown ISO currency code.");
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
||||
private static final FormField<String, String> PAYMENT_METHOD_NONCE_FIELD =
|
||||
FormField.named("paymentMethodNonce")
|
||||
.trimmed()
|
||||
.emptyToNull()
|
||||
.required()
|
||||
.build();
|
||||
|
||||
@Inject HttpServletRequest request;
|
||||
@Inject BraintreeGateway braintreeGateway;
|
||||
@Inject JsonActionRunner jsonActionRunner;
|
||||
@Inject AuthResult authResult;
|
||||
@Inject SessionUtils sessionUtils;
|
||||
@Inject @Config("braintreeMerchantAccountIds") ImmutableMap<CurrencyUnit, String> accountIds;
|
||||
@Inject RegistrarPaymentAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jsonActionRunner.run(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> handleJsonRequest(Map<String, ?> json) {
|
||||
Registrar registrar = sessionUtils.getRegistrarForAuthResult(request, authResult);
|
||||
logger.atInfo().log("Processing payment: %s", json);
|
||||
String paymentMethodNonce;
|
||||
Money amount;
|
||||
String merchantAccountId;
|
||||
try {
|
||||
paymentMethodNonce = PAYMENT_METHOD_NONCE_FIELD.extractUntyped(json).get();
|
||||
try {
|
||||
amount = Money.of(
|
||||
CURRENCY_FIELD.extractUntyped(json).get(),
|
||||
AMOUNT_FIELD.extractUntyped(json).get());
|
||||
} catch (ArithmeticException e) {
|
||||
// This happens when amount has more precision than the currency allows, e.g. $3.141.
|
||||
throw new FormFieldException(AMOUNT_FIELD.name(), e.getMessage());
|
||||
}
|
||||
merchantAccountId = accountIds.get(amount.getCurrencyUnit());
|
||||
if (merchantAccountId == null) {
|
||||
throw new FormFieldException(CURRENCY_FIELD.name(), "Unsupported currency.");
|
||||
}
|
||||
} catch (FormFieldException e) {
|
||||
logger.atWarning().withCause(e).log("Form field error in RegistrarPaymentAction.");
|
||||
return JsonResponseHelper.createFormFieldError(e.getMessage(), e.getFieldName());
|
||||
}
|
||||
Result<Transaction> result =
|
||||
braintreeGateway.transaction().sale(
|
||||
new TransactionRequest()
|
||||
.amount(amount.getAmount())
|
||||
.paymentMethodNonce(paymentMethodNonce)
|
||||
.merchantAccountId(merchantAccountId)
|
||||
.customerId(registrar.getClientId())
|
||||
.options()
|
||||
.submitForSettlement(true)
|
||||
.done());
|
||||
if (result.isSuccess()) {
|
||||
return handleSuccessResponse(result.getTarget());
|
||||
} else if (result.getTransaction() != null) {
|
||||
Transaction transaction = result.getTransaction();
|
||||
switch (transaction.getStatus()) {
|
||||
case PROCESSOR_DECLINED:
|
||||
return handleProcessorDeclined(transaction);
|
||||
case SETTLEMENT_DECLINED:
|
||||
return handleSettlementDecline(transaction);
|
||||
case GATEWAY_REJECTED:
|
||||
return handleRejection(transaction);
|
||||
default:
|
||||
return handleMiscProcessorError(transaction);
|
||||
}
|
||||
} else {
|
||||
return handleValidationErrorResponse(result.getErrors());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a transaction success response.
|
||||
*
|
||||
* @see <a href="https://developers.braintreepayments.com/reference/response/transaction/java#success">
|
||||
* Braintree - Transaction - Success</a>
|
||||
* @see <a href="https://developers.braintreepayments.com/reference/general/statuses#transaction">
|
||||
* Braintree - Statuses - Transaction</a>
|
||||
*/
|
||||
private Map<String, Object> handleSuccessResponse(Transaction transaction) {
|
||||
// XXX: Currency scaling: https://github.com/braintree/braintree_java/issues/33
|
||||
Money amount =
|
||||
Money.of(CurrencyUnit.of(transaction.getCurrencyIsoCode()),
|
||||
transaction.getAmount().stripTrailingZeros());
|
||||
logger.atInfo().log(
|
||||
"Transaction for %s via %s %s with ID: %s",
|
||||
amount,
|
||||
transaction.getPaymentInstrumentType(), // e.g. credit_card, paypal_account
|
||||
transaction.getStatus(), // e.g. SUBMITTED_FOR_SETTLEMENT
|
||||
transaction.getId());
|
||||
return JsonResponseHelper
|
||||
.create(SUCCESS, "Payment processed successfully", asList(
|
||||
ImmutableMap.of(
|
||||
"id", transaction.getId(),
|
||||
"formattedAmount", formatMoney(amount))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a processor declined response.
|
||||
*
|
||||
* <p>This happens when the customer's bank blocks the transaction.
|
||||
*
|
||||
* @see <a href="https://developers.braintreepayments.com/reference/response/transaction/java#processor-declined">
|
||||
* Braintree - Transaction - Processor declined</a>
|
||||
* @see <a href="https://articles.braintreepayments.com/control-panel/transactions/declines">
|
||||
* Braintree - Transactions/Declines</a>
|
||||
*/
|
||||
private Map<String, Object> handleProcessorDeclined(Transaction transaction) {
|
||||
logger.atWarning().log(
|
||||
"Processor declined: %s %s",
|
||||
transaction.getProcessorResponseCode(), transaction.getProcessorResponseText());
|
||||
return JsonResponseHelper.create(ERROR,
|
||||
"Payment declined: " + transaction.getProcessorResponseText());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a settlement declined response.
|
||||
*
|
||||
* <p>This is a very rare condition that, for all intents and purposes, means the same thing as a
|
||||
* processor declined response.
|
||||
*
|
||||
* @see <a href="https://developers.braintreepayments.com/reference/response/transaction/java#processor-settlement-declined">
|
||||
* Braintree - Transaction - Processor settlement declined</a>
|
||||
* @see <a href="https://articles.braintreepayments.com/control-panel/transactions/declines">
|
||||
* Braintree - Transactions/Declines</a>
|
||||
*/
|
||||
private Map<String, Object> handleSettlementDecline(Transaction transaction) {
|
||||
logger.atWarning().log(
|
||||
"Settlement declined: %s %s",
|
||||
transaction.getProcessorSettlementResponseCode(),
|
||||
transaction.getProcessorSettlementResponseText());
|
||||
return JsonResponseHelper.create(ERROR,
|
||||
"Payment declined: " + transaction.getProcessorSettlementResponseText());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a gateway rejection response.
|
||||
*
|
||||
* <p>This happens when a transaction is blocked due to settings we configured ourselves in the
|
||||
* Braintree control panel.
|
||||
*
|
||||
* @see <a href="https://developers.braintreepayments.com/reference/response/transaction/java#gateway-rejection">
|
||||
* Braintree - Transaction - Gateway rejection</a>
|
||||
* @see <a href="https://articles.braintreepayments.com/control-panel/transactions/gateway-rejections">
|
||||
* Braintree - Transactions/Gateway Rejections</a>
|
||||
* @see <a href="https://articles.braintreepayments.com/guides/fraud-tools/avs-cvv">
|
||||
* Braintree - Fruad Tools/Basic Fraud Tools - AVS and CVV rules</a>
|
||||
* @see <a href="https://articles.braintreepayments.com/guides/fraud-tools/overview">
|
||||
* Braintree - Fraud Tools/Overview</a>
|
||||
*/
|
||||
private Map<String, Object> handleRejection(Transaction transaction) {
|
||||
logger.atWarning().log("Gateway rejection: %s", transaction.getGatewayRejectionReason());
|
||||
switch (transaction.getGatewayRejectionReason()) {
|
||||
case DUPLICATE:
|
||||
return JsonResponseHelper.create(ERROR, "Payment rejected: Possible duplicate.");
|
||||
case AVS:
|
||||
return JsonResponseHelper.create(ERROR, "Payment rejected: Invalid address.");
|
||||
case CVV:
|
||||
return JsonResponseHelper.create(ERROR, "Payment rejected: Invalid CVV code.");
|
||||
case AVS_AND_CVV:
|
||||
return JsonResponseHelper.create(ERROR, "Payment rejected: Invalid address and CVV code.");
|
||||
case FRAUD:
|
||||
return JsonResponseHelper.create(ERROR,
|
||||
"Our merchant gateway suspects this payment of fraud. Please contact support.");
|
||||
default:
|
||||
return JsonResponseHelper.create(ERROR,
|
||||
"Payment rejected: " + transaction.getGatewayRejectionReason());
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles a miscellaneous transaction processing error response. */
|
||||
private Map<String, Object> handleMiscProcessorError(Transaction transaction) {
|
||||
logger.atWarning().log(
|
||||
"Error processing transaction: %s %s %s",
|
||||
transaction.getStatus(),
|
||||
transaction.getProcessorResponseCode(),
|
||||
transaction.getProcessorResponseText());
|
||||
return JsonResponseHelper.create(ERROR,
|
||||
"Payment failure: "
|
||||
+ firstNonNull(
|
||||
emptyToNull(transaction.getProcessorResponseText()),
|
||||
transaction.getStatus().toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a validation error response from Braintree.
|
||||
*
|
||||
* @see <a href="https://developers.braintreepayments.com/reference/response/transaction/java#validation-errors">
|
||||
* Braintree - Transaction - Validation errors</a>
|
||||
* @see <a href="https://developers.braintreepayments.com/reference/general/validation-errors/all/java">
|
||||
* Braintree - Validation Errors/All</a>
|
||||
*/
|
||||
private Map<String, Object> handleValidationErrorResponse(ValidationErrors validationErrors) {
|
||||
List<ValidationError> errors = validationErrors.getAllDeepValidationErrors();
|
||||
verify(!errors.isEmpty(), "Payment failed but validation error list was empty");
|
||||
for (ValidationError error : errors) {
|
||||
logger.atWarning().log(
|
||||
"Payment validation failed on field: %s\nCode: %s\nMessage: %s",
|
||||
error.getAttribute(), error.getCode(), error.getMessage());
|
||||
}
|
||||
return JsonResponseHelper
|
||||
.createFormFieldError(errors.get(0).getMessage(), errors.get(0).getAttribute());
|
||||
}
|
||||
|
||||
private static String formatMoney(Money amount) {
|
||||
String symbol = amount.getCurrencyUnit().getSymbol(Locale.US);
|
||||
BigDecimal number = amount.getAmount().setScale(amount.getCurrencyUnit().getDecimalPlaces());
|
||||
return symbol.length() == 1 ? symbol + number : amount.toString();
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.security.JsonResponseHelper.Status.ERROR;
|
||||
import static google.registry.security.JsonResponseHelper.Status.SUCCESS;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import com.braintreegateway.BraintreeGateway;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.braintree.BraintreeRegistrarSyncer;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.JsonActionRunner.JsonAction;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.security.JsonResponseHelper;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* Action returning information needed to render payment form in browser.
|
||||
*
|
||||
* <h3>Request Object</h3>
|
||||
*
|
||||
* <p>The request payload must be an empty JSON object.
|
||||
*
|
||||
* <h3>Response Object</h3>
|
||||
*
|
||||
* <p>The response payload will be a JSON response object (as defined by {@link JsonResponseHelper})
|
||||
* containing a single result object with the following fields:
|
||||
*
|
||||
* <dl>
|
||||
* <dt>brainframe
|
||||
* <dd>URL for iframe that loads Braintree payment method selector.
|
||||
* <dt>token
|
||||
* <dd>Nonce string obtained from the Braintree API which is needed by the Braintree JS SDK.
|
||||
* <dt>currencies
|
||||
* <dd>Array of strings, each containing a three letter currency code, which should be displayed
|
||||
* to the customer in a drop-down field. This will be all currencies for which a Braintree
|
||||
* merchant account exists. A currency will even be displayed if no TLD is enabled on the
|
||||
* customer account that bills in that currency.
|
||||
* </dl>
|
||||
*
|
||||
* <p><b>Note:</b> These definitions corresponds to Closure Compiler extern {@code
|
||||
* registry.rpc.PaymentSetup} which must be updated should these definitions change.
|
||||
*
|
||||
* @see RegistrarPaymentAction
|
||||
* @see <a
|
||||
* href="https://developers.braintreepayments.com/start/hello-server/java#generate-a-client-token">Generate
|
||||
* a client token</a>
|
||||
*/
|
||||
@Action(
|
||||
path = "/registrar-payment-setup",
|
||||
method = Action.Method.POST,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN
|
||||
)
|
||||
public final class RegistrarPaymentSetupAction implements Runnable, JsonAction {
|
||||
|
||||
@Inject HttpServletRequest request;
|
||||
@Inject BraintreeGateway braintreeGateway;
|
||||
@Inject BraintreeRegistrarSyncer customerSyncer;
|
||||
@Inject JsonActionRunner jsonActionRunner;
|
||||
@Inject AuthResult authResult;
|
||||
@Inject SessionUtils sessionUtils;
|
||||
@Inject @Config("brainframe") String brainframe;
|
||||
@Inject @Config("braintreeMerchantAccountIds") ImmutableMap<CurrencyUnit, String> accountIds;
|
||||
@Inject RegistrarPaymentSetupAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jsonActionRunner.run(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> handleJsonRequest(Map<String, ?> json) {
|
||||
Registrar registrar = sessionUtils.getRegistrarForAuthResult(request, authResult);
|
||||
|
||||
if (!json.isEmpty()) {
|
||||
return JsonResponseHelper.create(ERROR, "JSON request object must be empty");
|
||||
}
|
||||
|
||||
// payment.js is hard-coded to display a specific SOY error template for certain error messages.
|
||||
if (registrar.getBillingMethod() != Registrar.BillingMethod.BRAINTREE) {
|
||||
// Registrar needs to contact support to have their billing bit flipped.
|
||||
return JsonResponseHelper.create(ERROR, "not-using-cc-billing");
|
||||
}
|
||||
|
||||
// In order to set the customerId field on the payment, the customer must exist.
|
||||
customerSyncer.sync(registrar);
|
||||
|
||||
return JsonResponseHelper.create(
|
||||
SUCCESS,
|
||||
"Success",
|
||||
asList(
|
||||
ImmutableMap.of(
|
||||
"brainframe", brainframe,
|
||||
"token", braintreeGateway.clientToken().generate(),
|
||||
"currencies",
|
||||
accountIds
|
||||
.keySet()
|
||||
.stream()
|
||||
.map(Object::toString)
|
||||
.collect(toImmutableList()))));
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@
|
|||
{@param clientId: string} /** Registrar client identifier. */
|
||||
{@param username: string} /** Arbitrary username to display. */
|
||||
{@param logoutUrl: string} /** Generated URL for logging out of Google. */
|
||||
{@param showPaymentLink: bool}
|
||||
{@param productName: string} /** Name to display for this software product. */
|
||||
{@param integrationEmail: string}
|
||||
{@param supportEmail: string}
|
||||
|
@ -76,17 +75,12 @@
|
|||
|
||||
/** Sidebar nav. Ids on each elt for testing only. */
|
||||
{template .navbar_ visibility="private"}
|
||||
{@param showPaymentLink: bool}
|
||||
<div id="reg-nav" class="{css('kd-content-sidebar')}">
|
||||
<ul id="reg-navlist">
|
||||
<li>
|
||||
<a href="/registrar#">Home</a>
|
||||
<li>
|
||||
<a href="/registrar#resources">Resources & billing</a>
|
||||
{if $showPaymentLink}
|
||||
<li>
|
||||
<a href="/registrar#payment">Pay invoice</a>
|
||||
{/if}
|
||||
<li>
|
||||
<ul>
|
||||
<span class="{css('reg-navlist-sub')}">Settings</span>
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
{namespace registry.soy.registrar.payment}
|
||||
|
||||
|
||||
/** Page allowing registrar to send registry money. */
|
||||
{template .form}
|
||||
{@param currencies: list<string>} /** Currencies in which customer can remit payment. */
|
||||
{@param brainframe: uri} /** Location of Braintree iframe sandbox iframe HTML. */
|
||||
<div class="{css('reg-payment')}">
|
||||
<h1>Make a Payment</h1>
|
||||
<p>
|
||||
Please use the form below to pay your monthly invoice by credit card.
|
||||
<p>
|
||||
The bill you received from the registry should list an outstanding balance
|
||||
for each currency. If you hold an outstanding balance in multiple currencies,
|
||||
this form should be filled out and submitted separately for each one.
|
||||
<form method="post" action="#" class="{css('reg-payment-form')}">
|
||||
<fieldset>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="amount">Amount</label>
|
||||
<input type="text"
|
||||
id="amount"
|
||||
name="amount"
|
||||
autocomplete="off"
|
||||
autofocus>
|
||||
<li>
|
||||
<label>Currency</label>
|
||||
{call registry.soy.forms.menuButton}
|
||||
{param id: 'currency' /}
|
||||
{param selected: $currencies[0] /}
|
||||
{param items: $currencies /}
|
||||
{/call}
|
||||
<li>
|
||||
<label>Payment Method</label>
|
||||
<iframe src="{$brainframe |blessStringAsTrustedResourceUrlForLegacy}"
|
||||
id="method"
|
||||
class="{css('reg-payment-form-method')}"
|
||||
height="0"
|
||||
width="100%"
|
||||
frameBorder="0"
|
||||
scrolling="no"></iframe>
|
||||
<div class="{css('reg-payment-form-method-info')} {css('hidden')}"></div>
|
||||
</ul>
|
||||
<input type="submit" value="Submit Payment"
|
||||
class="{css('reg-payment-form-submit')}{sp}
|
||||
{css('kd-button')}{sp}
|
||||
{css('kd-button-submit')}{sp}
|
||||
{css('disabled')}">
|
||||
<img alt="[Processing...]"
|
||||
class="{css('reg-payment-form-loader')}"
|
||||
src="/assets/images/loader1x.gif"
|
||||
width="22" height="22">
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
{/template}
|
||||
|
||||
|
||||
/** Page allowing registrar to send registry money. */
|
||||
{template .success}
|
||||
{@param id: string} /** Transaction ID from payment gateway. */
|
||||
{@param formattedAmount: string} /** Amount in which payment was made. */
|
||||
<div class="{css('reg-payment')}">
|
||||
<h1>Payment Processed</h1>
|
||||
<p>
|
||||
Your payment of {$formattedAmount} was successfully processed with
|
||||
the Transaction ID {$id}.
|
||||
<p>
|
||||
<button class="{css('reg-payment-again')} {css('kd-button')} {css('kd-button-submit')}">
|
||||
Make Another Payment
|
||||
</button>
|
||||
</div>
|
||||
{/template}
|
||||
|
||||
|
||||
/** Information about credit card payment method, once it's been entered. */
|
||||
{template .methodInfoCard}
|
||||
{@param cardType: string} /** Type of credit card, e.g. Visa. */
|
||||
{@param lastTwo: string} /** Last two digits of credit card number. */
|
||||
{if $cardType == 'Amex'}
|
||||
American Express: xxxx xxxxxx xxx{$lastTwo}
|
||||
{else}
|
||||
{$cardType}: xxxx xxxx xxxx xx{$lastTwo}
|
||||
{/if}
|
||||
{/template}
|
||||
|
||||
|
||||
/** Information about PayPal payment method, once it's been entered. */
|
||||
{template .methodInfoPaypal}
|
||||
{@param email: string} /** Email address associated with PayPal account. */
|
||||
PayPal: {$email}
|
||||
{/template}
|
||||
|
||||
|
||||
/** Page used to block browsers without necessary features. */
|
||||
{template .unsupported}
|
||||
<div class="{css('reg-payment')}">
|
||||
<img alt="[Crying Android]"
|
||||
class="{css('reg-cryingAndroid')}"
|
||||
src="/assets/images/android_sad.png"
|
||||
width="183"
|
||||
height="275">
|
||||
<h1>Browser Unsupported</h1>
|
||||
<p>
|
||||
The Payment page requires features which are not present in your
|
||||
browser. Please use one of the following compatible browsers:
|
||||
<ul class="{css('reg-bullets')}">
|
||||
<li>Chrome
|
||||
<li>Android
|
||||
<li>Safari
|
||||
<li>Firefox
|
||||
<li>IE 10+ or Edge
|
||||
</ul>
|
||||
</div>
|
||||
{/template}
|
||||
|
||||
|
||||
/** Page indicating customer is not on credit card billing terms. */
|
||||
{template .notUsingCcBilling}
|
||||
<div class="{css('reg-payment')}">
|
||||
<h1>Payment Page Disabled</h1>
|
||||
<p>
|
||||
Your customer account is not on credit card billing terms. Please{sp}
|
||||
<a href="/registrar#contact-us">contact support</a> to have your account
|
||||
switched over.
|
||||
</div>
|
||||
{/template}
|
Loading…
Add table
Add a link
Reference in a new issue