Pārlūkot izejas kodu

allow user to select preferred plan and supply payment information with registration

tags/v0.9.14
Jonathan Cobb pirms 4 gadiem
vecāks
revīzija
42c31cad79
16 mainītis faili ar 146 papildinājumiem un 43 dzēšanām
  1. +4
    -1
      bubble-server/src/main/java/bubble/auth/BubbleAuthFilter.java
  2. +12
    -3
      bubble-server/src/main/java/bubble/cloud/payment/stripe/StripePaymentDriver.java
  3. +0
    -4
      bubble-server/src/main/java/bubble/dao/account/AccountDAO.java
  4. +20
    -0
      bubble-server/src/main/java/bubble/dao/account/AccountInitializer.java
  5. +3
    -1
      bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java
  6. +6
    -1
      bubble-server/src/main/java/bubble/model/account/Account.java
  7. +3
    -0
      bubble-server/src/main/java/bubble/model/account/AccountRegistration.java
  8. +4
    -0
      bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java
  9. +8
    -5
      bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java
  10. +9
    -4
      bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java
  11. +37
    -1
      bubble-server/src/main/java/bubble/resources/account/AuthResource.java
  12. +6
    -2
      bubble-server/src/main/java/bubble/resources/bill/AllPaymentMethodsResource.java
  13. +9
    -0
      bubble-server/src/main/java/bubble/resources/bill/BubblePlansResource.java
  14. +0
    -20
      bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties
  15. +24
    -0
      bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties
  16. +1
    -1
      bubble-web

+ 4
- 1
bubble-server/src/main/java/bubble/auth/BubbleAuthFilter.java Parādīt failu

@@ -28,7 +28,10 @@ import static bubble.server.BubbleServer.isRestoreMode;
public class BubbleAuthFilter extends AuthFilter<Account> {

public static final Set<String> 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<String> SKIP_AUTH_PATHS = new SingletonSet<>(AUTH_ENDPOINT);


+ 12
- 3
bubble-server/src/main/java/bubble/cloud/payment/stripe/StripePaymentDriver.java Parādīt failu

@@ -110,9 +110,18 @@ public class StripePaymentDriver extends PaymentDriverBase<StripePaymentDriverCo
final AccountPolicy policy = policyDAO.findSingleByAccount(accountPaymentMethod.getAccount());
if (policy == null) return new PaymentValidationResult("err.paymentInfo.emailRequired");

final String email = policy.getFirstVerifiedEmail();
if (email == null && policy.getFirstEmail() != null) {
return new PaymentValidationResult("err.paymentInfo.verifiedEmailRequired");

final String email;
if (accountPaymentMethod.requireValidatedEmail()) {
email = policy.getFirstVerifiedEmail();
if (email == null && policy.getFirstEmail() != null) {
return new PaymentValidationResult("err.paymentInfo.verifiedEmailRequired");
}
} else {
email = policy.getFirstEmail();
if (email == null) {
return new PaymentValidationResult("err.paymentInfo.verifiedEmailRequired");
}
}

final Map<String, Object> customerParams = new HashMap<>();


+ 0
- 4
bubble-server/src/main/java/bubble/dao/account/AccountDAO.java Parādīt failu

@@ -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<Account> 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);
}
});


+ 20
- 0
bubble-server/src/main/java/bubble/dao/account/AccountInitializer.java Parādīt failu

@@ -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<Exception> 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);
}
}
}

+ 3
- 1
bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java Parādīt failu

@@ -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<AccountPlan> {
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<AccountPlan> {
}

@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()) {


+ 6
- 1
bubble-server/src/main/java/bubble/model/account/Account.java Parādīt failu

@@ -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(); }


+ 3
- 0
bubble-server/src/main/java/bubble/model/account/AccountRegistration.java Parādīt failu

@@ -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; }
}

+ 4
- 0
bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java Parādīt failu

@@ -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());


+ 8
- 5
bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java Parādīt failu

@@ -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");
}
}
}
}


+ 9
- 4
bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java Parādīt failu

@@ -111,20 +111,25 @@ public class AccountOwnedResource<E extends HasAccount, DAO extends AccountOwned
public Response view(@Context ContainerRequest ctx,
@PathParam("id") String id) {

final Account caller = userPrincipal(ctx);
final String accountUuid = getAccountUuid(ctx);
if (!caller.admin() && !caller.getUuid().equals(accountUuid)) return notFound();
final Account caller = getAccountForViewById(ctx);
E found = find(ctx, id);

if (found == null) {
found = findAlternate(ctx, id);
if (found == null) return notFound(id);
}
if (!found.getAccount().equals(caller.getUuid()) && !caller.admin()) return notFound(id);
if (caller != null && !found.getAccount().equals(caller.getUuid()) && !caller.admin()) return notFound(id);

return ok(populate(ctx, found));
}

