mirror of
https://github.com/google/nomulus.git
synced 2025-05-13 07:57:13 +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
|
@ -1,20 +0,0 @@
|
||||||
package(
|
|
||||||
default_visibility = ["//visibility:public"],
|
|
||||||
)
|
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
|
||||||
|
|
||||||
java_library(
|
|
||||||
name = "braintree",
|
|
||||||
srcs = glob(["*.java"]),
|
|
||||||
deps = [
|
|
||||||
"//java/google/registry/config",
|
|
||||||
"//java/google/registry/keyring/api",
|
|
||||||
"//java/google/registry/model",
|
|
||||||
"@com_braintreepayments_gateway_braintree_java",
|
|
||||||
"@com_google_code_findbugs_jsr305",
|
|
||||||
"@com_google_dagger",
|
|
||||||
"@com_google_guava",
|
|
||||||
"@javax_inject",
|
|
||||||
],
|
|
||||||
)
|
|
|
@ -1,44 +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.braintree;
|
|
||||||
|
|
||||||
import com.braintreegateway.BraintreeGateway;
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
import google.registry.config.RegistryConfig.Config;
|
|
||||||
import google.registry.config.RegistryEnvironment;
|
|
||||||
import google.registry.keyring.api.KeyModule.Key;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
/** Dagger module for Braintree Payments API. */
|
|
||||||
@Module
|
|
||||||
public final class BraintreeModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
static BraintreeGateway provideBraintreeGateway(
|
|
||||||
RegistryEnvironment environment,
|
|
||||||
@Config("braintreeMerchantId") String merchantId,
|
|
||||||
@Config("braintreePublicKey") String publicKey,
|
|
||||||
@Key("braintreePrivateKey") String privateKey) {
|
|
||||||
return new BraintreeGateway(
|
|
||||||
environment == RegistryEnvironment.PRODUCTION
|
|
||||||
? com.braintreegateway.Environment.PRODUCTION
|
|
||||||
: com.braintreegateway.Environment.SANDBOX,
|
|
||||||
merchantId,
|
|
||||||
publicKey,
|
|
||||||
privateKey);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,103 +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.braintree;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.base.Verify.verify;
|
|
||||||
|
|
||||||
import com.braintreegateway.BraintreeGateway;
|
|
||||||
import com.braintreegateway.Customer;
|
|
||||||
import com.braintreegateway.CustomerRequest;
|
|
||||||
import com.braintreegateway.Result;
|
|
||||||
import com.braintreegateway.exceptions.NotFoundException;
|
|
||||||
import com.google.common.base.VerifyException;
|
|
||||||
import google.registry.model.registrar.Registrar;
|
|
||||||
import google.registry.model.registrar.RegistrarContact;
|
|
||||||
import java.util.Optional;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
/** Helper for creating Braintree customer entries for registrars. */
|
|
||||||
public class BraintreeRegistrarSyncer {
|
|
||||||
|
|
||||||
private final BraintreeGateway braintree;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
BraintreeRegistrarSyncer(BraintreeGateway braintreeGateway) {
|
|
||||||
this.braintree = braintreeGateway;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Syncs {@code registrar} with Braintree customer entry, creating it if one doesn't exist.
|
|
||||||
*
|
|
||||||
* <p>The customer ID will be the same as {@link Registrar#getClientId()}.
|
|
||||||
*
|
|
||||||
* <p>Creating a customer object in Braintree's database is a necessary step in order to associate
|
|
||||||
* a payment with a registrar. The transaction will fail if the customer object doesn't exist.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if {@code registrar} is not using BRAINTREE billing
|
|
||||||
* @throws VerifyException if the Braintree API returned a failure response
|
|
||||||
*/
|
|
||||||
public void sync(Registrar registrar) {
|
|
||||||
String id = registrar.getClientId();
|
|
||||||
checkArgument(registrar.getBillingMethod() == Registrar.BillingMethod.BRAINTREE,
|
|
||||||
"Registrar (%s) billing method (%s) is not BRAINTREE", id, registrar.getBillingMethod());
|
|
||||||
CustomerRequest request = createRequest(registrar);
|
|
||||||
Result<Customer> result;
|
|
||||||
if (doesCustomerExist(id)) {
|
|
||||||
result = braintree.customer().update(id, request);
|
|
||||||
} else {
|
|
||||||
result = braintree.customer().create(request);
|
|
||||||
}
|
|
||||||
verify(result.isSuccess(),
|
|
||||||
"Failed to sync registrar (%s) to braintree customer: %s", id, result.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
private CustomerRequest createRequest(Registrar registrar) {
|
|
||||||
CustomerRequest result =
|
|
||||||
new CustomerRequest()
|
|
||||||
.id(registrar.getClientId())
|
|
||||||
.customerId(registrar.getClientId())
|
|
||||||
.company(registrar.getRegistrarName());
|
|
||||||
Optional<RegistrarContact> contact = getBillingContact(registrar);
|
|
||||||
if (contact.isPresent()) {
|
|
||||||
result.email(contact.get().getEmailAddress());
|
|
||||||
result.phone(contact.get().getPhoneNumber());
|
|
||||||
result.fax(contact.get().getFaxNumber());
|
|
||||||
} else {
|
|
||||||
result.email(registrar.getEmailAddress());
|
|
||||||
result.phone(registrar.getPhoneNumber());
|
|
||||||
result.fax(registrar.getFaxNumber());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<RegistrarContact> getBillingContact(Registrar registrar) {
|
|
||||||
for (RegistrarContact contact : registrar.getContacts()) {
|
|
||||||
if (contact.getTypes().contains(RegistrarContact.Type.BILLING)) {
|
|
||||||
return Optional.of(contact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean doesCustomerExist(String id) {
|
|
||||||
try {
|
|
||||||
braintree.customer().find(id);
|
|
||||||
return true;
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +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.
|
|
||||||
|
|
||||||
/** Braintree payment gateway utilities. */
|
|
||||||
@javax.annotation.ParametersAreNonnullByDefault
|
|
||||||
package google.registry.braintree;
|
|
|
@ -32,14 +32,11 @@ import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Qualifier;
|
import javax.inject.Qualifier;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import org.joda.money.CurrencyUnit;
|
|
||||||
import org.joda.time.DateTimeConstants;
|
import org.joda.time.DateTimeConstants;
|
||||||
import org.joda.time.Duration;
|
import org.joda.time.Duration;
|
||||||
|
|
||||||
|
@ -887,51 +884,6 @@ public final class RegistryConfig {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns Braintree Merchant Account IDs for each supported currency.
|
|
||||||
*
|
|
||||||
* @see google.registry.ui.server.registrar.RegistrarPaymentAction
|
|
||||||
* @see google.registry.ui.server.registrar.RegistrarPaymentSetupAction
|
|
||||||
*/
|
|
||||||
@Provides
|
|
||||||
@Config("braintreeMerchantAccountIds")
|
|
||||||
public static ImmutableMap<CurrencyUnit, String> provideBraintreeMerchantAccountId(
|
|
||||||
RegistryConfigSettings config) {
|
|
||||||
Map<String, String> merchantAccountIds = config.braintree.merchantAccountIdsMap;
|
|
||||||
ImmutableMap.Builder<CurrencyUnit, String> builder = new ImmutableMap.Builder<>();
|
|
||||||
for (Entry<String, String> entry : merchantAccountIds.entrySet()) {
|
|
||||||
builder.put(CurrencyUnit.of(entry.getKey()), entry.getValue());
|
|
||||||
}
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns Braintree Merchant ID of Registry, used for accessing Braintree API.
|
|
||||||
*
|
|
||||||
* <p>This is a base32 value copied from the Braintree website.
|
|
||||||
*
|
|
||||||
* @see google.registry.braintree.BraintreeModule
|
|
||||||
*/
|
|
||||||
@Provides
|
|
||||||
@Config("braintreeMerchantId")
|
|
||||||
public static String provideBraintreeMerchantId(RegistryConfigSettings config) {
|
|
||||||
return config.braintree.merchantId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns Braintree Public Key of Registry, used for accessing Braintree API.
|
|
||||||
*
|
|
||||||
* <p>This is a base32 value copied from the Braintree website.
|
|
||||||
*
|
|
||||||
* @see google.registry.braintree.BraintreeModule
|
|
||||||
* @see google.registry.keyring.api.Keyring#getBraintreePrivateKey()
|
|
||||||
*/
|
|
||||||
@Provides
|
|
||||||
@Config("braintreePublicKey")
|
|
||||||
public static String provideBraintreePublicKey(RegistryConfigSettings config) {
|
|
||||||
return config.braintree.publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disclaimer displayed at the end of WHOIS query results.
|
* Disclaimer displayed at the end of WHOIS query results.
|
||||||
*
|
*
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
package google.registry.config;
|
package google.registry.config;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/** The POJO that YAML config files are deserialized into. */
|
/** The POJO that YAML config files are deserialized into. */
|
||||||
public class RegistryConfigSettings {
|
public class RegistryConfigSettings {
|
||||||
|
@ -33,7 +32,6 @@ public class RegistryConfigSettings {
|
||||||
public RegistrarConsole registrarConsole;
|
public RegistrarConsole registrarConsole;
|
||||||
public Monitoring monitoring;
|
public Monitoring monitoring;
|
||||||
public Misc misc;
|
public Misc misc;
|
||||||
public Braintree braintree;
|
|
||||||
public Kms kms;
|
public Kms kms;
|
||||||
public RegistryTool registryTool;
|
public RegistryTool registryTool;
|
||||||
|
|
||||||
|
@ -158,13 +156,6 @@ public class RegistryConfigSettings {
|
||||||
public int asyncDeleteDelaySeconds;
|
public int asyncDeleteDelaySeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Configuration for Braintree credit card payment processing. */
|
|
||||||
public static class Braintree {
|
|
||||||
public String merchantId;
|
|
||||||
public String publicKey;
|
|
||||||
public Map<String, String> merchantAccountIdsMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Configuration options for the registry tool. */
|
/** Configuration options for the registry tool. */
|
||||||
public static class RegistryTool {
|
public static class RegistryTool {
|
||||||
public String clientSecretFilename;
|
public String clientSecretFilename;
|
||||||
|
|
|
@ -246,21 +246,6 @@ misc:
|
||||||
# hosts from being used on domains.
|
# hosts from being used on domains.
|
||||||
asyncDeleteDelaySeconds: 90
|
asyncDeleteDelaySeconds: 90
|
||||||
|
|
||||||
# Braintree is a credit card payment processor that is used on the registrar
|
|
||||||
# console to allow registrars to pay their invoices.
|
|
||||||
braintree:
|
|
||||||
# Merchant ID of the Braintree account.
|
|
||||||
merchantId: example
|
|
||||||
|
|
||||||
# Public key used for accessing Braintree API (this is found on their site).
|
|
||||||
publicKey: example
|
|
||||||
|
|
||||||
# A map of JODA Money CurrencyUnits, specified in three letter ISO-4217
|
|
||||||
# format, to Braintree account IDs (each account is limited to a single
|
|
||||||
# currency). For example, one entry might be:
|
|
||||||
# USD: accountIdUsingUSD
|
|
||||||
merchantAccountIdsMap: {}
|
|
||||||
|
|
||||||
kms:
|
kms:
|
||||||
# GCP project containing the KMS keyring. Should only be used for KMS in
|
# GCP project containing the KMS keyring. Should only be used for KMS in
|
||||||
# order to keep a simple locked down IAM configuration.
|
# order to keep a simple locked down IAM configuration.
|
||||||
|
|
|
@ -57,15 +57,5 @@ registrarConsole:
|
||||||
misc:
|
misc:
|
||||||
sheetExportId: placeholder
|
sheetExportId: placeholder
|
||||||
|
|
||||||
# You only need to specify this section if using Braintree.
|
|
||||||
braintree:
|
|
||||||
merchantId: placeholder
|
|
||||||
publicKey: placeholder
|
|
||||||
# Only include currencies that you use.
|
|
||||||
merchantAccountIdsMap:
|
|
||||||
EUR: placeholder
|
|
||||||
JPY: placeholder
|
|
||||||
USD: placeholder
|
|
||||||
|
|
||||||
kms:
|
kms:
|
||||||
projectId: placeholder
|
projectId: placeholder
|
||||||
|
|
|
@ -19,8 +19,3 @@ caching:
|
||||||
staticPremiumListMaxCachedEntries: 50
|
staticPremiumListMaxCachedEntries: 50
|
||||||
eppResourceCachingEnabled: true
|
eppResourceCachingEnabled: true
|
||||||
eppResourceCachingSeconds: 0
|
eppResourceCachingSeconds: 0
|
||||||
|
|
||||||
braintree:
|
|
||||||
merchantAccountIdsMap:
|
|
||||||
USD: accountIdUsd
|
|
||||||
JPY: accountIdJpy
|
|
||||||
|
|
|
@ -31,18 +31,6 @@
|
||||||
<url-pattern>/registrar</url-pattern>
|
<url-pattern>/registrar</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
|
|
||||||
<!-- Registrar Braintree payment form setup. -->
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>frontend-servlet</servlet-name>
|
|
||||||
<url-pattern>/registrar-payment-setup</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
<!-- Registrar Braintree payment. -->
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>frontend-servlet</servlet-name>
|
|
||||||
<url-pattern>/registrar-payment</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
<!-- Registrar Self-serve Settings. -->
|
<!-- Registrar Self-serve Settings. -->
|
||||||
<servlet-mapping>
|
<servlet-mapping>
|
||||||
<servlet-name>frontend-servlet</servlet-name>
|
<servlet-name>frontend-servlet</servlet-name>
|
||||||
|
@ -73,7 +61,6 @@
|
||||||
<!-- TODO(b/26776367): Move these files to /assets/sources. -->
|
<!-- TODO(b/26776367): Move these files to /assets/sources. -->
|
||||||
<url-pattern>/assets/js/registrar_bin.js.map</url-pattern>
|
<url-pattern>/assets/js/registrar_bin.js.map</url-pattern>
|
||||||
<url-pattern>/assets/js/registrar_dbg.js</url-pattern>
|
<url-pattern>/assets/js/registrar_dbg.js</url-pattern>
|
||||||
<url-pattern>/assets/js/brain_bin.js.map</url-pattern>
|
|
||||||
<url-pattern>/assets/css/registrar_dbg.css</url-pattern>
|
<url-pattern>/assets/css/registrar_dbg.css</url-pattern>
|
||||||
|
|
||||||
</web-resource-collection>
|
</web-resource-collection>
|
||||||
|
|
|
@ -61,7 +61,6 @@
|
||||||
<!-- TODO(b/26776367): Move these files to /assets/sources. -->
|
<!-- TODO(b/26776367): Move these files to /assets/sources. -->
|
||||||
<url-pattern>/assets/js/registrar_bin.js.map</url-pattern>
|
<url-pattern>/assets/js/registrar_bin.js.map</url-pattern>
|
||||||
<url-pattern>/assets/js/registrar_dbg.js</url-pattern>
|
<url-pattern>/assets/js/registrar_dbg.js</url-pattern>
|
||||||
<url-pattern>/assets/js/brain_bin.js.map</url-pattern>
|
|
||||||
<url-pattern>/assets/css/registrar_dbg.css</url-pattern>
|
<url-pattern>/assets/css/registrar_dbg.css</url-pattern>
|
||||||
|
|
||||||
</web-resource-collection>
|
</web-resource-collection>
|
||||||
|
|
|
@ -109,7 +109,6 @@ public final class DummyKeyringModule {
|
||||||
"not a real login",
|
"not a real login",
|
||||||
"not a real password",
|
"not a real password",
|
||||||
"not a real login",
|
"not a real login",
|
||||||
"not a real credential",
|
"not a real credential");
|
||||||
"not a real key");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ public final class InMemoryKeyring implements Keyring {
|
||||||
private final String marksdbLordnPassword;
|
private final String marksdbLordnPassword;
|
||||||
private final String marksdbSmdrlLogin;
|
private final String marksdbSmdrlLogin;
|
||||||
private final String jsonCredential;
|
private final String jsonCredential;
|
||||||
private final String braintreePrivateKey;
|
|
||||||
|
|
||||||
public InMemoryKeyring(
|
public InMemoryKeyring(
|
||||||
PGPKeyPair rdeStagingKey,
|
PGPKeyPair rdeStagingKey,
|
||||||
|
@ -52,8 +51,7 @@ public final class InMemoryKeyring implements Keyring {
|
||||||
String marksdbDnlLogin,
|
String marksdbDnlLogin,
|
||||||
String marksdbLordnPassword,
|
String marksdbLordnPassword,
|
||||||
String marksdbSmdrlLogin,
|
String marksdbSmdrlLogin,
|
||||||
String jsonCredential,
|
String jsonCredential) {
|
||||||
String braintreePrivateKey) {
|
|
||||||
checkArgument(PgpHelper.isSigningKey(rdeSigningKey.getPublicKey()),
|
checkArgument(PgpHelper.isSigningKey(rdeSigningKey.getPublicKey()),
|
||||||
"RDE signing key must support signing: %s", rdeSigningKey.getKeyID());
|
"RDE signing key must support signing: %s", rdeSigningKey.getKeyID());
|
||||||
checkArgument(rdeStagingKey.getPublicKey().isEncryptionKey(),
|
checkArgument(rdeStagingKey.getPublicKey().isEncryptionKey(),
|
||||||
|
@ -76,7 +74,6 @@ public final class InMemoryKeyring implements Keyring {
|
||||||
this.marksdbLordnPassword = checkNotNull(marksdbLordnPassword, "marksdbLordnPassword");
|
this.marksdbLordnPassword = checkNotNull(marksdbLordnPassword, "marksdbLordnPassword");
|
||||||
this.marksdbSmdrlLogin = checkNotNull(marksdbSmdrlLogin, "marksdbSmdrlLogin");
|
this.marksdbSmdrlLogin = checkNotNull(marksdbSmdrlLogin, "marksdbSmdrlLogin");
|
||||||
this.jsonCredential = checkNotNull(jsonCredential, "jsonCredential");
|
this.jsonCredential = checkNotNull(jsonCredential, "jsonCredential");
|
||||||
this.braintreePrivateKey = checkNotNull(braintreePrivateKey, "braintreePrivateKey");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -144,11 +141,6 @@ public final class InMemoryKeyring implements Keyring {
|
||||||
return jsonCredential;
|
return jsonCredential;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getBraintreePrivateKey() {
|
|
||||||
return braintreePrivateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Does nothing. */
|
/** Does nothing. */
|
||||||
@Override
|
@Override
|
||||||
public void close() {}
|
public void close() {}
|
||||||
|
|
|
@ -120,10 +120,4 @@ public final class KeyModule {
|
||||||
static String provideJsonCredential(Keyring keyring) {
|
static String provideJsonCredential(Keyring keyring) {
|
||||||
return keyring.getJsonCredential();
|
return keyring.getJsonCredential();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Key("braintreePrivateKey")
|
|
||||||
static String provideBraintreePrivateKey(Keyring keyring) {
|
|
||||||
return keyring.getBraintreePrivateKey();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,15 +149,6 @@ public interface Keyring extends AutoCloseable {
|
||||||
*/
|
*/
|
||||||
String getJsonCredential();
|
String getJsonCredential();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns Braintree API private key for Registry.
|
|
||||||
*
|
|
||||||
* <p>This is a base32 value copied from the Braintree website.
|
|
||||||
*
|
|
||||||
* @see google.registry.config.RegistryConfig.ConfigModule#provideBraintreePublicKey
|
|
||||||
*/
|
|
||||||
String getBraintreePrivateKey();
|
|
||||||
|
|
||||||
// Don't throw so try-with-resources works better.
|
// Don't throw so try-with-resources works better.
|
||||||
@Override
|
@Override
|
||||||
void close();
|
void close();
|
||||||
|
|
|
@ -64,7 +64,6 @@ public class KmsKeyring implements Keyring {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum StringKeyLabel {
|
enum StringKeyLabel {
|
||||||
BRAINTREE_PRIVATE_KEY_STRING,
|
|
||||||
ICANN_REPORTING_PASSWORD_STRING,
|
ICANN_REPORTING_PASSWORD_STRING,
|
||||||
JSON_CREDENTIAL_STRING,
|
JSON_CREDENTIAL_STRING,
|
||||||
MARKSDB_DNL_LOGIN_STRING,
|
MARKSDB_DNL_LOGIN_STRING,
|
||||||
|
@ -150,11 +149,6 @@ public class KmsKeyring implements Keyring {
|
||||||
return getString(StringKeyLabel.JSON_CREDENTIAL_STRING);
|
return getString(StringKeyLabel.JSON_CREDENTIAL_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getBraintreePrivateKey() {
|
|
||||||
return getString(StringKeyLabel.BRAINTREE_PRIVATE_KEY_STRING);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** No persistent resources are maintained for this Keyring implementation. */
|
/** No persistent resources are maintained for this Keyring implementation. */
|
||||||
@Override
|
@Override
|
||||||
public void close() {}
|
public void close() {}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.BRDA_SIGNING
|
||||||
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_RECEIVER_PUBLIC;
|
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_RECEIVER_PUBLIC;
|
||||||
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_SIGNING_PUBLIC;
|
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_SIGNING_PUBLIC;
|
||||||
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_STAGING_PUBLIC;
|
import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_STAGING_PUBLIC;
|
||||||
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.BRAINTREE_PRIVATE_KEY_STRING;
|
|
||||||
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.ICANN_REPORTING_PASSWORD_STRING;
|
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.ICANN_REPORTING_PASSWORD_STRING;
|
||||||
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.JSON_CREDENTIAL_STRING;
|
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.JSON_CREDENTIAL_STRING;
|
||||||
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.MARKSDB_DNL_LOGIN_STRING;
|
import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.MARKSDB_DNL_LOGIN_STRING;
|
||||||
|
@ -116,10 +115,6 @@ public final class KmsUpdater {
|
||||||
return setString(credential, JSON_CREDENTIAL_STRING);
|
return setString(credential, JSON_CREDENTIAL_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KmsUpdater setBraintreePrivateKey(String braintreePrivateKey) {
|
|
||||||
return setString(braintreePrivateKey, BRAINTREE_PRIVATE_KEY_STRING);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates new encryption keys in KMS, encrypts the updated secrets with them, and persists the
|
* Generates new encryption keys in KMS, encrypts the updated secrets with them, and persists the
|
||||||
* encrypted secrets to Datastore.
|
* encrypted secrets to Datastore.
|
||||||
|
|
|
@ -158,16 +158,6 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
SUSPENDED;
|
SUSPENDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Method for acquiring money from a registrar customer. */
|
|
||||||
public enum BillingMethod {
|
|
||||||
|
|
||||||
/** Billing method where billing invoice data is exported to an external accounting system. */
|
|
||||||
EXTERNAL,
|
|
||||||
|
|
||||||
/** Billing method where we accept Braintree credit card payments in the Registrar Console. */
|
|
||||||
BRAINTREE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Regex for E.164 phone number format specified by {@code contact.xsd}. */
|
/** Regex for E.164 phone number format specified by {@code contact.xsd}. */
|
||||||
private static final Pattern E164_PATTERN = Pattern.compile("\\+[0-9]{1,3}\\.[0-9]{1,14}");
|
private static final Pattern E164_PATTERN = Pattern.compile("\\+[0-9]{1,3}\\.[0-9]{1,14}");
|
||||||
|
|
||||||
|
@ -398,16 +388,6 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
*/
|
*/
|
||||||
boolean contactsRequireSyncing = true;
|
boolean contactsRequireSyncing = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for receiving money from a registrar customer.
|
|
||||||
*
|
|
||||||
* <p>Each registrar may opt-in to their preferred billing method. This value can be changed at
|
|
||||||
* any time using the {@code update_registrar} command.
|
|
||||||
*
|
|
||||||
* <p><b>Note:</b> This value should not be changed if the balance is non-zero.
|
|
||||||
*/
|
|
||||||
BillingMethod billingMethod;
|
|
||||||
|
|
||||||
/** Whether the registrar must acknowledge the price to register non-standard-priced domains. */
|
/** Whether the registrar must acknowledge the price to register non-standard-priced domains. */
|
||||||
boolean premiumPriceAckRequired;
|
boolean premiumPriceAckRequired;
|
||||||
|
|
||||||
|
@ -555,10 +535,6 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
return driveFolderId;
|
return driveFolderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BillingMethod getBillingMethod() {
|
|
||||||
return firstNonNull(billingMethod, BillingMethod.EXTERNAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all {@link RegistrarContact} objects for this registrar sorted by their email
|
* Returns a list of all {@link RegistrarContact} objects for this registrar sorted by their email
|
||||||
* address.
|
* address.
|
||||||
|
@ -834,11 +810,6 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setBillingMethod(BillingMethod billingMethod) {
|
|
||||||
getInstance().billingMethod = billingMethod;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setPassword(String password) {
|
public Builder setPassword(String password) {
|
||||||
// Passwords must be [6,16] chars long. See "pwType" in the base EPP schema of RFC 5730.
|
// Passwords must be [6,16] chars long. See "pwType" in the base EPP schema of RFC 5730.
|
||||||
checkArgument(
|
checkArgument(
|
||||||
|
|
|
@ -8,7 +8,6 @@ java_library(
|
||||||
name = "frontend",
|
name = "frontend",
|
||||||
srcs = glob(["*.java"]),
|
srcs = glob(["*.java"]),
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/braintree",
|
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/dns",
|
"//java/google/registry/dns",
|
||||||
"//java/google/registry/flows",
|
"//java/google/registry/flows",
|
||||||
|
|
|
@ -17,7 +17,6 @@ package google.registry.module.frontend;
|
||||||
import com.google.monitoring.metrics.MetricReporter;
|
import com.google.monitoring.metrics.MetricReporter;
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import google.registry.braintree.BraintreeModule;
|
|
||||||
import google.registry.config.RegistryConfig.ConfigModule;
|
import google.registry.config.RegistryConfig.ConfigModule;
|
||||||
import google.registry.flows.ServerTridProviderModule;
|
import google.registry.flows.ServerTridProviderModule;
|
||||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||||
|
@ -34,7 +33,7 @@ import google.registry.request.Modules.UrlFetchTransportModule;
|
||||||
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
||||||
import google.registry.request.Modules.UserServiceModule;
|
import google.registry.request.Modules.UserServiceModule;
|
||||||
import google.registry.request.auth.AuthModule;
|
import google.registry.request.auth.AuthModule;
|
||||||
import google.registry.ui.ConsoleConfigModule;
|
import google.registry.ui.ConsoleDebug.ConsoleConfigModule;
|
||||||
import google.registry.util.SystemClock.SystemClockModule;
|
import google.registry.util.SystemClock.SystemClockModule;
|
||||||
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
@ -45,7 +44,6 @@ import javax.inject.Singleton;
|
||||||
modules = {
|
modules = {
|
||||||
AppIdentityCredentialModule.class,
|
AppIdentityCredentialModule.class,
|
||||||
AuthModule.class,
|
AuthModule.class,
|
||||||
BraintreeModule.class,
|
|
||||||
ConfigModule.class,
|
ConfigModule.class,
|
||||||
ConsoleConfigModule.class,
|
ConsoleConfigModule.class,
|
||||||
CustomLogicFactoryModule.class,
|
CustomLogicFactoryModule.class,
|
||||||
|
|
|
@ -26,8 +26,6 @@ import google.registry.request.RequestComponentBuilder;
|
||||||
import google.registry.request.RequestModule;
|
import google.registry.request.RequestModule;
|
||||||
import google.registry.request.RequestScope;
|
import google.registry.request.RequestScope;
|
||||||
import google.registry.ui.server.registrar.ConsoleUiAction;
|
import google.registry.ui.server.registrar.ConsoleUiAction;
|
||||||
import google.registry.ui.server.registrar.RegistrarPaymentAction;
|
|
||||||
import google.registry.ui.server.registrar.RegistrarPaymentSetupAction;
|
|
||||||
import google.registry.ui.server.registrar.RegistrarSettingsAction;
|
import google.registry.ui.server.registrar.RegistrarSettingsAction;
|
||||||
|
|
||||||
/** Dagger component with per-request lifetime for "default" App Engine module. */
|
/** Dagger component with per-request lifetime for "default" App Engine module. */
|
||||||
|
@ -44,8 +42,6 @@ interface FrontendRequestComponent {
|
||||||
EppConsoleAction eppConsoleAction();
|
EppConsoleAction eppConsoleAction();
|
||||||
EppTlsAction eppTlsAction();
|
EppTlsAction eppTlsAction();
|
||||||
FlowComponent.Builder flowComponentBuilder();
|
FlowComponent.Builder flowComponentBuilder();
|
||||||
RegistrarPaymentAction registrarPaymentAction();
|
|
||||||
RegistrarPaymentSetupAction registrarPaymentSetupAction();
|
|
||||||
RegistrarSettingsAction registrarSettingsAction();
|
RegistrarSettingsAction registrarSettingsAction();
|
||||||
|
|
||||||
@Subcomponent.Builder
|
@Subcomponent.Builder
|
||||||
|
|
|
@ -23,7 +23,6 @@ def domain_registry_bazel_check():
|
||||||
|
|
||||||
def domain_registry_repositories(
|
def domain_registry_repositories(
|
||||||
omit_com_beust_jcommander=False,
|
omit_com_beust_jcommander=False,
|
||||||
omit_com_braintreepayments_gateway_braintree_java=False,
|
|
||||||
omit_com_fasterxml_jackson_core=False,
|
omit_com_fasterxml_jackson_core=False,
|
||||||
omit_com_fasterxml_jackson_core_jackson_annotations=False,
|
omit_com_fasterxml_jackson_core_jackson_annotations=False,
|
||||||
omit_com_fasterxml_jackson_core_jackson_databind=False,
|
omit_com_fasterxml_jackson_core_jackson_databind=False,
|
||||||
|
@ -163,8 +162,6 @@ def domain_registry_repositories(
|
||||||
domain_registry_bazel_check()
|
domain_registry_bazel_check()
|
||||||
if not omit_com_beust_jcommander:
|
if not omit_com_beust_jcommander:
|
||||||
com_beust_jcommander()
|
com_beust_jcommander()
|
||||||
if not omit_com_braintreepayments_gateway_braintree_java:
|
|
||||||
com_braintreepayments_gateway_braintree_java()
|
|
||||||
if not omit_com_fasterxml_jackson_core:
|
if not omit_com_fasterxml_jackson_core:
|
||||||
com_fasterxml_jackson_core()
|
com_fasterxml_jackson_core()
|
||||||
if not omit_com_fasterxml_jackson_core_jackson_annotations:
|
if not omit_com_fasterxml_jackson_core_jackson_annotations:
|
||||||
|
@ -447,17 +444,6 @@ def com_beust_jcommander():
|
||||||
licenses = ["notice"], # The Apache Software License, Version 2.0
|
licenses = ["notice"], # The Apache Software License, Version 2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
def com_braintreepayments_gateway_braintree_java():
|
|
||||||
java_import_external(
|
|
||||||
name = "com_braintreepayments_gateway_braintree_java",
|
|
||||||
jar_sha256 = "e6fa51822d05334971d60a8353d4bfcab155b9639d9d8d3d052fe75ead534dd9",
|
|
||||||
jar_urls = [
|
|
||||||
"http://domain-registry-maven.storage.googleapis.com/repo1.maven.org/maven2/com/braintreepayments/gateway/braintree-java/2.54.0/braintree-java-2.54.0.jar",
|
|
||||||
"http://repo1.maven.org/maven2/com/braintreepayments/gateway/braintree-java/2.54.0/braintree-java-2.54.0.jar",
|
|
||||||
],
|
|
||||||
licenses = ["notice"], # MIT license
|
|
||||||
)
|
|
||||||
|
|
||||||
def com_fasterxml_jackson_core():
|
def com_fasterxml_jackson_core():
|
||||||
java_import_external(
|
java_import_external(
|
||||||
name = "com_fasterxml_jackson_core",
|
name = "com_fasterxml_jackson_core",
|
||||||
|
|
|
@ -29,7 +29,6 @@ import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import google.registry.model.registrar.Registrar;
|
import google.registry.model.registrar.Registrar;
|
||||||
import google.registry.model.registrar.Registrar.BillingMethod;
|
|
||||||
import google.registry.model.registrar.RegistrarAddress;
|
import google.registry.model.registrar.RegistrarAddress;
|
||||||
import google.registry.model.registry.Registry;
|
import google.registry.model.registry.Registry;
|
||||||
import google.registry.tools.params.KeyValueMapParameter.CurrencyUnitToStringMap;
|
import google.registry.tools.params.KeyValueMapParameter.CurrencyUnitToStringMap;
|
||||||
|
@ -187,12 +186,6 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||||
)
|
)
|
||||||
private Map<CurrencyUnit, String> billingAccountMap;
|
private Map<CurrencyUnit, String> billingAccountMap;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Parameter(
|
|
||||||
names = "--billing_method",
|
|
||||||
description = "Method by which registry bills this registrar customer")
|
|
||||||
private BillingMethod billingMethod;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Parameter(
|
@Parameter(
|
||||||
names = "--street",
|
names = "--street",
|
||||||
|
@ -367,7 +360,6 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||||
newBillingAccountMap.putAll(billingAccountMap);
|
newBillingAccountMap.putAll(billingAccountMap);
|
||||||
builder.setBillingAccountMap(newBillingAccountMap);
|
builder.setBillingAccountMap(newBillingAccountMap);
|
||||||
}
|
}
|
||||||
Optional.ofNullable(billingMethod).ifPresent(builder::setBillingMethod);
|
|
||||||
List<Object> streetAddressFields = Arrays.asList(street, city, state, zip, countryCode);
|
List<Object> streetAddressFields = Arrays.asList(street, city, state, zip, countryCode);
|
||||||
checkArgument(
|
checkArgument(
|
||||||
streetAddressFields.stream().anyMatch(isNull())
|
streetAddressFields.stream().anyMatch(isNull())
|
||||||
|
|
|
@ -57,9 +57,6 @@ final class GetKeyringSecretCommand implements RemoteApiCommand {
|
||||||
Security.addProvider(new BouncyCastleProvider());
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
|
||||||
switch (keyringKeyName) {
|
switch (keyringKeyName) {
|
||||||
case BRAINTREE_PRIVATE_KEY:
|
|
||||||
out.write(KeySerializer.serializeString(keyring.getBraintreePrivateKey()));
|
|
||||||
break;
|
|
||||||
case BRDA_RECEIVER_PUBLIC_KEY:
|
case BRDA_RECEIVER_PUBLIC_KEY:
|
||||||
out.write(KeySerializer.serializePublicKey(keyring.getBrdaReceiverKey()));
|
out.write(KeySerializer.serializePublicKey(keyring.getBrdaReceiverKey()));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -56,9 +56,6 @@ final class UpdateKmsKeyringCommand implements RemoteApiCommand {
|
||||||
byte[] input = Files.readAllBytes(inputPath);
|
byte[] input = Files.readAllBytes(inputPath);
|
||||||
|
|
||||||
switch (keyringKeyName) {
|
switch (keyringKeyName) {
|
||||||
case BRAINTREE_PRIVATE_KEY:
|
|
||||||
kmsUpdater.setBraintreePrivateKey(deserializeString(input));
|
|
||||||
break;
|
|
||||||
case BRDA_RECEIVER_PUBLIC_KEY:
|
case BRDA_RECEIVER_PUBLIC_KEY:
|
||||||
kmsUpdater.setBrdaReceiverPublicKey(deserializePublicKey(input));
|
kmsUpdater.setBrdaReceiverPublicKey(deserializePublicKey(input));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -21,7 +21,6 @@ package google.registry.tools.params;
|
||||||
* any secret update command such as UpdateKmsKeyringCommand.
|
* any secret update command such as UpdateKmsKeyringCommand.
|
||||||
*/
|
*/
|
||||||
public enum KeyringKeyName {
|
public enum KeyringKeyName {
|
||||||
BRAINTREE_PRIVATE_KEY,
|
|
||||||
BRDA_RECEIVER_PUBLIC_KEY,
|
BRDA_RECEIVER_PUBLIC_KEY,
|
||||||
BRDA_SIGNING_KEY_PAIR,
|
BRDA_SIGNING_KEY_PAIR,
|
||||||
BRDA_SIGNING_PUBLIC_KEY,
|
BRDA_SIGNING_PUBLIC_KEY,
|
||||||
|
|
|
@ -10,7 +10,6 @@ exports_files(["globals.txt"])
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "runfiles",
|
name = "runfiles",
|
||||||
srcs = glob(["assets/**"]) + [
|
srcs = glob(["assets/**"]) + [
|
||||||
"brain_bin.js",
|
|
||||||
"registrar_bin.js",
|
"registrar_bin.js",
|
||||||
"//java/google/registry/ui/css:registrar_bin.css",
|
"//java/google/registry/ui/css:registrar_bin.css",
|
||||||
"//java/google/registry/ui/html:html_files",
|
"//java/google/registry/ui/html:html_files",
|
||||||
|
@ -27,7 +26,6 @@ filegroup(
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "runfiles_debug",
|
name = "runfiles_debug",
|
||||||
srcs = [
|
srcs = [
|
||||||
":brain_bin",
|
|
||||||
":deps",
|
":deps",
|
||||||
":registrar_bin",
|
":registrar_bin",
|
||||||
":registrar_dbg",
|
":registrar_dbg",
|
||||||
|
@ -63,7 +61,6 @@ zip_file(
|
||||||
out = "ui_debug.war",
|
out = "ui_debug.war",
|
||||||
data = [":runfiles_debug"],
|
data = [":runfiles_debug"],
|
||||||
mappings = {
|
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_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/registrar_dbg.js": "assets/js/registrar_dbg.js",
|
||||||
"domain_registry/java/google/registry/ui/css/registrar_dbg.css": "assets/css/registrar_dbg.css",
|
"domain_registry/java/google/registry/ui/css/registrar_dbg.css": "assets/css/registrar_dbg.css",
|
||||||
|
@ -77,7 +74,6 @@ java_library(
|
||||||
srcs = glob(["*.java"]),
|
srcs = glob(["*.java"]),
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/config",
|
|
||||||
"@com_google_appengine_api_1_0_sdk",
|
"@com_google_appengine_api_1_0_sdk",
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
|
@ -124,16 +120,3 @@ closure_js_binary(
|
||||||
"//java/google/registry/ui/js/registrar",
|
"//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;
|
package google.registry.ui;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
/** Enum defining which JS/CSS files get rendered in a soy templates. */
|
/** Enum defining which JS/CSS files get rendered in a soy templates. */
|
||||||
public enum ConsoleDebug {
|
public enum ConsoleDebug {
|
||||||
|
|
||||||
|
@ -44,4 +47,14 @@ public enum ConsoleDebug {
|
||||||
public static void set(ConsoleDebug value) {
|
public static void set(ConsoleDebug value) {
|
||||||
System.setProperty(PROPERTY, value.toString());
|
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;
|
padding-left: 1em;
|
||||||
list-style: disc inside;
|
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.Domain');
|
||||||
goog.require('registry.registrar.EppSession');
|
goog.require('registry.registrar.EppSession');
|
||||||
goog.require('registry.registrar.Host');
|
goog.require('registry.registrar.Host');
|
||||||
goog.require('registry.registrar.Payment');
|
|
||||||
goog.require('registry.registrar.Resources');
|
goog.require('registry.registrar.Resources');
|
||||||
goog.require('registry.registrar.SecuritySettings');
|
goog.require('registry.registrar.SecuritySettings');
|
||||||
goog.require('registry.registrar.WhoisSettings');
|
goog.require('registry.registrar.WhoisSettings');
|
||||||
|
@ -86,7 +85,6 @@ registry.registrar.Console = function(params) {
|
||||||
this.pageMap['contact-us'] = registry.registrar.ContactUs;
|
this.pageMap['contact-us'] = registry.registrar.ContactUs;
|
||||||
this.pageMap['resources'] = registry.registrar.Resources;
|
this.pageMap['resources'] = registry.registrar.Resources;
|
||||||
this.pageMap['contact'] = registry.registrar.Contact;
|
this.pageMap['contact'] = registry.registrar.Contact;
|
||||||
this.pageMap['payment'] = registry.registrar.Payment;
|
|
||||||
this.pageMap['domain'] = registry.registrar.Domain;
|
this.pageMap['domain'] = registry.registrar.Domain;
|
||||||
this.pageMap['host'] = registry.registrar.Host;
|
this.pageMap['host'] = registry.registrar.Host;
|
||||||
this.pageMap[''] = registry.registrar.Dashboard;
|
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",
|
"//java/google/registry/ui/css:registrar_dbg.css.js",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/braintree",
|
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/export/sheet",
|
"//java/google/registry/export/sheet",
|
||||||
"//java/google/registry/flows",
|
"//java/google/registry/flows",
|
||||||
|
|
|
@ -137,7 +137,6 @@ public final class ConsoleUiAction implements Runnable {
|
||||||
Registrar.loadByClientIdCached(clientId), "Registrar %s does not exist", clientId);
|
Registrar.loadByClientIdCached(clientId), "Registrar %s does not exist", clientId);
|
||||||
data.put("xsrfToken", xsrfTokenManager.generateToken(userAuthInfo.user().getEmail()));
|
data.put("xsrfToken", xsrfTokenManager.generateToken(userAuthInfo.user().getEmail()));
|
||||||
data.put("clientId", clientId);
|
data.put("clientId", clientId);
|
||||||
data.put("showPaymentLink", registrar.getBillingMethod() == Registrar.BillingMethod.BRAINTREE);
|
|
||||||
data.put("requireFeeExtension", registrar.getPremiumPriceAckRequired());
|
data.put("requireFeeExtension", registrar.getPremiumPriceAckRequired());
|
||||||
|
|
||||||
String payload = TOFU_SUPPLIER.get()
|
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 clientId: string} /** Registrar client identifier. */
|
||||||
{@param username: string} /** Arbitrary username to display. */
|
{@param username: string} /** Arbitrary username to display. */
|
||||||
{@param logoutUrl: string} /** Generated URL for logging out of Google. */
|
{@param logoutUrl: string} /** Generated URL for logging out of Google. */
|
||||||
{@param showPaymentLink: bool}
|
|
||||||
{@param productName: string} /** Name to display for this software product. */
|
{@param productName: string} /** Name to display for this software product. */
|
||||||
{@param integrationEmail: string}
|
{@param integrationEmail: string}
|
||||||
{@param supportEmail: string}
|
{@param supportEmail: string}
|
||||||
|
@ -76,17 +75,12 @@
|
||||||
|
|
||||||
/** Sidebar nav. Ids on each elt for testing only. */
|
/** Sidebar nav. Ids on each elt for testing only. */
|
||||||
{template .navbar_ visibility="private"}
|
{template .navbar_ visibility="private"}
|
||||||
{@param showPaymentLink: bool}
|
|
||||||
<div id="reg-nav" class="{css('kd-content-sidebar')}">
|
<div id="reg-nav" class="{css('kd-content-sidebar')}">
|
||||||
<ul id="reg-navlist">
|
<ul id="reg-navlist">
|
||||||
<li>
|
<li>
|
||||||
<a href="/registrar#">Home</a>
|
<a href="/registrar#">Home</a>
|
||||||
<li>
|
<li>
|
||||||
<a href="/registrar#resources">Resources & billing</a>
|
<a href="/registrar#resources">Resources & billing</a>
|
||||||
{if $showPaymentLink}
|
|
||||||
<li>
|
|
||||||
<a href="/registrar#payment">Pay invoice</a>
|
|
||||||
{/if}
|
|
||||||
<li>
|
<li>
|
||||||
<ul>
|
<ul>
|
||||||
<span class="{css('reg-navlist-sub')}">Settings</span>
|
<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}
|
|
|
@ -174,15 +174,6 @@ public class KmsKeyringTest {
|
||||||
assertThat(jsonCredential).isEqualTo("json-credential-stringmoo");
|
assertThat(jsonCredential).isEqualTo("json-credential-stringmoo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test_getBraintreePrivateKey() {
|
|
||||||
saveCleartextSecret("braintree-private-key-string");
|
|
||||||
|
|
||||||
String braintreePrivateKey = keyring.getBraintreePrivateKey();
|
|
||||||
|
|
||||||
assertThat(braintreePrivateKey).isEqualTo("braintree-private-key-stringmoo");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void persistSecret(String secretName, byte[] secretValue) {
|
private static void persistSecret(String secretName, byte[] secretValue) {
|
||||||
KmsConnection kmsConnection = new FakeKmsConnection();
|
KmsConnection kmsConnection = new FakeKmsConnection();
|
||||||
|
|
||||||
|
|
|
@ -50,14 +50,14 @@ public class KmsUpdaterTest {
|
||||||
@Test
|
@Test
|
||||||
public void test_setMultipleSecrets() {
|
public void test_setMultipleSecrets() {
|
||||||
updater
|
updater
|
||||||
.setBraintreePrivateKey("value1")
|
.setMarksdbDnlLogin("value1")
|
||||||
.setIcannReportingPassword("value2")
|
.setIcannReportingPassword("value2")
|
||||||
.setJsonCredential("value3")
|
.setJsonCredential("value3")
|
||||||
.update();
|
.update();
|
||||||
|
|
||||||
verifySecretAndSecretRevisionWritten(
|
verifySecretAndSecretRevisionWritten(
|
||||||
"braintree-private-key-string",
|
"marksdb-dnl-login-string",
|
||||||
"braintree-private-key-string/foo",
|
"marksdb-dnl-login-string/foo",
|
||||||
getCiphertext("value1"));
|
getCiphertext("value1"));
|
||||||
verifySecretAndSecretRevisionWritten(
|
verifySecretAndSecretRevisionWritten(
|
||||||
"icann-reporting-password-string",
|
"icann-reporting-password-string",
|
||||||
|
@ -67,16 +67,6 @@ public class KmsUpdaterTest {
|
||||||
"json-credential-string", "json-credential-string/foo", getCiphertext("value3"));
|
"json-credential-string", "json-credential-string/foo", getCiphertext("value3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test_setBraintreePrivateKey() {
|
|
||||||
updater.setBraintreePrivateKey("value1").update();
|
|
||||||
|
|
||||||
verifySecretAndSecretRevisionWritten(
|
|
||||||
"braintree-private-key-string",
|
|
||||||
"braintree-private-key-string/foo",
|
|
||||||
getCiphertext("value1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_setBrdaReceiverKey() throws Exception {
|
public void test_setBrdaReceiverKey() throws Exception {
|
||||||
updater.setBrdaReceiverPublicKey(KmsTestHelper.getPublicKey()).update();
|
updater.setBrdaReceiverPublicKey(KmsTestHelper.getPublicKey()).update();
|
||||||
|
|
|
@ -560,7 +560,6 @@ class google.registry.model.registrar.Registrar {
|
||||||
boolean premiumPriceAckRequired;
|
boolean premiumPriceAckRequired;
|
||||||
google.registry.model.CreateAutoTimestamp creationTime;
|
google.registry.model.CreateAutoTimestamp creationTime;
|
||||||
google.registry.model.UpdateAutoTimestamp lastUpdateTime;
|
google.registry.model.UpdateAutoTimestamp lastUpdateTime;
|
||||||
google.registry.model.registrar.Registrar$BillingMethod billingMethod;
|
|
||||||
google.registry.model.registrar.Registrar$State state;
|
google.registry.model.registrar.Registrar$State state;
|
||||||
google.registry.model.registrar.Registrar$Type type;
|
google.registry.model.registrar.Registrar$Type type;
|
||||||
google.registry.model.registrar.RegistrarAddress internationalizedAddress;
|
google.registry.model.registrar.RegistrarAddress internationalizedAddress;
|
||||||
|
@ -591,10 +590,6 @@ class google.registry.model.registrar.Registrar$BillingAccountEntry {
|
||||||
java.lang.String accountId;
|
java.lang.String accountId;
|
||||||
org.joda.money.CurrencyUnit currency;
|
org.joda.money.CurrencyUnit currency;
|
||||||
}
|
}
|
||||||
enum google.registry.model.registrar.Registrar$BillingMethod {
|
|
||||||
BRAINTREE;
|
|
||||||
EXTERNAL;
|
|
||||||
}
|
|
||||||
enum google.registry.model.registrar.Registrar$State {
|
enum google.registry.model.registrar.Registrar$State {
|
||||||
ACTIVE;
|
ACTIVE;
|
||||||
PENDING;
|
PENDING;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||||
/_dr/epp EppTlsAction POST n INTERNAL,API APP PUBLIC
|
/_dr/epp EppTlsAction POST n INTERNAL,API APP PUBLIC
|
||||||
/registrar ConsoleUiAction GET n INTERNAL,API,LEGACY NONE PUBLIC
|
/registrar ConsoleUiAction GET n INTERNAL,API,LEGACY NONE PUBLIC
|
||||||
/registrar-payment RegistrarPaymentAction POST n API,LEGACY USER PUBLIC
|
/registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC
|
||||||
/registrar-payment-setup RegistrarPaymentSetupAction POST n API,LEGACY USER PUBLIC
|
/registrar-xhr EppConsoleAction POST n API,LEGACY USER PUBLIC
|
||||||
/registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC
|
|
||||||
/registrar-xhr EppConsoleAction POST n API,LEGACY USER PUBLIC
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ import google.registry.model.contact.ContactResource;
|
||||||
import google.registry.model.contact.PostalInfo;
|
import google.registry.model.contact.PostalInfo;
|
||||||
import google.registry.model.domain.DesignatedContact;
|
import google.registry.model.domain.DesignatedContact;
|
||||||
import google.registry.model.ofy.Ofy;
|
import google.registry.model.ofy.Ofy;
|
||||||
import google.registry.model.registrar.Registrar;
|
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.testing.FakeClock;
|
||||||
import google.registry.testing.InjectRule;
|
import google.registry.testing.InjectRule;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
@ -139,7 +138,6 @@ public enum Fixture {
|
||||||
loadRegistrar("TheRegistrar")
|
loadRegistrar("TheRegistrar")
|
||||||
.asBuilder()
|
.asBuilder()
|
||||||
.setAllowedTlds(ImmutableSet.of("example", "xn--q9jyb4c"))
|
.setAllowedTlds(ImmutableSet.of("example", "xn--q9jyb4c"))
|
||||||
.setBillingMethod(Registrar.BillingMethod.BRAINTREE)
|
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,10 +87,6 @@ public final class RegistryTestServer {
|
||||||
// Registrar Console
|
// Registrar Console
|
||||||
route("/registrar", google.registry.module.frontend.FrontendServlet.class),
|
route("/registrar", google.registry.module.frontend.FrontendServlet.class),
|
||||||
route("/registrar-settings",
|
route("/registrar-settings",
|
||||||
google.registry.module.frontend.FrontendServlet.class),
|
|
||||||
route("/registrar-payment",
|
|
||||||
google.registry.module.frontend.FrontendServlet.class),
|
|
||||||
route("/registrar-payment-setup",
|
|
||||||
google.registry.module.frontend.FrontendServlet.class));
|
google.registry.module.frontend.FrontendServlet.class));
|
||||||
|
|
||||||
private static final ImmutableList<Class<? extends Filter>> FILTERS = ImmutableList.of(
|
private static final ImmutableList<Class<? extends Filter>> FILTERS = ImmutableList.of(
|
||||||
|
|
|
@ -54,7 +54,6 @@ public final class FakeKeyringModule {
|
||||||
private static final String MARKSDB_DNL_LOGIN = "dnl:yolo";
|
private static final String MARKSDB_DNL_LOGIN = "dnl:yolo";
|
||||||
private static final String MARKSDB_LORDN_PASSWORD = "yolo";
|
private static final String MARKSDB_LORDN_PASSWORD = "yolo";
|
||||||
private static final String MARKSDB_SMDRL_LOGIN = "smdrl:yolo";
|
private static final String MARKSDB_SMDRL_LOGIN = "smdrl:yolo";
|
||||||
private static final String BRAINTREE_PRIVATE_KEY = "braintree123";
|
|
||||||
private static final String JSON_CREDENTIAL = "json123";
|
private static final String JSON_CREDENTIAL = "json123";
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -145,11 +144,6 @@ public final class FakeKeyringModule {
|
||||||
return rdeReceiverKey;
|
return rdeReceiverKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getBraintreePrivateKey() {
|
|
||||||
return BRAINTREE_PRIVATE_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {}
|
public void close() {}
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ import static google.registry.testing.TestDataHelper.filePath;
|
||||||
import static google.registry.testing.TestDataHelper.loadFile;
|
import static google.registry.testing.TestDataHelper.loadFile;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.flogger.FluentLogger;
|
||||||
import google.registry.request.RouterDisplayHelper;
|
import google.registry.request.RouterDisplayHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,6 +30,8 @@ import google.registry.request.RouterDisplayHelper;
|
||||||
*/
|
*/
|
||||||
public class GoldenFileTestHelper {
|
public class GoldenFileTestHelper {
|
||||||
|
|
||||||
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
|
|
||||||
String actualValue = null;
|
String actualValue = null;
|
||||||
String nomulusCommand = null;
|
String nomulusCommand = null;
|
||||||
String goldenFileDescription = null;
|
String goldenFileDescription = null;
|
||||||
|
@ -73,6 +76,7 @@ public class GoldenFileTestHelper {
|
||||||
checkNotNull(context);
|
checkNotNull(context);
|
||||||
checkNotNull(filename);
|
checkNotNull(filename);
|
||||||
if (!actualValue.equals(loadFile(context, filename).trim())) {
|
if (!actualValue.equals(loadFile(context, filename).trim())) {
|
||||||
|
logger.atWarning().log("Actual routing map was:\n\n%s", actualValue);
|
||||||
assert_()
|
assert_()
|
||||||
.fail(
|
.fail(
|
||||||
String.format(
|
String.format(
|
||||||
|
|
|
@ -29,7 +29,6 @@ import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import google.registry.model.registrar.Registrar;
|
import google.registry.model.registrar.Registrar;
|
||||||
import google.registry.model.registrar.Registrar.BillingMethod;
|
|
||||||
import google.registry.model.registrar.Registrar.State;
|
import google.registry.model.registrar.Registrar.State;
|
||||||
import google.registry.model.registrar.Registrar.Type;
|
import google.registry.model.registrar.Registrar.Type;
|
||||||
import google.registry.util.CidrAddressBlock;
|
import google.registry.util.CidrAddressBlock;
|
||||||
|
@ -258,15 +257,6 @@ public class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarC
|
||||||
.containsExactly(CurrencyUnit.JPY, "123xyz", CurrencyUnit.USD, "abc123");
|
.containsExactly(CurrencyUnit.JPY, "123xyz", CurrencyUnit.USD, "abc123");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_changeBillingMethodToBraintreeWhenBalanceIsZero() throws Exception {
|
|
||||||
createTlds("xn--q9jyb4c");
|
|
||||||
assertThat(loadRegistrar("NewRegistrar").getBillingMethod()).isEqualTo(BillingMethod.EXTERNAL);
|
|
||||||
runCommand("--billing_method=braintree", "--force", "NewRegistrar");
|
|
||||||
assertThat(loadRegistrar("NewRegistrar").getBillingMethod())
|
|
||||||
.isEqualTo(BillingMethod.BRAINTREE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_streetAddress() throws Exception {
|
public void testSuccess_streetAddress() throws Exception {
|
||||||
runCommand("--street=\"1234 Main St\"", "--street \"4th Floor\"", "--street \"Suite 1\"",
|
runCommand("--street=\"1234 Main St\"", "--street \"4th Floor\"", "--street \"Suite 1\"",
|
||||||
|
|
|
@ -1,97 +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.setTestOnly();
|
|
||||||
|
|
||||||
goog.require('goog.dispose');
|
|
||||||
goog.require('goog.dom');
|
|
||||||
goog.require('goog.style');
|
|
||||||
goog.require('goog.testing.MockControl');
|
|
||||||
goog.require('goog.testing.asserts');
|
|
||||||
goog.require('goog.testing.jsunit');
|
|
||||||
goog.require('registry.registrar.BrainFrame');
|
|
||||||
goog.require('registry.testing');
|
|
||||||
|
|
||||||
|
|
||||||
var mocks = new goog.testing.MockControl();
|
|
||||||
|
|
||||||
var brainframe;
|
|
||||||
var mockPostMessage;
|
|
||||||
|
|
||||||
|
|
||||||
function setUp() {
|
|
||||||
registry.testing.addToDocument('<form><div id="bf"></div></form>');
|
|
||||||
brainframe = new registry.registrar.BrainFrame('omg', 'bf');
|
|
||||||
mockPostMessage =
|
|
||||||
mocks.createMethodMock(registry.registrar.BrainFrame, 'postMessage_');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function tearDown() {
|
|
||||||
goog.dispose(brainframe);
|
|
||||||
mocks.$tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testRun_sendsTokenRequestToParent() {
|
|
||||||
mockPostMessage('{"type":"token_request"}', 'omg');
|
|
||||||
mocks.$replayAll();
|
|
||||||
brainframe.run();
|
|
||||||
mocks.$verifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testTokenResponseMessage_callsSetup() {
|
|
||||||
var called = false;
|
|
||||||
goog.global.braintree = {};
|
|
||||||
goog.global.braintree.setup = function(token, mode, args) {
|
|
||||||
called = true;
|
|
||||||
assertEquals('imatoken', token);
|
|
||||||
assertEquals('dropin', mode);
|
|
||||||
assertEquals('bf', args.container.id);
|
|
||||||
};
|
|
||||||
brainframe.onMessage_({
|
|
||||||
getBrowserEvent: function() {
|
|
||||||
return {
|
|
||||||
source: window.parent,
|
|
||||||
origin: 'omg',
|
|
||||||
data: '{"type": "token_response", "token": "imatoken"}'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertTrue(called);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testPaymentMethodMessage_sendsInfo() {
|
|
||||||
mockPostMessage('{"type":"payment_method","submit":false,"method":"hi"}',
|
|
||||||
'omg');
|
|
||||||
mocks.$replayAll();
|
|
||||||
brainframe.onPaymentMethod_('hi');
|
|
||||||
mocks.$verifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testOnResizeTimer_sendsHeight() {
|
|
||||||
mockPostMessage('{"type":"resize_request","height":123}', 'omg');
|
|
||||||
mocks.$replayAll();
|
|
||||||
goog.style.setHeight(goog.dom.getElement('bf'), 123);
|
|
||||||
brainframe.onResizeTimer_();
|
|
||||||
mocks.$verifyAll();
|
|
||||||
|
|
||||||
// But does not send height if size hasn't changed.
|
|
||||||
mocks.$replayAll();
|
|
||||||
brainframe.onResizeTimer_();
|
|
||||||
mocks.$verifyAll();
|
|
||||||
}
|
|
|
@ -51,7 +51,6 @@ function setUp() {
|
||||||
logoutUrl: 'omg',
|
logoutUrl: 'omg',
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
clientId: test.testClientId,
|
clientId: test.testClientId,
|
||||||
showPaymentLink: false,
|
|
||||||
logoFilename: 'logo.png',
|
logoFilename: 'logo.png',
|
||||||
productName: 'Nomulus',
|
productName: 'Nomulus',
|
||||||
integrationEmail: 'integration@example.com',
|
integrationEmail: 'integration@example.com',
|
||||||
|
|
|
@ -50,7 +50,6 @@ function setUp() {
|
||||||
logoutUrl: 'omg',
|
logoutUrl: 'omg',
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
clientId: test.testClientId,
|
clientId: test.testClientId,
|
||||||
showPaymentLink: false,
|
|
||||||
logoFilename: 'logo.png',
|
logoFilename: 'logo.png',
|
||||||
productName: 'Nomulus',
|
productName: 'Nomulus',
|
||||||
integrationEmail: 'integration@google.com',
|
integrationEmail: 'integration@google.com',
|
||||||
|
|
|
@ -41,7 +41,6 @@ function setUp() {
|
||||||
logoutUrl: 'omg',
|
logoutUrl: 'omg',
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
clientId: 'daddy',
|
clientId: 'daddy',
|
||||||
showPaymentLink: false,
|
|
||||||
logoFilename: 'logo.png',
|
logoFilename: 'logo.png',
|
||||||
productName: 'Nomulus',
|
productName: 'Nomulus',
|
||||||
integrationEmail: 'integration@example.com',
|
integrationEmail: 'integration@example.com',
|
||||||
|
|
|
@ -47,7 +47,6 @@ function setUp() {
|
||||||
logoutUrl: 'https://justinetunney.com',
|
logoutUrl: 'https://justinetunney.com',
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
clientId: 'ignore',
|
clientId: 'ignore',
|
||||||
showPaymentLink: false,
|
|
||||||
logoFilename: 'logo.png',
|
logoFilename: 'logo.png',
|
||||||
productName: 'Nomulus',
|
productName: 'Nomulus',
|
||||||
integrationEmail: 'integration@example.com',
|
integrationEmail: 'integration@example.com',
|
||||||
|
|
|
@ -47,7 +47,6 @@ function setUp() {
|
||||||
logoutUrl: 'https://example.com',
|
logoutUrl: 'https://example.com',
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
clientId: 'ignore',
|
clientId: 'ignore',
|
||||||
showPaymentLink: false,
|
|
||||||
logoFilename: 'logo.png',
|
logoFilename: 'logo.png',
|
||||||
productName: 'Nomulus',
|
productName: 'Nomulus',
|
||||||
integrationEmail: 'integration@example.com',
|
integrationEmail: 'integration@example.com',
|
||||||
|
|
|
@ -1,208 +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.setTestOnly();
|
|
||||||
|
|
||||||
goog.require('goog.dispose');
|
|
||||||
goog.require('goog.dom');
|
|
||||||
goog.require('goog.dom.classlist');
|
|
||||||
goog.require('goog.events.EventType');
|
|
||||||
goog.require('goog.json');
|
|
||||||
goog.require('goog.testing.PropertyReplacer');
|
|
||||||
goog.require('goog.testing.asserts');
|
|
||||||
goog.require('goog.testing.events');
|
|
||||||
goog.require('goog.testing.events.Event');
|
|
||||||
goog.require('goog.testing.jsunit');
|
|
||||||
goog.require('goog.testing.net.XhrIo');
|
|
||||||
goog.require('registry.registrar.Payment');
|
|
||||||
goog.require('registry.testing');
|
|
||||||
|
|
||||||
|
|
||||||
var $ = goog.dom.getRequiredElement;
|
|
||||||
var $$ = goog.dom.getRequiredElementByClass;
|
|
||||||
var stubs = new goog.testing.PropertyReplacer();
|
|
||||||
|
|
||||||
var page;
|
|
||||||
|
|
||||||
|
|
||||||
function setUp() {
|
|
||||||
registry.testing.addToDocument('<div id="reg-content"></div>');
|
|
||||||
registry.testing.addToDocument('<div class="kd-butterbar"></div>');
|
|
||||||
registry.testing.addToDocument('<div class="kd-butterbar-text"></div>');
|
|
||||||
stubs.setPath('goog.net.XhrIo', goog.testing.net.XhrIo);
|
|
||||||
page = new registry.registrar.Payment(null, '?');
|
|
||||||
page.bindToDom();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function tearDown() {
|
|
||||||
goog.dispose(page);
|
|
||||||
stubs.reset();
|
|
||||||
goog.testing.net.XhrIo.cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testRenderForm() {
|
|
||||||
registry.testing.assertReqMockRsp(
|
|
||||||
'?',
|
|
||||||
'/registrar-payment-setup',
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
status: 'SUCCESS',
|
|
||||||
results: [
|
|
||||||
{
|
|
||||||
token: 'omg-im-a-token',
|
|
||||||
currencies: ['LOL', 'OMG'],
|
|
||||||
brainframe: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
assertEquals('', $('amount').value);
|
|
||||||
assertEquals('LOL', goog.dom.getTextContent($$('selected', $('currency'))));
|
|
||||||
assertTrue(
|
|
||||||
goog.dom.classlist.contains($$('reg-payment-form-submit'), 'disabled'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testResize() {
|
|
||||||
testRenderForm();
|
|
||||||
send({
|
|
||||||
type: 'resize_request',
|
|
||||||
height: 123
|
|
||||||
});
|
|
||||||
assertEquals('123', $$('reg-payment-form-method').height);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testReady() {
|
|
||||||
testRenderForm();
|
|
||||||
send({type: 'ready'});
|
|
||||||
assertFalse(
|
|
||||||
goog.dom.classlist.contains($$('reg-payment-form-submit'), 'disabled'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testPaymentMethodCard() {
|
|
||||||
testRenderForm();
|
|
||||||
send({
|
|
||||||
type: 'payment_method',
|
|
||||||
method: {
|
|
||||||
type: 'CreditCard',
|
|
||||||
nonce: 'omg-im-a-nonce',
|
|
||||||
details: {
|
|
||||||
cardType: 'Amex',
|
|
||||||
lastTwo: '12'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertEquals(
|
|
||||||
'American Express: xxxx xxxxxx xxx12',
|
|
||||||
goog.dom.getTextContent($$('reg-payment-form-method-info')));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testPaymentMethodPaypal() {
|
|
||||||
testRenderForm();
|
|
||||||
send({
|
|
||||||
type: 'payment_method',
|
|
||||||
method: {
|
|
||||||
type: 'PayPalAccount',
|
|
||||||
nonce: 'omg-im-a-nonce',
|
|
||||||
details: {
|
|
||||||
email: 'sparrows@nightingales.example'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertEquals(
|
|
||||||
'PayPal: sparrows@nightingales.example',
|
|
||||||
goog.dom.getTextContent($$('reg-payment-form-method-info')));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testBadAmount_displaysError() {
|
|
||||||
testPaymentMethodCard();
|
|
||||||
$('amount').value = '3.14';
|
|
||||||
submit();
|
|
||||||
registry.testing.assertReqMockRsp(
|
|
||||||
'?',
|
|
||||||
'/registrar-payment',
|
|
||||||
{
|
|
||||||
amount: '3.14',
|
|
||||||
currency: 'LOL',
|
|
||||||
paymentMethodNonce: 'omg-im-a-nonce'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 'ERROR',
|
|
||||||
message: 'gimmeh moar money',
|
|
||||||
field: 'amount'
|
|
||||||
});
|
|
||||||
assertTrue(goog.dom.classlist.contains($('amount'), 'kd-formerror'));
|
|
||||||
assertEquals('gimmeh moar money',
|
|
||||||
goog.dom.getTextContent($$('kd-errormessage')));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function testGoodPayment_displaysSuccessPage() {
|
|
||||||
testPaymentMethodCard();
|
|
||||||
$('amount').value = '314';
|
|
||||||
submit();
|
|
||||||
registry.testing.assertReqMockRsp(
|
|
||||||
'?',
|
|
||||||
'/registrar-payment',
|
|
||||||
{
|
|
||||||
amount: '314',
|
|
||||||
currency: 'LOL',
|
|
||||||
paymentMethodNonce: 'omg-im-a-nonce'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 'SUCCESS',
|
|
||||||
results: [
|
|
||||||
{
|
|
||||||
id: 'omg-im-an-id',
|
|
||||||
formattedAmount: '$314'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
assertContains('Payment Processed',
|
|
||||||
goog.dom.getTextContent($$('reg-payment')));
|
|
||||||
assertContains('omg-im-an-id',
|
|
||||||
goog.dom.getTextContent($$('reg-payment')));
|
|
||||||
assertContains('$314',
|
|
||||||
goog.dom.getTextContent($$('reg-payment')));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends message to page.
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
function send(message) {
|
|
||||||
page.onMessage_({
|
|
||||||
getBrowserEvent: function() {
|
|
||||||
return {
|
|
||||||
source: goog.dom.getFrameContentWindow($$('reg-payment-form-method')),
|
|
||||||
data: goog.json.serialize(message)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Submits payment form. */
|
|
||||||
function submit() {
|
|
||||||
goog.testing.events.fireBrowserEvent(
|
|
||||||
new goog.testing.events.Event(
|
|
||||||
goog.events.EventType.SUBMIT,
|
|
||||||
$$('reg-payment-form')));
|
|
||||||
}
|
|
|
@ -55,7 +55,6 @@ function setUp() {
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
xsrfToken: test.testXsrfToken,
|
xsrfToken: test.testXsrfToken,
|
||||||
clientId: test.testClientId,
|
clientId: test.testClientId,
|
||||||
showPaymentLink: false,
|
|
||||||
logoFilename: 'logo.png',
|
logoFilename: 'logo.png',
|
||||||
productName: 'Nomulus',
|
productName: 'Nomulus',
|
||||||
integrationEmail: 'integration@example.com',
|
integrationEmail: 'integration@example.com',
|
||||||
|
|
|
@ -49,7 +49,6 @@ function setUp() {
|
||||||
logoutUrl: 'omg',
|
logoutUrl: 'omg',
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
clientId: test.testClientId,
|
clientId: test.testClientId,
|
||||||
showPaymentLink: false,
|
|
||||||
logoFilename: 'logo.png',
|
logoFilename: 'logo.png',
|
||||||
productName: 'Nomulus',
|
productName: 'Nomulus',
|
||||||
integrationEmail: 'integration@example.com',
|
integrationEmail: 'integration@example.com',
|
||||||
|
|
|
@ -12,7 +12,6 @@ java_library(
|
||||||
srcs = glob(["*.java"]),
|
srcs = glob(["*.java"]),
|
||||||
resources = glob(["testdata/*"]),
|
resources = glob(["testdata/*"]),
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/braintree",
|
|
||||||
"//java/google/registry/config",
|
"//java/google/registry/config",
|
||||||
"//java/google/registry/export/sheet",
|
"//java/google/registry/export/sheet",
|
||||||
"//java/google/registry/model",
|
"//java/google/registry/model",
|
||||||
|
|
|
@ -1,445 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.DatastoreHelper.loadRegistrar;
|
|
||||||
import static google.registry.testing.ReflectiveFieldExtractor.extractField;
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
import static org.mockito.Matchers.any;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.braintreegateway.BraintreeGateway;
|
|
||||||
import com.braintreegateway.Result;
|
|
||||||
import com.braintreegateway.Transaction;
|
|
||||||
import com.braintreegateway.Transaction.GatewayRejectionReason;
|
|
||||||
import com.braintreegateway.TransactionGateway;
|
|
||||||
import com.braintreegateway.TransactionRequest;
|
|
||||||
import com.braintreegateway.ValidationError;
|
|
||||||
import com.braintreegateway.ValidationErrorCode;
|
|
||||||
import com.braintreegateway.ValidationErrors;
|
|
||||||
import com.google.appengine.api.users.User;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import google.registry.request.auth.AuthLevel;
|
|
||||||
import google.registry.request.auth.AuthResult;
|
|
||||||
import google.registry.request.auth.UserAuthInfo;
|
|
||||||
import google.registry.testing.AppEngineRule;
|
|
||||||
import google.registry.testing.MockitoJUnitRule;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import org.joda.money.CurrencyUnit;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.Captor;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
|
|
||||||
/** Tests for {@link RegistrarPaymentAction}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class RegistrarPaymentActionTest {
|
|
||||||
|
|
||||||
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
|
|
||||||
@Rule public final MockitoJUnitRule mocks = MockitoJUnitRule.create();
|
|
||||||
|
|
||||||
@Mock private BraintreeGateway braintreeGateway;
|
|
||||||
@Mock private TransactionGateway transactionGateway;
|
|
||||||
@Mock private Result<Transaction> result;
|
|
||||||
@Mock private Transaction transaction;
|
|
||||||
@Mock private ValidationErrors validationErrors;
|
|
||||||
@Captor private ArgumentCaptor<TransactionRequest> transactionRequestCaptor;
|
|
||||||
|
|
||||||
private final SessionUtils sessionUtils = mock(SessionUtils.class);
|
|
||||||
private final User user = new User("marla.singer@example.com", "gmail.com", "12345");
|
|
||||||
private final RegistrarPaymentAction paymentAction = new RegistrarPaymentAction();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() {
|
|
||||||
paymentAction.sessionUtils = sessionUtils;
|
|
||||||
paymentAction.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
|
|
||||||
paymentAction.accountIds =
|
|
||||||
ImmutableMap.of(
|
|
||||||
CurrencyUnit.USD, "merchant-account-usd",
|
|
||||||
CurrencyUnit.JPY, "merchant-account-jpy");
|
|
||||||
paymentAction.braintreeGateway = braintreeGateway;
|
|
||||||
when(braintreeGateway.transaction()).thenReturn(transactionGateway);
|
|
||||||
when(transactionGateway.sale(any(TransactionRequest.class))).thenReturn(result);
|
|
||||||
when(sessionUtils.getRegistrarForAuthResult(
|
|
||||||
any(HttpServletRequest.class), any(AuthResult.class)))
|
|
||||||
.thenReturn(loadRegistrar("TheRegistrar"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCurrencyIsUsd_usesAmericanMerchantAccount() throws Exception {
|
|
||||||
when(result.isSuccess()).thenReturn(true);
|
|
||||||
when(result.getTarget()).thenReturn(transaction);
|
|
||||||
when(transaction.getId()).thenReturn("omg-im-an-id");
|
|
||||||
when(transaction.getAmount()).thenReturn(BigDecimal.valueOf(123.4));
|
|
||||||
when(transaction.getCurrencyIsoCode()).thenReturn("USD");
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", " 123.4 ",
|
|
||||||
"currency", "USD",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "SUCCESS",
|
|
||||||
"message", "Payment processed successfully",
|
|
||||||
"results", asList(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"id", "omg-im-an-id",
|
|
||||||
"formattedAmount", "$123.40")));
|
|
||||||
verify(transactionGateway).sale(transactionRequestCaptor.capture());
|
|
||||||
TransactionRequest transactionRequest = transactionRequestCaptor.getAllValues().get(0);
|
|
||||||
assertThat(extractField(BigDecimal.class, transactionRequest, "amount"))
|
|
||||||
.isEqualTo(BigDecimal.valueOf(123.4).setScale(2));
|
|
||||||
assertThat(extractField(String.class, transactionRequest, "merchantAccountId"))
|
|
||||||
.isEqualTo("merchant-account-usd");
|
|
||||||
assertThat(extractField(String.class, transactionRequest, "customerId"))
|
|
||||||
.isEqualTo("TheRegistrar");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCurrencyIsJpy_usesJapaneseMerchantAccount() throws Exception {
|
|
||||||
when(result.isSuccess()).thenReturn(true);
|
|
||||||
when(result.getTarget()).thenReturn(transaction);
|
|
||||||
when(transaction.getId()).thenReturn("omg-im-an-id");
|
|
||||||
when(transaction.getAmount()).thenReturn(BigDecimal.valueOf(123));
|
|
||||||
when(transaction.getCurrencyIsoCode()).thenReturn("JPY");
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "JPY",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "SUCCESS",
|
|
||||||
"message", "Payment processed successfully",
|
|
||||||
"results", asList(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"id", "omg-im-an-id",
|
|
||||||
"formattedAmount", "JPY 123")));
|
|
||||||
verify(transactionGateway).sale(transactionRequestCaptor.capture());
|
|
||||||
TransactionRequest transactionRequest = transactionRequestCaptor.getAllValues().get(0);
|
|
||||||
assertThat(extractField(BigDecimal.class, transactionRequest, "amount"))
|
|
||||||
.isEqualTo(BigDecimal.valueOf(123));
|
|
||||||
assertThat(extractField(String.class, transactionRequest, "merchantAccountId"))
|
|
||||||
.isEqualTo("merchant-account-jpy");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAmountNotPresent_returnsErrorOnAmountField() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"currency", "JPY",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "This field is required.",
|
|
||||||
"field", "amount",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAmountNotNumber_returnsErrorOnAmountField() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "abc",
|
|
||||||
"currency", "JPY",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Invalid number.",
|
|
||||||
"field", "amount",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNegativeAmount_returnsErrorOnAmountField() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "-10",
|
|
||||||
"currency", "JPY",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Must be a positive number.",
|
|
||||||
"field", "amount",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testZeroAmount_returnsErrorOnAmountField() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "0",
|
|
||||||
"currency", "JPY",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Must be a positive number.",
|
|
||||||
"field", "amount",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAmountHasMorePrecisionThanUsdCurrencyAllows_returnsError() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123.456",
|
|
||||||
"currency", "USD",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Scale of amount 123.456 is greater than the scale of the currency USD",
|
|
||||||
"field", "amount",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAmountHasMorePrecisionThanJpyCurrencyAllows_returnsError() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "1.1",
|
|
||||||
"currency", "JPY",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Scale of amount 1.1 is greater than the scale of the currency JPY",
|
|
||||||
"field", "amount",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCurrencyValidButNotSupported_returnsErrorOnCurrencyField() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "EUR",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Unsupported currency.",
|
|
||||||
"field", "currency",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCurrencyBogus_returnsErrorOnCurrencyField() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "rm -rf /etc/passwd",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Invalid currency code.",
|
|
||||||
"field", "currency",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCurrencyCodeNotAssigned_returnsErrorOnCurrencyField() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "ZZZ",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Unknown ISO currency code.",
|
|
||||||
"field", "currency",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCurrencyMissing_returnsErrorOnCurrencyField() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"paymentMethodNonce", "omg")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "This field is required.",
|
|
||||||
"field", "currency",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPaymentMethodMissing_returnsErrorOnPaymentMethodField() {
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "JPY")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "This field is required.",
|
|
||||||
"field", "paymentMethodNonce",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testProcessorDeclined_returnsErrorResponse() {
|
|
||||||
when(result.isSuccess()).thenReturn(false);
|
|
||||||
when(result.getTransaction()).thenReturn(transaction);
|
|
||||||
when(transaction.getStatus()).thenReturn(Transaction.Status.PROCESSOR_DECLINED);
|
|
||||||
when(transaction.getProcessorResponseText())
|
|
||||||
.thenReturn("You don't know the power of the dark side");
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "USD",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Payment declined: You don't know the power of the dark side",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGatewayRejectedDueToCvv_returnsErrorResponse() {
|
|
||||||
when(result.isSuccess()).thenReturn(false);
|
|
||||||
when(result.getTransaction()).thenReturn(transaction);
|
|
||||||
when(transaction.getStatus()).thenReturn(Transaction.Status.GATEWAY_REJECTED);
|
|
||||||
when(transaction.getGatewayRejectionReason()).thenReturn(GatewayRejectionReason.CVV);
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "USD",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Payment rejected: Invalid CVV code.",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGatewayRejectedDueToAddress_returnsErrorResponse() {
|
|
||||||
when(result.isSuccess()).thenReturn(false);
|
|
||||||
when(result.getTransaction()).thenReturn(transaction);
|
|
||||||
when(transaction.getStatus()).thenReturn(Transaction.Status.GATEWAY_REJECTED);
|
|
||||||
when(transaction.getGatewayRejectionReason()).thenReturn(GatewayRejectionReason.AVS);
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "USD",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Payment rejected: Invalid address.",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSettlementDeclined_returnsErrorResponse() {
|
|
||||||
when(result.isSuccess()).thenReturn(false);
|
|
||||||
when(result.getTransaction()).thenReturn(transaction);
|
|
||||||
when(transaction.getStatus()).thenReturn(Transaction.Status.SETTLEMENT_DECLINED);
|
|
||||||
when(transaction.getProcessorSettlementResponseText())
|
|
||||||
.thenReturn("mine eyes have seen the glory of the coming of the borg");
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "USD",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Payment declined: mine eyes have seen the glory of the coming of the borg",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHasTransactionObjectWithWeirdStatus_returnsErrorResponse() {
|
|
||||||
when(result.isSuccess()).thenReturn(false);
|
|
||||||
when(result.getTransaction()).thenReturn(transaction);
|
|
||||||
when(transaction.getStatus()).thenReturn(Transaction.Status.UNRECOGNIZED);
|
|
||||||
when(transaction.getProcessorResponseText())
|
|
||||||
.thenReturn("he is preempting the instance where the deadlocked shards are stored");
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "USD",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Payment failure: "
|
|
||||||
+ "he is preempting the instance where the deadlocked shards are stored",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFailedWithWeirdStatusButNoHelpfulText_usesWeirdStatusAsMsg() {
|
|
||||||
when(result.isSuccess()).thenReturn(false);
|
|
||||||
when(result.getTransaction()).thenReturn(transaction);
|
|
||||||
when(transaction.getStatus()).thenReturn(Transaction.Status.FAILED);
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "USD",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Payment failure: FAILED",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRemoteValidationError_showsErrorOnlyOnFirstFormField() {
|
|
||||||
when(result.isSuccess()).thenReturn(false);
|
|
||||||
when(result.getErrors()).thenReturn(validationErrors);
|
|
||||||
when(validationErrors.getAllDeepValidationErrors())
|
|
||||||
.thenReturn(asList(
|
|
||||||
new ValidationError(
|
|
||||||
"amount",
|
|
||||||
ValidationErrorCode.TRANSACTION_SETTLEMENT_AMOUNT_IS_LESS_THAN_SERVICE_FEE_AMOUNT,
|
|
||||||
"Gimmeh moar moneys"),
|
|
||||||
new ValidationError(
|
|
||||||
"fax",
|
|
||||||
ValidationErrorCode.CUSTOMER_FAX_IS_TOO_LONG,
|
|
||||||
"Fax is too long")));
|
|
||||||
assertThat(
|
|
||||||
paymentAction.handleJsonRequest(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"amount", "123",
|
|
||||||
"currency", "USD",
|
|
||||||
"paymentMethodNonce", "abc-123")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "Gimmeh moar moneys",
|
|
||||||
"field", "amount",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +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.truth.Truth.assertThat;
|
|
||||||
import static google.registry.testing.DatastoreHelper.loadRegistrar;
|
|
||||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
import static org.mockito.Matchers.any;
|
|
||||||
import static org.mockito.Matchers.eq;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.braintreegateway.BraintreeGateway;
|
|
||||||
import com.braintreegateway.ClientTokenGateway;
|
|
||||||
import com.google.appengine.api.users.User;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import google.registry.braintree.BraintreeRegistrarSyncer;
|
|
||||||
import google.registry.model.registrar.Registrar;
|
|
||||||
import google.registry.request.auth.AuthLevel;
|
|
||||||
import google.registry.request.auth.AuthResult;
|
|
||||||
import google.registry.request.auth.UserAuthInfo;
|
|
||||||
import google.registry.testing.AppEngineRule;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import org.joda.money.CurrencyUnit;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Tests for {@link RegistrarPaymentSetupAction}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class RegistrarPaymentSetupActionTest {
|
|
||||||
|
|
||||||
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
|
|
||||||
|
|
||||||
private final BraintreeGateway braintreeGateway = mock(BraintreeGateway.class);
|
|
||||||
private final ClientTokenGateway clientTokenGateway = mock(ClientTokenGateway.class);
|
|
||||||
private final BraintreeRegistrarSyncer customerSyncer = mock(BraintreeRegistrarSyncer.class);
|
|
||||||
private final SessionUtils sessionUtils = mock(SessionUtils.class);
|
|
||||||
|
|
||||||
private final User user = new User("marla.singer@example.com", "gmail.com", "12345");
|
|
||||||
private final RegistrarPaymentSetupAction action = new RegistrarPaymentSetupAction();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() {
|
|
||||||
action.sessionUtils = sessionUtils;
|
|
||||||
action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
|
|
||||||
action.braintreeGateway = braintreeGateway;
|
|
||||||
action.customerSyncer = customerSyncer;
|
|
||||||
Registrar registrar = persistResource(
|
|
||||||
loadRegistrar("TheRegistrar")
|
|
||||||
.asBuilder()
|
|
||||||
.setBillingMethod(Registrar.BillingMethod.BRAINTREE)
|
|
||||||
.build());
|
|
||||||
when(sessionUtils.getRegistrarForAuthResult(
|
|
||||||
any(HttpServletRequest.class), any(AuthResult.class)))
|
|
||||||
.thenReturn(registrar);
|
|
||||||
when(braintreeGateway.clientToken()).thenReturn(clientTokenGateway);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTokenGeneration() {
|
|
||||||
action.brainframe = "/doodle";
|
|
||||||
action.accountIds =
|
|
||||||
ImmutableMap.of(
|
|
||||||
CurrencyUnit.USD, "sorrow",
|
|
||||||
CurrencyUnit.JPY, "torment");
|
|
||||||
String blanketsOfSadness = "our hearts are beating, but no one is breathing";
|
|
||||||
when(clientTokenGateway.generate()).thenReturn(blanketsOfSadness);
|
|
||||||
assertThat(action.handleJsonRequest(ImmutableMap.of()))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "SUCCESS",
|
|
||||||
"message", "Success",
|
|
||||||
"results", asList(
|
|
||||||
ImmutableMap.of(
|
|
||||||
"token", blanketsOfSadness,
|
|
||||||
"currencies", asList("USD", "JPY"),
|
|
||||||
"brainframe", "/doodle")));
|
|
||||||
verify(customerSyncer).sync(eq(loadRegistrar("TheRegistrar")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNonEmptyRequestObject_returnsError() {
|
|
||||||
assertThat(action.handleJsonRequest(ImmutableMap.of("oh", "no")))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "JSON request object must be empty",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNotOnCreditCardBillingTerms_showsErrorPage() {
|
|
||||||
Registrar registrar = persistResource(
|
|
||||||
loadRegistrar("TheRegistrar")
|
|
||||||
.asBuilder()
|
|
||||||
.setBillingMethod(Registrar.BillingMethod.EXTERNAL)
|
|
||||||
.build());
|
|
||||||
when(sessionUtils.getRegistrarForAuthResult(
|
|
||||||
any(HttpServletRequest.class), any(AuthResult.class)))
|
|
||||||
.thenReturn(registrar);
|
|
||||||
assertThat(action.handleJsonRequest(ImmutableMap.of()))
|
|
||||||
.containsExactly(
|
|
||||||
"status", "ERROR",
|
|
||||||
"message", "not-using-cc-billing",
|
|
||||||
"results", asList());
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue