From 42c31cad79379142ce58590263c9ba715aa04489 Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Wed, 18 Mar 2020 15:45:20 -0400 Subject: [PATCH] allow user to select preferred plan and supply payment information with registration --- .../java/bubble/auth/BubbleAuthFilter.java | 5 ++- .../payment/stripe/StripePaymentDriver.java | 15 ++++++-- .../java/bubble/dao/account/AccountDAO.java | 4 -- .../dao/account/AccountInitializer.java | 20 ++++++++++ .../java/bubble/dao/bill/AccountPlanDAO.java | 4 +- .../java/bubble/model/account/Account.java | 7 +++- .../model/account/AccountRegistration.java | 3 ++ .../model/bill/AccountPaymentMethod.java | 4 ++ .../bubble/model/cloud/BubbleNetwork.java | 13 ++++--- .../account/AccountOwnedResource.java | 13 +++++-- .../resources/account/AuthResource.java | 38 ++++++++++++++++++- .../bill/AllPaymentMethodsResource.java | 8 +++- .../resources/bill/BubblePlansResource.java | 9 +++++ .../post_auth/ResourceMessages.properties | 20 ---------- .../pre_auth/ResourceMessages.properties | 24 ++++++++++++ bubble-web | 2 +- 16 files changed, 146 insertions(+), 43 deletions(-) diff --git a/bubble-server/src/main/java/bubble/auth/BubbleAuthFilter.java b/bubble-server/src/main/java/bubble/auth/BubbleAuthFilter.java index 2871495f..a790f3bc 100644 --- a/bubble-server/src/main/java/bubble/auth/BubbleAuthFilter.java +++ b/bubble-server/src/main/java/bubble/auth/BubbleAuthFilter.java @@ -28,7 +28,10 @@ import static bubble.server.BubbleServer.isRestoreMode; public class BubbleAuthFilter extends AuthFilter { public static final Set SKIP_AUTH_PREFIXES = new HashSet<>(Arrays.asList( - AUTH_ENDPOINT, ENTITY_CONFIGS_ENDPOINT, BUBBLE_MAGIC_ENDPOINT, MESSAGES_ENDPOINT, TIMEZONES_ENDPOINT, + AUTH_ENDPOINT, ENTITY_CONFIGS_ENDPOINT, + PLANS_ENDPOINT, PAYMENT_METHODS_ENDPOINT, + BUBBLE_MAGIC_ENDPOINT, + MESSAGES_ENDPOINT, TIMEZONES_ENDPOINT, NOTIFY_ENDPOINT, FILTER_HTTP_ENDPOINT, DETECT_ENDPOINT )); public static final Set SKIP_AUTH_PATHS = new SingletonSet<>(AUTH_ENDPOINT); diff --git a/bubble-server/src/main/java/bubble/cloud/payment/stripe/StripePaymentDriver.java b/bubble-server/src/main/java/bubble/cloud/payment/stripe/StripePaymentDriver.java index 015ec122..a7d5abf2 100644 --- a/bubble-server/src/main/java/bubble/cloud/payment/stripe/StripePaymentDriver.java +++ b/bubble-server/src/main/java/bubble/cloud/payment/stripe/StripePaymentDriver.java @@ -110,9 +110,18 @@ public class StripePaymentDriver extends PaymentDriverBase customerParams = new HashMap<>(); diff --git a/bubble-server/src/main/java/bubble/dao/account/AccountDAO.java b/bubble-server/src/main/java/bubble/dao/account/AccountDAO.java index 032fb4bd..39e43157 100644 --- a/bubble-server/src/main/java/bubble/dao/account/AccountDAO.java +++ b/bubble-server/src/main/java/bubble/dao/account/AccountDAO.java @@ -46,8 +46,6 @@ import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; import static java.lang.Thread.currentThread; import static java.util.concurrent.TimeUnit.MINUTES; import static org.cobbzilla.util.daemon.ZillaRuntime.daemon; -import static org.cobbzilla.util.json.JsonUtil.COMPACT_MAPPER; -import static org.cobbzilla.util.json.JsonUtil.json; import static org.cobbzilla.wizard.model.IdentifiableBase.CTIME_ASC; import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; @@ -257,11 +255,9 @@ public class AccountDAO extends AbstractCRUDDAO implements SqlViewSearc return ((AppSiteDAO) dao).findByAccountAndAppAndId(accountUuid, apps.get(parentEntity.getApp()).getUuid(), parentEntity.getName()); } @Override public AppSite preCreate(AppSite parentEntity, AppSite accountEntity) { - log.info("CopyTemplate.AppSite.preCreate: site="+json(accountEntity, COMPACT_MAPPER)); return accountEntity.setApp(apps.get(parentEntity.getApp()).getUuid()); } @Override public void postCreate(AppSite parentEntity, AppSite accountEntity) { - log.info("CopyTemplate.AppSite.postCreate: site="+json(accountEntity, COMPACT_MAPPER)); sites.put(parentEntity.getUuid(), accountEntity); } }); diff --git a/bubble-server/src/main/java/bubble/dao/account/AccountInitializer.java b/bubble-server/src/main/java/bubble/dao/account/AccountInitializer.java index 30c4429f..8c60b82a 100644 --- a/bubble-server/src/main/java/bubble/dao/account/AccountInitializer.java +++ b/bubble-server/src/main/java/bubble/dao/account/AccountInitializer.java @@ -27,6 +27,7 @@ public class AccountInitializer implements Runnable { public static final int MAX_ACCOUNT_INIT_RETRIES = 3; public static final long COPY_WAIT_TIME = SECONDS.toMillis(2); + public static final long SEND_MESSAGE_WAIT_TIME = SECONDS.toMillis(1); private Account account; private AccountDAO accountDAO; @@ -36,6 +37,15 @@ public class AccountInitializer implements Runnable { private AtomicBoolean ready = new AtomicBoolean(false); public boolean ready() { return ready.get(); } + private AtomicBoolean canSendAccountMessages = new AtomicBoolean(false); + public void setCanSendAccountMessages() { canSendAccountMessages.set(true); } + + private AtomicBoolean abort = new AtomicBoolean(false); + public void setAbort () { abort.set(true); } + + private AtomicBoolean completed = new AtomicBoolean(false); + public boolean completed () { return completed.get(); } + private AtomicReference error = new AtomicReference<>(); public Exception getError() { return error.get(); } public boolean hasError () { return getError() != null; } @@ -56,6 +66,14 @@ public class AccountInitializer implements Runnable { sleep(COPY_WAIT_TIME, "waiting before copyTemplates"); accountDAO.copyTemplates(account, ready); + while (!canSendAccountMessages.get() && !abort.get()) { + sleep(SEND_MESSAGE_WAIT_TIME, "waiting before sending welcome message"); + } + if (abort.get()) { + log.warn("aborting!"); + return; + } + if (account.hasPolicy() && account.getPolicy().hasAccountContacts()) { messageDAO.sendVerifyRequest(account.getRemoteHost(), account, account.getPolicy().getAccountContacts()[0]); } @@ -82,6 +100,8 @@ public class AccountInitializer implements Runnable { error.set(e); // todo: send to errbit die("error: "+e, e); + } finally { + completed.set(true); } } } diff --git a/bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java b/bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java index ac45cf9f..74efdb7a 100644 --- a/bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java +++ b/bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java @@ -5,6 +5,7 @@ package bubble.dao.bill; import bubble.cloud.payment.PaymentServiceDriver; +import bubble.dao.account.AccountDAO; import bubble.dao.account.AccountOwnedEntityDAO; import bubble.dao.cloud.BubbleNetworkDAO; import bubble.dao.cloud.CloudServiceDAO; @@ -39,6 +40,7 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO { public static final long PURCHASE_DELAY = SECONDS.toMillis(3); @Autowired private BubblePlanDAO planDAO; + @Autowired private AccountDAO accountDAO; @Autowired private BillDAO billDAO; @Autowired private CloudServiceDAO cloudDAO; @Autowired private BubbleNetworkDAO networkDAO; @@ -77,7 +79,7 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO { } @Override public Object preCreate(AccountPlan accountPlan) { - final ValidationResult errors = validateHostname(accountPlan); + final ValidationResult errors = validateHostname(accountPlan, accountDAO, networkDAO); if (errors.isInvalid()) throw invalidEx(errors); if (configuration.paymentsEnabled()) { diff --git a/bubble-server/src/main/java/bubble/model/account/Account.java b/bubble-server/src/main/java/bubble/model/account/Account.java index f95b87ed..06de5f4c 100644 --- a/bubble-server/src/main/java/bubble/model/account/Account.java +++ b/bubble-server/src/main/java/bubble/model/account/Account.java @@ -86,7 +86,8 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci public static final String[] UPDATE_FIELDS = {"url", "description", "autoUpdatePolicy"}; public static final String[] ADMIN_UPDATE_FIELDS = ArrayUtil.append(UPDATE_FIELDS, "suspended", "admin"); - public static final String[] CREATE_FIELDS = ArrayUtil.append(ADMIN_UPDATE_FIELDS, "name", "referralCode", "termsAgreed"); + public static final String[] CREATE_FIELDS = ArrayUtil.append(ADMIN_UPDATE_FIELDS, + "name", "referralCode", "termsAgreed", "preferredPlan"); public static final String ROOT_USERNAME = "root"; public static final int NAME_MIN_LENGTH = 4; @@ -191,6 +192,10 @@ public class Account extends IdentifiableBaseParentEntity implements TokenPrinci return new ConstraintViolationBean("err.password.invalid", "Password must contain at least one letter, one number, and one special character"); } + @Column(length=UUID_MAXLEN) + @Getter @Setter private String preferredPlan; + public boolean hasPreferredPlan () { return !empty(preferredPlan); } + @Embedded @Getter @Setter private AutoUpdatePolicy autoUpdatePolicy; public boolean wantsNewStuff () { return autoUpdatePolicy != null && autoUpdatePolicy.newStuff(); } diff --git a/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java b/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java index bd389103..7733a4c0 100644 --- a/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java +++ b/bubble-server/src/main/java/bubble/model/account/AccountRegistration.java @@ -4,6 +4,7 @@ */ package bubble.model.account; +import bubble.model.bill.AccountPaymentMethod; import lombok.Getter; import lombok.Setter; @@ -22,4 +23,6 @@ public class AccountRegistration extends Account { @Getter @Setter private Boolean agreeToTerms = null; public boolean agreeToTerms () { return agreeToTerms != null && agreeToTerms; } + @Getter @Setter private AccountPaymentMethod paymentMethodObject; + public boolean hasPaymentMethod () { return paymentMethodObject != null; } } diff --git a/bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java b/bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java index fae51659..3ccc58e4 100644 --- a/bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java +++ b/bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java @@ -98,6 +98,9 @@ public class AccountPaymentMethod extends IdentifiableBase implements HasAccount @JsonProperty @Override public long getCtime () { return super.getCtime(); } + @Transient @Getter @Setter private transient Boolean requireValidatedEmail = null; + public boolean requireValidatedEmail() { return requireValidatedEmail == null || requireValidatedEmail; } + public ValidationResult validate(ValidationResult result, BubbleConfiguration configuration) { if (!hasPaymentMethodType()) { @@ -139,6 +142,7 @@ public class AccountPaymentMethod extends IdentifiableBase implements HasAccount if (empty(getPaymentInfo())) { result.addViolation("err.paymentInfo.required"); } else { + log.info("validate: starting validation of payment method with this.requireValidatedEmail="+requireValidatedEmail); final PaymentValidationResult validationResult = paymentDriver.validate(this); if (validationResult.hasErrors()) { result.addAll(validationResult.getViolations()); diff --git a/bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java b/bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java index 0167b73a..aaf3546a 100644 --- a/bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java +++ b/bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java @@ -202,12 +202,15 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu errors.addViolation("err.name.length"); } else if (name.length() < NETWORK_NAME_MINLEN) { errors.addViolation("err.name.tooShort"); - } else if (networkDAO.findByNameAndDomainUuid(name, request.getDomain()) != null) { - errors.addViolation("err.name.alreadyInUse"); } else { - final Account acct = accountDAO.findByName(name); - if (acct != null && !acct.getUuid().equals(request.getAccount())) { - errors.addViolation("err.name.reservedForAccount"); + final BubbleNetwork network = networkDAO.findByNameAndDomainUuid(name, request.getDomain()); + if (network != null && !network.getUuid().equals(request.getNetwork())) { + errors.addViolation("err.name.alreadyInUse"); + } else { + final Account acct = accountDAO.findByName(name); + if (acct != null && !acct.getUuid().equals(request.getAccount())) { + errors.addViolation("err.name.reservedForAccount"); + } } } } diff --git a/bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java b/bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java index 084a0982..3097b6c4 100644 --- a/bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java +++ b/bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java @@ -111,20 +111,25 @@ public class AccountOwnedResource allPaymentServices = cloudDAO.findByAccountAndType(account.getUuid(), CloudServiceType.payment); + final Account account = optionalUserPrincipal(ctx); + final List allPaymentServices = account != null + ? cloudDAO.findByAccountAndType(account.getUuid(), CloudServiceType.payment) + : cloudDAO.findPublicTemplatesByType(accountDAO.getFirstAdmin().getUuid(), CloudServiceType.payment); final Set typesFound = new HashSet<>(); final List paymentServices = new ArrayList<>(); for (CloudService cloud : allPaymentServices) { diff --git a/bubble-server/src/main/java/bubble/resources/bill/BubblePlansResource.java b/bubble-server/src/main/java/bubble/resources/bill/BubblePlansResource.java index 24f8bf08..ae9acc79 100644 --- a/bubble-server/src/main/java/bubble/resources/bill/BubblePlansResource.java +++ b/bubble-server/src/main/java/bubble/resources/bill/BubblePlansResource.java @@ -41,6 +41,11 @@ public class BubblePlansResource extends AccountOwnedResource MAX_CHARGENAME_LEN) throw invalidEx("err.chargeName.length"); @@ -67,8 +72,12 @@ public class BubblePlansResource extends AccountOwnedResource apps = getAppsForPlan(plan); plan.setApps(apps); + if (account == null) { + plan.getApps().forEach(app -> app.setDataConfig(null)); + } return super.populate(ctx, plan); } diff --git a/bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties b/bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties index 9c1dcda2..c08a08ff 100644 --- a/bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties +++ b/bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties @@ -310,26 +310,6 @@ footprint_description_EU=Your Bubble will only run within European Union countri footprint_name_Worldwide=World-wide footprint_description_Worldwide=Your Bubble can run anywhere in the world -# Payment methods -payment_description_credit=Credit or Debit Card -payment_description_code=Invitation Code -payment_description_free=FREE! -message_payment_not_supported=This system is not configured to handle payments of this type - -# Invite code payment fields -field_payment_invite_code=Invitation Code -button_label_submit_invite_code=Use Invite Code -message_verified_invite_code=Invite Code Successfully Verified - -# Free payment fields -button_label_submit_free_pay=Click here to use the FREE payment method -message_verified_free_pay=Free Payment Successfully Applied - -# Credit payment fields -field_payment_card_number=Credit or Debit Card -button_label_submit_card=Verify Card -message_verified_card=Card Successfully Verified - # Launch progress meter: pre-launch (standard) ticks meter_tick_confirming_network_lock=Confirming network lock meter_tick_validating_node_network_and_plan=Verifying settings for Bubble diff --git a/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties b/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties index bd3befff..bb603210 100644 --- a/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties +++ b/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties @@ -268,6 +268,8 @@ form_title_register=Register field_label_contactType=Contact Type field_label_email=Email field_label_promoCode=Beta Invite Code +message_request_promoCode=Don't have an invite code? Request one here. +message_request_promoCode_link=https://bubblev.com/beta field_label_sms=SMS Phone field_label_agreeToTerms=By checking this box, you agree to our Privacy Policy and Terms of Service. message_login_agreeToTerms=By logging in, you agree to the Privacy Policy and Terms of Service. @@ -278,6 +280,8 @@ field_label_paymentMethod=Payment Method field_label_newPaymentMethod=Add a Payment Method field_label_existingPaymentMethod=Payment Method err_noPaymentMethods=No payment methods are configured. Contact the administrator of this system. +err.paymentMethod.required=Payment information is required +err.plan.notFound=Plan not found button_label_login=Login button_label_register=Register button_label_forgotPassword=Forgot Password @@ -287,6 +291,26 @@ form_title_forgotPassword=Forgot Password button_label_resetPassword=Request Password Reset message_resetPassword_sent=Password Reset Message Successfully Sent +# Payment methods +payment_description_credit=Credit or Debit Card +payment_description_code=Invitation Code +payment_description_free=FREE! +message_payment_not_supported=This system is not configured to handle payments of this type + +# Invite code payment fields +field_payment_invite_code=Invitation Code +button_label_submit_invite_code=Use Invite Code +message_verified_invite_code=Invite Code Successfully Verified + +# Free payment fields +button_label_submit_free_pay=Click here to use the FREE payment method +message_verified_free_pay=Free Payment Successfully Applied + +# Credit payment fields +field_payment_card_number=Credit or Debit Card +button_label_submit_card=Verify Card +message_verified_card=Card Successfully Verified + # Change Password / Set Password pages form_title_change_password=Change Password form_title_set_password=Set Password diff --git a/bubble-web b/bubble-web index 2da83c23..372ad8a7 160000 --- a/bubble-web +++ b/bubble-web @@ -1 +1 @@ -Subproject commit 2da83c231b0a07b723ba60e33e2e1a540311cceb +Subproject commit 372ad8a74fddeab1cb174685bd7f2cde919ac48e