public Account getAccountForViewById(ContainerRequest ctx) {
final Account caller = userPrincipal(ctx);
final String accountUuid = getAccountUuid(ctx);
if (!caller.admin() && !caller.getUuid().equals(accountUuid)) throw notFoundEx();
return caller;
}

@PUT
public Response create(@Context Request req,
@Context ContainerRequest ctx,


+ 37
- 1
bubble-server/src/main/java/bubble/resources/account/AuthResource.java Parādīt failu

@@ -8,12 +8,15 @@ import bubble.dao.SessionDAO;
import bubble.dao.account.AccountDAO;
import bubble.dao.account.AccountPolicyDAO;
import bubble.dao.account.message.AccountMessageDAO;
import bubble.dao.bill.AccountPaymentMethodDAO;
import bubble.dao.bill.BubblePlanDAO;
import bubble.dao.cloud.BubbleNodeDAO;
import bubble.dao.cloud.BubbleNodeKeyDAO;
import bubble.model.CertType;
import bubble.model.account.*;
import bubble.model.account.message.*;
import bubble.model.bill.AccountPaymentMethod;
import bubble.model.bill.BubblePlan;
import bubble.model.boot.ActivationRequest;
import bubble.model.cloud.BubbleNetwork;
import bubble.model.cloud.BubbleNode;
@@ -23,8 +26,8 @@ import bubble.model.cloud.notify.NotificationReceipt;
import bubble.model.device.BubbleDeviceType;
import bubble.model.device.Device;
import bubble.server.BubbleConfiguration;
import bubble.service.account.StandardAuthenticatorService;
import bubble.service.account.StandardAccountMessageService;
import bubble.service.account.StandardAuthenticatorService;
import bubble.service.backup.RestoreService;
import bubble.service.bill.PromotionService;
import bubble.service.boot.ActivationService;
@@ -63,6 +66,8 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON;
import static org.cobbzilla.util.http.HttpContentTypes.CONTENT_TYPE_ANY;
import static org.cobbzilla.util.json.JsonUtil.COMPACT_MAPPER;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.util.string.LocaleUtil.currencyForLocale;
import static org.cobbzilla.util.system.Sleep.sleep;
import static org.cobbzilla.wizard.resources.ResourceUtil.*;
@@ -81,6 +86,7 @@ public class AuthResource {
@Autowired private SessionDAO sessionDAO;
@Autowired private ActivationService activationService;
@Autowired private AccountMessageDAO accountMessageDAO;
@Autowired private AccountPaymentMethodDAO accountPaymentMethodDAO;
@Autowired private StandardAccountMessageService messageService;
@Autowired private BubblePlanDAO planDAO;
@Autowired private BubbleNodeDAO nodeDAO;
@@ -240,6 +246,11 @@ public class AuthResource {
}
}

if (request.hasPreferredPlan()) {
final BubblePlan plan = planDAO.findById(request.getPreferredPlan());
if (plan == null) errors.addViolation("err.plan.notFound");
}

if (errors.isInvalid()) return invalid(errors);

final String parentUuid = thisNetwork.getTag(TAG_PARENT_ACCOUNT, thisNetwork.getAccount());
@@ -249,12 +260,37 @@ public class AuthResource {
final Account account = accountDAO.newAccount(req, null, request, parent);
SimpleViolationException promoEx = null;
if (configuration.paymentsEnabled()) {
if (request.hasPaymentMethod()) {
final AccountPaymentMethod paymentMethodObject = request.getPaymentMethodObject();
log.info("register: found AccountPaymentMethod at registration-time: " + json(paymentMethodObject, COMPACT_MAPPER));
paymentMethodObject.setUuid(null);
paymentMethodObject.setAccount(account.getUuid());
paymentMethodObject.setRequireValidatedEmail(false);
account.waitForAccountInit(); // payment clouds for user must exist before we can create the APM
final ValidationResult result = new ValidationResult();
log.info("register: starting validation of payment method with requireValidatedEmail="+paymentMethodObject.requireValidatedEmail());
paymentMethodObject.validate(result, configuration);
if (result.isInvalid()) {
account.getAccountInitializer().setAbort();
final AccountPolicy policy = policyDAO.findSingleByAccount(account.getUuid());
policyDAO.update(policy.setDeletionPolicy(AccountDeletionPolicy.full_delete));
while (!account.getAccountInitializer().completed()) {
sleep(SECONDS.toMillis(1), "waiting for account initialization to complete before deleting");
}
accountDAO.delete(account.getUuid());
throw invalidEx(result);
}
log.info("register: creating AccountPaymentMethod upon registration: " + json(paymentMethodObject, COMPACT_MAPPER));
final AccountPaymentMethod apm = accountPaymentMethodDAO.create(paymentMethodObject);
log.info("register: created AccountPaymentMethod upon registration: " + apm.getUuid());
}
try {
promoService.applyPromotions(account, request.getPromoCode(), currency);
} catch (SimpleViolationException e) {
promoEx = e;
}
}
account.getAccountInitializer().setCanSendAccountMessages();
return ok(account
.waitForAccountInit()
.setPromoError(promoEx == null ? null : promoEx.getMessageTemplate())


+ 6
- 2
bubble-server/src/main/java/bubble/resources/bill/AllPaymentMethodsResource.java Parādīt failu

@@ -5,6 +5,7 @@
package bubble.resources.bill;

import bubble.cloud.CloudServiceType;
import bubble.dao.account.AccountDAO;
import bubble.dao.cloud.CloudServiceDAO;
import bubble.model.account.Account;
import bubble.model.bill.PaymentMethodType;
@@ -33,13 +34,16 @@ import static org.cobbzilla.wizard.resources.ResourceUtil.*;
public class AllPaymentMethodsResource {

@Autowired private CloudServiceDAO cloudDAO;
@Autowired private AccountDAO accountDAO;
@Autowired private BubbleConfiguration configuration;

@GET
public Response listPaymentMethods(@Context ContainerRequest ctx,
@QueryParam("type") PaymentMethodType type) {
final Account account = userPrincipal(ctx);
final List<CloudService> allPaymentServices = cloudDAO.findByAccountAndType(account.getUuid(), CloudServiceType.payment);
final Account account = optionalUserPrincipal(ctx);
final List<CloudService> allPaymentServices = account != null
? cloudDAO.findByAccountAndType(account.getUuid(), CloudServiceType.payment)
: cloudDAO.findPublicTemplatesByType(accountDAO.getFirstAdmin().getUuid(), CloudServiceType.payment);
final Set<PaymentMethodType> typesFound = new HashSet<>();
final List<CloudService> paymentServices = new ArrayList<>();
for (CloudService cloud : allPaymentServices) {


+ 9
- 0
bubble-server/src/main/java/bubble/resources/bill/BubblePlansResource.java Parādīt failu

@@ -41,6 +41,11 @@ public class BubblePlansResource extends AccountOwnedResource<BubblePlan, Bubble
@Autowired private BubblePlanAppDAO planAppDAO;
@Autowired private BubbleAppDAO appDAO;

// allow unauthenticated users to read plans
@Override public Account getAccountForViewById(ContainerRequest ctx) {
return optionalUserPrincipal(ctx);
}

@Override protected BubblePlan setReferences(ContainerRequest ctx, Account caller, BubblePlan bubblePlan) {
if (empty(bubblePlan.getChargeName())) throw invalidEx("err.chargeName.required");
if (bubblePlan.getChargeName().length() > MAX_CHARGENAME_LEN) throw invalidEx("err.chargeName.length");
@@ -67,8 +72,12 @@ public class BubblePlansResource extends AccountOwnedResource<BubblePlan, Bubble
}

@Override protected BubblePlan populate(ContainerRequest ctx, BubblePlan plan) {
final Account account = optionalUserPrincipal(ctx);
final List<BubbleApp> apps = getAppsForPlan(plan);
plan.setApps(apps);
if (account == null) {
plan.getApps().forEach(app -> app.setDataConfig(null));
}
return super.populate(ctx, plan);
}



+ 0
- 20
bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties Parādīt failu

@@ -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


+ 24
- 0
bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties Parādīt failu

@@ -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 <a target="_blank" rel="noopener" href="https://bubblev.com/pages/privacy">Privacy Policy</a> and <a target="_blank" rel="noopener" href="https://bubblev.com/pages/terms">Terms of Service</a>.
message_login_agreeToTerms=By logging in, you agree to the <a target="_blank" rel="noopener" href="https://bubblev.com/pages/privacy">Privacy Policy</a> and <a target="_blank" rel="noopener" href="https://bubblev.com/pages/terms">Terms of Service</a>.
@@ -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


+ 1
- 1
bubble-web

@@ -1 +1 @@
Subproject commit 2da83c231b0a07b723ba60e33e2e1a540311cceb
Subproject commit 372ad8a74fddeab1cb174685bd7f2cde919ac48e

Notiek ielāde…
Atcelt
Saglabāt