Skip to content

Commit

Permalink
Add provided_payment_method email
Browse files Browse the repository at this point in the history
  • Loading branch information
jshearer committed Jan 30, 2024
1 parent 6ad20a5 commit 05500f2
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 1 deletion.
50 changes: 50 additions & 0 deletions supabase/functions/alerts/alert_types/provided_payment_method.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { AlertRecord, EmailConfig } from "../index.ts";
import { commonTemplate } from "../template.ts";

interface ProvidedPaymentMethod {
// This feels like it should apply to all alert types, and doesn't belong here..
recipients: {
email: string;
full_name: string | null;
}[];
trial_start: string;
trial_end: string;
tenant: string;
in_trial: boolean;
straight_from_free_tier: boolean;
}

type ProvidedPaymentMethodRecord = AlertRecord<"provided_payment_method", ProvidedPaymentMethod>;

const ProvidedPaymentMethod = (req: ProvidedPaymentMethodRecord): EmailConfig[] => {
return req.arguments.recipients.map((recipient) => ({
emails: [recipient.email],
subject: `Estuary Flow: Payment Method Entered 🎉`,
content: commonTemplate(`
<mj-text font-size="20px" color="#512d0b"><strong>Dear ${recipient.full_name},</strong></mj-text>
<mj-text font-size="17px" line-height="1.3">Thank you for entering your payment information. ${
req.arguments.in_trial
? `After your free trial ends on <strong>${req.arguments.trial_end}</strong>, your account will begin accruing usage. Your dataflows will not be interrupted.`
: req.arguments.straight_from_free_tier
? `Your account will now begin accruing usage. Your dataflows will not be interrupted.`
: `Your account is now active.`
}
<mj-button background-color="#5072EB" color="white" href="https://dashboard.estuary.dev/admin/billing" padding="25px 0 0 0" font-weight="400" font-size="17px">📈 See your bill</mj-button>
<mj-divider border-width="1px" border-style="dashed" border-color="lightgrey" />
<mj-text align="center" font-weight="bold" font-size="24pt">Frequently Asked Questions</mj-text>
<mj-divider border-width="1px" border-style="dashed" border-color="lightgrey" />
<mj-text align="left" font-weight="bold" font-size="18pt">Where is my data stored?</mj-text>
<mj-text align="left" font-size="16pt">By default, all collection data is stored in an Estuary-owned cloud storage bucket with a 30 day retention plicy. Now that you have a paid account, you can update this to store data in your own cloud storage bucket. We support <strong>GCS</strong>, <strong>S3</strong>, and <strong>Azure Blob storage</strong>.</mj-text>
`),
}));
};

export const ProvidedPaymentMethodEmail = (request: ProvidedPaymentMethodRecord): EmailConfig[] => {
if (request.resolved_at) {
// Do we want to send a "cc confirmed" email when this alert stops firing?
return [];
} else {
return ProvidedPaymentMethod(request);
}
};
5 changes: 5 additions & 0 deletions supabase/functions/alerts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { freeTierExceededEmail } from "./alert_types/free_tier_exceeded.ts";
import { freeTrialEndedEmail } from "./alert_types/free_trial_ended.ts";
import { freeTrialEndingEmail } from "./alert_types/free_trial_ending.ts";
import { freeTrialGracePeriodOverEmail } from "./alert_types/free_trial_grace_period_over.ts";
import { ProvidedPaymentMethodEmail } from "./alert_types/provided_payment_method.ts";

export interface AlertRecord<T extends keyof typeof emailTemplates, A> {
alert_type: T;
Expand All @@ -27,6 +28,7 @@ const emailTemplates = {
"free_trial_ended": freeTrialEndedEmail,
"free_trial_ending": freeTrialEndingEmail,
"free_trial_grace_period_over": freeTrialGracePeriodOverEmail,
"provided_payment_method": ProvidedPaymentMethodEmail,
};

// This is a temporary type guard for the POST request that provides shallow validation
Expand Down Expand Up @@ -159,6 +161,9 @@ serve(async (rawRequest: Request): Promise<Response> => {
case "free_trial_grace_period_over":
pendingEmails = emailTemplates[request.alert_type](request);
break;
case "provided_payment_method":
pendingEmails = emailTemplates[request.alert_type](request);
break;
default: {
// This checks that we have an exhaustive match. If this line has a
// type error, make sure you have a case above for every key in `emailTemplates`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,34 @@ group by
customers.name,
users.raw_user_meta_data;

-- Alert us internally when they go past 5 days over the trial
create or replace view internal.provided_payment_method_firing as
select
'provided_payment_method' as alert_type,
tenants.tenant || 'alerts/provided_payment_method' as catalog_name,
alert_subscriptions.email,
auth.users.raw_user_meta_data->>'full_name' as full_name,
tenants.tenant,
tenants.trial_start,
tenants.trial_start + interval '1 month' as trial_end,
-- if tenants.trial_start is null, that means they entered their cc
-- while they're still in the free tier
coalesce((now() - tenants.trial_start) < interval '1 month', false) as in_trial,
tenants.trial_start is null as straight_from_free_tier
from tenants
left join alert_subscriptions on alert_subscriptions.catalog_prefix ^@ tenants.tenant and email is not null
left join stripe.customers on stripe.customers."name" = tenants.tenant
-- Filter out sso users because auth.users is only guarinteed unique when that is false:
-- CREATE UNIQUE INDEX users_email_partial_key ON auth.users(email text_ops) WHERE is_sso_user = false;
left join auth.users on auth.users.email = alert_subscriptions.email and auth.users.is_sso_user is false
where stripe.customers."invoice_settings/default_payment_method" is not null
group by
tenants.tenant,
tenants.trial_start,
alert_subscriptions.email,
customers.name,
users.raw_user_meta_data;

-- Have to update this to join in auth.users for full_name support
-- Update to v2 because of the change from `emails` to `recipients`
create or replace view internal.alert_data_processing_firing_v2 as
Expand Down Expand Up @@ -254,12 +282,38 @@ free_trial_grace_period_over as (
trial_start,
trial_end,
has_credit_card
),
provided_payment_method_firing as (
select
catalog_name,
alert_type,
json_build_object(
'tenant', tenant,
'recipients', array_agg(json_build_object(
'email', email,
'full_name', full_name
)),
'trial_start', trial_start,
'trial_end', trial_end,
'in_trial', in_trial,
'straight_from_free_tier', straight_from_free_tier
) as arguments
from internal.alert_free_trial_grace_period_over_firing
group by
catalog_name,
tenant,
alert_type,
trial_start,
trial_end,
in_trial
straight_from_free_tier
)
select * from data_processing
union all select * from free_tier_exceeded
union all select * from free_trial_ending
union all select * from free_trial_ended
union all select * from free_trial_grace_period_over
union all select * from provided_payment_method_firing
order by catalog_name asc;

create or replace function internal.send_alerts()
Expand All @@ -277,7 +331,10 @@ if new.alert_type = 'data_not_processed_in_interval' then
to_jsonb(new.*),
headers:=format('{"Content-Type": "application/json", "Authorization": "Basic %s"}', token)::jsonb
);
else
-- Skip all of the past events that got triggered when we added these new event types
-- NOTE: Change this so that the date is the day (or time) that it's deployed
-- so that only "real" events that happen after deployment get sent
else if new.fired_at > '2024-01-30'
perform
net.http_post(
'https://eyrcnmuzzyriypdajwdk.supabase.co/functions/v1/alerts',
Expand Down

0 comments on commit 05500f2

Please sign in to comment.