Skip to content

Commit

Permalink
fix: billing email and deploy paywall copy (#6132)
Browse files Browse the repository at this point in the history
* Update some billing email copy

* Update quota error to match plan

* Fix trial org usage percent

* PR comments
  • Loading branch information
AdityaHegde authored Nov 26, 2024
1 parent dd912e8 commit 3f7e202
Show file tree
Hide file tree
Showing 14 changed files with 76 additions and 53 deletions.
40 changes: 19 additions & 21 deletions runtime/pkg/email/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ func (c *Client) SendInvoicePaymentFailed(opts *InvoicePaymentFailed) error {
ToName: opts.ToName,
Subject: fmt.Sprintf("Payment failed for %s. Please update your payment method", opts.OrgName),
PreButton: template.HTML(fmt.Sprintf(`
We couldn’t process your payment for %s. You have until %s to update your payment details before your org is <a href="https://docs.rilldata.com/home/FAQ#what-is-project-hibernation">hibernating</a>.
We couldn’t process your payment for <b>%s</b>. You have until <b>%s</b> to update your payment details before your org is <a href="https://docs.rilldata.com/home/FAQ#what-is-project-hibernation">hibernating</a>.
`, opts.OrgName, opts.GracePeriodEndDate.Format(dateFormat))),
ButtonText: "Update Payment Info",
ButtonLink: opts.PaymentURL,
Expand All @@ -424,7 +424,7 @@ func (c *Client) SendInvoicePaymentSuccess(opts *InvoicePaymentSuccess) error {
Body: template.HTML(fmt.Sprintf(`
Thank you for your payment!
<br /><br />
Your payment for %s has been successfully processed.
Your payment for <b>%s</b> has been successfully processed.
<br /><br />
If you believe this charge to be in error or have any questions, please email [email protected].
<br /><br />
Expand All @@ -447,7 +447,7 @@ func (c *Client) SendInvoiceUnpaid(opts *InvoiceUnpaid) error {
ToName: opts.ToName,
Subject: fmt.Sprintf("Invoice for %s is now past due. Org is now hibernated", opts.OrgName),
PreButton: template.HTML(fmt.Sprintf(`
%s and its projects have been hibernated due to an overdue payment.
<b>%s</b> and its projects have been hibernated due to an overdue payment.
<br /><br />
Restore access by updating your payment information today!
`, opts.ToName)),
Expand All @@ -473,11 +473,9 @@ func (c *Client) SendSubscriptionCancelled(opts *SubscriptionCancelled) error {
PreButton: template.HTML(fmt.Sprintf(`
We’re sorry to see you go!
<br /><br />
You’ve successfully canceled the %s for %s. You’ll still have access to Rill Cloud until %s. After this date, your subscription will expire, and you will no longer have access.
You’ve successfully canceled the %s plan for <b>%s</b>. You’ll still have access to Rill Cloud until <b>%s</b>. After this date, your subscription will expire, and you will no longer have access.
<br /><br />
If you change your mind, you can always reactivate your subscription!
<br /><br />
If you found that our service did not meet your needs, please reply to this email and we’ll do our best to address your feedback and concerns.
`, opts.PlanName, opts.ToName, opts.EndDate.Format(dateFormat))),
ButtonText: "Billing Settings",
ButtonLink: opts.BillingURL,
Expand All @@ -498,7 +496,7 @@ func (c *Client) SendSubscriptionEnded(opts *SubscriptionEnded) error {
ToName: opts.ToName,
Subject: fmt.Sprintf("Subscription for %s has now ended. Org is hibernated", opts.OrgName),
PreButton: template.HTML(fmt.Sprintf(`
Your cancelled subscription for %s has and its projects are now <a href="https://docs.rilldata.com/home/FAQ#what-is-project-hibernation">hibernating</a>. We hope you enjoyed using Rill Cloud during your time with us.
Your cancelled subscription for <b>%s</b> has and its projects are now <a href="https://docs.rilldata.com/home/FAQ#what-is-project-hibernation">hibernating</a>. We hope you enjoyed using Rill Cloud during your time with us.
<br /><br />
If you’d like to reactive your subscription and regain access, you can easily do so at any time by renewing your subscription from here:
`, opts.OrgName)),
Expand Down Expand Up @@ -527,7 +525,7 @@ func (c *Client) SendTrialStarted(opts *TrialStarted) error {
Subject: fmt.Sprintf("A 30-day free trial for %s has started", opts.OrgName),
FrontendURL: opts.FrontendURL,
WelcomeText: template.HTML(fmt.Sprintf(`
You now have access to Rill Cloud until %s to explore all features. Let us know if you need any help along the way!
You now have access to Rill Cloud until <b>%s</b> to explore all features. Let us know if you need any help along the way!
`, opts.TrialEndDate.Format(dateFormat))),
})
}
Expand All @@ -548,7 +546,7 @@ func (c *Client) SendTrialEndingSoon(opts *TrialEndingSoon) error {
ToName: opts.ToName,
Subject: fmt.Sprintf("Your Rill Cloud trial for %s is expiring in %d days", opts.OrgName, days),
PreButton: template.HTML(fmt.Sprintf(`
Your trial for %s ends on %s.
Your trial for <b>%s</b> ends on <b>%s</b>.
<br /><br />
How's Rill working out for you? Have you checked out our newest features highlighted in our <a href="https://docs.rilldata.com/notes">Release Notes</a>?
<br /><br />
Expand Down Expand Up @@ -577,7 +575,7 @@ func (c *Client) SendTrialEnded(opts *TrialEnded) error {
PreButton: template.HTML(fmt.Sprintf(`
Hi %s,
<br /><br />
Your Rill Cloud trial has now expired. %s will be hibernated on %s. We hope you’ve enjoyed using our software. If you’d like to keep using Rill Cloud, upgrade to our Team Plan!
Your Rill Cloud trial has now expired. <b>%s</b> will be hibernated on <b>%s</b>. We hope you’ve enjoyed using our software. If you’d like to keep using Rill Cloud, upgrade to our Team Plan!
`, opts.ToName, opts.OrgName, opts.GracePeriodEndDate.Format(dateFormat))),
ButtonText: "Upgrade to Team Plan",
ButtonLink: opts.UpgradeURL,
Expand All @@ -598,7 +596,7 @@ func (c *Client) SendTrialGracePeriodEnded(opts *TrialGracePeriodEnded) error {
ToName: opts.ToName,
Subject: fmt.Sprintf("Trial plan grace period for %s has ended. Org is now hibernated", opts.OrgName),
PreButton: template.HTML(fmt.Sprintf(`
%s and its projects are now <a href="https://docs.rilldata.com/home/FAQ#what-is-project-hibernation">hibernating</a>.
<b>%s</b> and its projects are now <a href="https://docs.rilldata.com/home/FAQ#what-is-project-hibernation">hibernating</a>.
<br /><br />
Reactivate your org by upgrading to the Team Plan today!
`, opts.OrgName)),
Expand All @@ -624,7 +622,7 @@ func (c *Client) SendTrialExtended(opts *TrialExtended) error {
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your trial for %s has been extended", opts.OrgName),
Body: template.HTML(fmt.Sprintf("Your trial for %q has been extended until %s.", opts.OrgName, opts.TrialEndDate.Format(dateFormat))),
Body: template.HTML(fmt.Sprintf("Your trial for <b>%q</b> has been extended until %s.", opts.OrgName, opts.TrialEndDate.Format(dateFormat))),
})
}

Expand All @@ -639,8 +637,8 @@ func (c *Client) SendPlanUpdate(opts *PlanUpdate) error {
return c.SendInformational(&Informational{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your plan for %s has been updated to %s", opts.OrgName, opts.PlanName),
Body: template.HTML(fmt.Sprintf("%q has been updated to %q.", opts.OrgName, opts.PlanName)),
Subject: fmt.Sprintf("Your plan for %s has been updated to %s plan", opts.OrgName, opts.PlanName),
Body: template.HTML(fmt.Sprintf("<b>%q</b> has been updated to %q plan.", opts.OrgName, opts.PlanName)),
})
}

Expand All @@ -655,8 +653,8 @@ func (c *Client) SendSubscriptionRenewed(opts *SubscriptionRenewed) error {
return c.SendInformational(&Informational{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your %s subscription for %s has been renewed", opts.PlanName, opts.OrgName),
Body: template.HTML(fmt.Sprintf("Your subscription for %q has been renewed for %q.", opts.OrgName, opts.PlanName)),
Subject: fmt.Sprintf("Your %s subscription for %s plan has been renewed", opts.PlanName, opts.OrgName),
Body: template.HTML(fmt.Sprintf("Your subscription for <b>%q</b> has been renewed for %q plan.", opts.OrgName, opts.PlanName)),
})
}

Expand All @@ -674,12 +672,12 @@ func (c *Client) SendTeamPlanStarted(opts *TeamPlan) error {
return c.SendWelcomeToTeam(&Welcome{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Welcome to the %s", opts.PlanName),
Subject: fmt.Sprintf("Welcome to the %s plan", opts.PlanName),
FrontendURL: opts.FrontendURL,
WelcomeText: template.HTML(fmt.Sprintf(`
Thank you! You’ve successfully upgraded %s to the %s.
Thank you! You’ve successfully upgraded %s to the %s plan.
<br /><br />
Your next billing cycle starts on %s.
Your next billing cycle starts on <b>%s</b>.
`, opts.OrgName, opts.PlanName, opts.BillingStartDate.Format(dateFormat))),
})
}
Expand All @@ -689,10 +687,10 @@ func (c *Client) SendTeamPlanRenewal(opts *TeamPlan) error {
return c.SendWelcomeToTeam(&Welcome{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your %s subscription for %s has been renewed", opts.PlanName, opts.OrgName),
Subject: fmt.Sprintf("Your %s plan subscription for %s has been renewed", opts.PlanName, opts.OrgName),
FrontendURL: opts.FrontendURL,
WelcomeText: template.HTML(fmt.Sprintf(`
Thank you! You’ve successfully renewed to the %s for %s.
Thank you! You’ve successfully renewed to the <b>%s</b> for %s plan.
<br /><br />
Your next billing cycle starts on %s.
`, opts.OrgName, opts.PlanName, opts.BillingStartDate.Format(dateFormat))),
Expand Down
4 changes: 2 additions & 2 deletions runtime/pkg/email/templates/call_to_action.mjml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<mj-body>
<mj-section padding-top="24px">
<mj-column>
<mj-image container-background-color="#FFF" width="48px" src="https://cdn.rilldata.com/email-transactional/rill-logo.png" />
<mj-image container-background-color="#FFF" width="48px" src="https://cdn.rilldata.com/email-transactional/rill-logo-purple-square.png" />
</mj-column>
</mj-section>
<mj-section>
Expand Down Expand Up @@ -58,4 +58,4 @@
</mj-section>

</mj-body>
</mjml>
</mjml>
2 changes: 1 addition & 1 deletion runtime/pkg/email/templates/gen/call_to_action.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
<tbody>
<tr>
<td style="width:48px;">
<img alt="" src="https://cdn.rilldata.com/email-transactional/rill-logo.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="48" height="auto" />
<img alt="" src="https://cdn.rilldata.com/email-transactional/rill-logo-purple-square.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="48" height="auto" />
</td>
</tr>
</tbody>
Expand Down
2 changes: 1 addition & 1 deletion runtime/pkg/email/templates/gen/informational.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
<tbody>
<tr>
<td style="width:48px;">
<img alt="" src="https://cdn.rilldata.com/email-transactional/rill-logo.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="48" height="auto" />
<img alt="" src="https://cdn.rilldata.com/email-transactional/rill-logo-purple-square.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="48" height="auto" />
</td>
</tr>
</tbody>
Expand Down
7 changes: 1 addition & 6 deletions runtime/pkg/email/templates/gen/welcome_to_team.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,18 +111,13 @@
<tbody>
<tr>
<td style="width:48px;">
<img alt="" src="https://cdn.rilldata.com/email-transactional/rill-logo.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="48" height="auto" />
<img alt="" src="https://cdn.rilldata.com/email-transactional/rill-logo-purple-square.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="48" height="auto" />
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Helvetica;font-size:20px;line-height:1.25;text-align:left;color:#4b5563;">Welcome to Rill</div>
</td>
</tr>
</tbody>
</table>
</td>
Expand Down
2 changes: 1 addition & 1 deletion runtime/pkg/email/templates/gen/welcome_to_trial.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
<tbody>
<tr>
<td style="width:48px;">
<img alt="" src="https://cdn.rilldata.com/email-transactional/rill-logo.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="48" height="auto" />
<img alt="" src="https://cdn.rilldata.com/email-transactional/rill-logo-purple-square.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="48" height="auto" />
</td>
</tr>
</tbody>
Expand Down
4 changes: 2 additions & 2 deletions runtime/pkg/email/templates/informational.mjml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<mj-body>
<mj-section padding-top="24px">
<mj-column>
<mj-image container-background-color="#FFF" width="48px" src="https://cdn.rilldata.com/email-transactional/rill-logo.png" />
<mj-image container-background-color="#FFF" width="48px" src="https://cdn.rilldata.com/email-transactional/rill-logo-purple-square.png" />
</mj-column>
</mj-section>
<mj-section>
Expand Down Expand Up @@ -49,4 +49,4 @@
</mj-section>

</mj-body>
</mjml>
</mjml>
8 changes: 2 additions & 6 deletions runtime/pkg/email/templates/welcome_to_team.mjml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@
<mj-body>
<mj-section padding-top="24px">
<mj-column>
<mj-image container-background-color="#FFF" width="48px" src="https://cdn.rilldata.com/email-transactional/rill-logo.png" />

<mj-text font-size="20px">
Welcome to Rill
</mj-text>
<mj-image container-background-color="#FFF" width="48px" src="https://cdn.rilldata.com/email-transactional/rill-logo-purple-square.png" />
</mj-column>
</mj-section>
<mj-section>
Expand Down Expand Up @@ -62,4 +58,4 @@
</mj-section>

</mj-body>
</mjml>
</mjml>
4 changes: 2 additions & 2 deletions runtime/pkg/email/templates/welcome_to_trial.mjml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<mj-body>
<mj-section padding-top="24px">
<mj-column>
<mj-image container-background-color="#FFF" width="48px" src="https://cdn.rilldata.com/email-transactional/rill-logo.png" />
<mj-image container-background-color="#FFF" width="48px" src="https://cdn.rilldata.com/email-transactional/rill-logo-purple-square.png" />

<mj-text font-size="20px">
Welcome to Rill
Expand Down Expand Up @@ -66,4 +66,4 @@
</mj-section>

</mj-body>
</mjml>
</mjml>
2 changes: 1 addition & 1 deletion web-admin/src/features/billing/plans/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function formatUsageVsQuota(
const formattedUsage = formatMemorySize(usageInBytes);
const formattedQuota = formatMemorySize(quota);
const percent =
formattedUsage > formattedQuota
usageInBytes > quota
? "100+"
: Math.round((usageInBytes * 100) / quota) + "";
return `${formattedUsage} of ${formattedQuota} (${percent}%)`;
Expand Down
8 changes: 8 additions & 0 deletions web-common/src/features/billing/issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,11 @@ import {
export function getNeverSubscribedIssue(issues: BillingIssue[]) {
return issues.find((i) => i.type === BillingIssueType.NEVER_SUBSCRIBED);
}

export function getTrialIssue(issues: BillingIssue[]) {
return issues.find(
(i) =>
i.type === BillingIssueType.ON_TRIAL ||
i.type === BillingIssueType.TRIAL_ENDED,
);
}
28 changes: 24 additions & 4 deletions web-common/src/features/project/ProjectDeployer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { page } from "$app/stores";
import type { ConnectError } from "@connectrpc/connect";
import { getTrialIssue } from "@rilldata/web-common/features/billing/issues";
import { sanitizeOrgName } from "@rilldata/web-common/features/organization/sanitizeOrgName";
import {
DeployErrorType,
extractDeployError,
getPrettyDeployError,
} from "@rilldata/web-common/features/project/deploy-errors";
import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient";
import { waitUntil } from "@rilldata/web-common/lib/waitUtils";
Expand All @@ -19,6 +20,7 @@ import {
createLocalServiceGetCurrentProject,
createLocalServiceGetCurrentUser,
createLocalServiceGetMetadata,
createLocalServiceListOrganizationsAndBillingMetadataRequest,
createLocalServiceRedeploy,
getLocalServiceGetCurrentUserQueryKey,
localServiceGetCurrentUser,
Expand All @@ -27,6 +29,8 @@ import { derived, get, writable } from "svelte/store";

export class ProjectDeployer {
public readonly metadata = createLocalServiceGetMetadata();
public readonly orgsMetadata =
createLocalServiceListOrganizationsAndBillingMetadataRequest();
public readonly user = createLocalServiceGetCurrentUser();
public readonly project = createLocalServiceGetCurrentProject();
public readonly promptOrgSelection = writable(false);
Expand All @@ -53,27 +57,43 @@ export class ProjectDeployer {
return derived(
[
this.metadata,
this.orgsMetadata,
this.user,
this.project,
this.org,
this.deployMutation,
this.redeployMutation,
],
([metadata, user, project, deployMutation, redeployMutation]) => {
([
metadata,
orgsMetadata,
user,
project,
org,
deployMutation,
redeployMutation,
]) => {
if (
metadata.error ||
orgsMetadata.error ||
user.error ||
project.error ||
deployMutation.error ||
redeployMutation.error
) {
const orgMetadata = orgsMetadata?.data?.orgs.find(
(om) => om.name === org,
);
const onTrial = !!getTrialIssue(orgMetadata?.issues ?? []);
return {
isLoading: false,
error: extractDeployError(
error: getPrettyDeployError(
(metadata.error as ConnectError) ??
(user.error as ConnectError) ??
(project.error as ConnectError) ??
(deployMutation.error as ConnectError) ??
(redeployMutation.error as ConnectError),
onTrial,
),
};
}
Expand Down Expand Up @@ -189,7 +209,7 @@ export class ProjectDeployer {
);
return resp.frontendUrl;
} catch (e) {
const err = extractDeployError(e);
const err = getPrettyDeployError(e, false);
if (err.type === DeployErrorType.PermissionDenied && checkNextOrg) {
i++;
} else {
Expand Down
9 changes: 7 additions & 2 deletions web-common/src/features/project/deploy-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ export type DeployError = {
message: string;
};

export function extractDeployError(error: ConnectError): DeployError {
export function getPrettyDeployError(
error: ConnectError,
orgOnTrial: boolean,
): DeployError {
if (!error) {
return {
type: DeployErrorType.Unknown,
Expand Down Expand Up @@ -81,10 +84,12 @@ export function extractDeployError(error: ConnectError): DeployError {
const projectQuotaMatch = ProjectQuotaErrorMatcher.exec(desc);
if (projectQuotaMatch?.length) {
const projectQuota = Number(projectQuotaMatch[1]);
const planLabel = orgOnTrial ? "current plan" : "trial plan";

return {
type: DeployErrorType.ProjectLimitHit,
title: "To deploy this project, start a Team plan",
message: `Your trial plan is limited to ${projectQuota} project${projectQuota > 1 ? "s" : ""}. To have unlimited projects, upgrade to a Team plan.`,
message: `Your ${planLabel} is limited to ${projectQuota} project${projectQuota > 1 ? "s" : ""}. To have unlimited projects, upgrade to a Team plan.`,
};
}

Expand Down
Loading

1 comment on commit 3f7e202

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.