Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add self serve billing management UI #5431

Merged
merged 80 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
2440f32
Add basic plan info
AdityaHegde Aug 12, 2024
acb2da5
Add actions for switching plans
AdityaHegde Aug 19, 2024
44a38ab
Add payment section
AdityaHegde Aug 19, 2024
475dbd0
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Aug 21, 2024
441e0bf
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Aug 26, 2024
662bfee
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Sep 19, 2024
418e180
Use new APIs
AdityaHegde Sep 19, 2024
376cbaa
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Sep 23, 2024
96d26a0
Add banners for trial and billing issues
AdityaHegde Sep 23, 2024
72e6508
Improve trial end math
AdityaHegde Sep 24, 2024
1f71f66
Add TRIAL_ENDING_SOON state
AdityaHegde Sep 24, 2024
fade30f
Remove TRIAL_ENDING_SOON
AdityaHegde Sep 30, 2024
83ae354
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Sep 30, 2024
e5af9dd
Add plan pill
AdityaHegde Sep 30, 2024
c337e64
Add start team plan variants
AdityaHegde Sep 30, 2024
a16c1f7
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 1, 2024
6ba3913
Handle payment issues
AdityaHegde Oct 1, 2024
b60caca
Handle some edge cases
AdityaHegde Oct 1, 2024
368dd77
Open stripe page before plan upgrade
AdityaHegde Oct 2, 2024
ebc8e6e
Use billing issue to show trial dates
AdityaHegde Oct 3, 2024
7730520
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 3, 2024
5a3105c
Avoid using ListPlans API
AdityaHegde Oct 3, 2024
c4ea1c3
Add null checks
AdityaHegde Oct 3, 2024
8310b96
Fix trial grace period end check
AdityaHegde Oct 4, 2024
df1f20b
Handle cancelled subscriptions
AdityaHegde Oct 4, 2024
9752b05
UXQA fixes - pass 1
AdityaHegde Oct 5, 2024
fc7e934
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 7, 2024
3dabb6a
Use RenewBillingSubscription
AdityaHegde Oct 7, 2024
1d61d74
Handle no subscription case
AdityaHegde Oct 8, 2024
e993973
Always fetch stripe page
AdityaHegde Oct 8, 2024
1e5f1dd
Embed Orb customer portal
AdityaHegde Oct 8, 2024
d4b790c
Open upgrader in same page
AdityaHegde Oct 8, 2024
79f4874
Tweaks
AdityaHegde Oct 8, 2024
883738d
Fix misc issues
AdityaHegde Oct 8, 2024
2198ea1
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 9, 2024
599d568
UXQA - Pass 2
AdityaHegde Oct 9, 2024
7560fa8
Handle viewer cases
AdityaHegde Oct 9, 2024
3613cc3
Refactor to use billing issues message in hibernating message
AdityaHegde Oct 10, 2024
9c2b883
Support PAYMENT_FAILED_ISSUE
AdityaHegde Oct 10, 2024
cfe2460
Add wake all projects
AdityaHegde Oct 10, 2024
98e75c8
UXQA - Pass 3
AdityaHegde Oct 11, 2024
187bb17
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 11, 2024
d2148b1
Hibernating message improvements
AdityaHegde Oct 14, 2024
c4d546b
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 14, 2024
e382acc
Fix hibernate sub cancel check
AdityaHegde Oct 14, 2024
c6389c2
minor changes (#5890)
pjain1 Oct 14, 2024
e49a3d8
Revert asset deletion
AdityaHegde Oct 15, 2024
24acbb9
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 16, 2024
d65af0d
UXQA - Pass 4
AdityaHegde Oct 16, 2024
3d0718c
Add back quotas
AdityaHegde Oct 16, 2024
de504fe
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 17, 2024
f6fc368
Hardcode plan names
AdityaHegde Oct 17, 2024
ab7e0f1
Add CTAs to existing email formats
AdityaHegde Oct 17, 2024
323699b
UI Review comments
AdityaHegde Oct 18, 2024
472e5de
PR comments pass 2
AdityaHegde Oct 21, 2024
dfd796b
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 22, 2024
1be4252
Fix upgrade callback url to stripe
AdityaHegde Oct 22, 2024
4c04d87
PR comments pass 3
AdityaHegde Oct 23, 2024
a671dd4
Styling updates
AdityaHegde Oct 24, 2024
5674229
Add spinner to iframe loading
AdityaHegde Oct 24, 2024
8ec01f5
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 24, 2024
6d57a5c
Add POC plan support
AdityaHegde Oct 24, 2024
4b87ed9
Tweaks
AdityaHegde Oct 24, 2024
8dcd55b
PR comments pass 4
AdityaHegde Oct 25, 2024
f4301e5
fix start trial race condition (#5971)
pjain1 Oct 25, 2024
a8e41c2
web-auth: use svelte and vite-plugin-singlefile (#5965)
briangregoryholmes Oct 24, 2024
d6ce8c4
Share Project Popover (#5887)
lovincyrus Oct 24, 2024
0d774ea
disable preview button when explore resource is reconciling (#5962)
briangregoryholmes Oct 24, 2024
06c444c
fix: display null values with italics in TDD (#5945)
briangregoryholmes Oct 25, 2024
c535b01
completely remove org from orb and stripe (#5955)
pjain1 Oct 25, 2024
035155b
Don't log request cancellation as internal errors in the Github APIs …
begelundmuller Oct 25, 2024
87b95ae
trigger org repair manually (#5936)
pjain1 Oct 25, 2024
2af7c7d
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 28, 2024
4b14bf3
Fix public URLs
AdityaHegde Oct 28, 2024
7c5ba24
Fix upgrade emails
AdityaHegde Oct 28, 2024
52df6b5
Merge branch 'main' into adityahegde/billing-ui
AdityaHegde Oct 28, 2024
0dc2a39
Fix lint
AdityaHegde Oct 28, 2024
691c99a
PR comments pass 5
AdityaHegde Oct 28, 2024
1deb7fb
check org billing init in start trial job (#5983)
pjain1 Oct 28, 2024
15132a4
Add back commented email
AdityaHegde Oct 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions admin/database/postgres/migrations/0049.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE assets ALTER COLUMN org_id DROP NOT NULL;
8 changes: 5 additions & 3 deletions admin/jobs/river/biller_event_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func (w *PaymentFailedWorker) Work(ctx context.Context, job *river.Job[PaymentFa
OrgName: org.Name,
Currency: job.Args.Currency,
Amount: job.Args.Amount,
PaymentURL: w.admin.URLs.PaymentPortal(org.Name),
GracePeriodEndDate: gracePeriodEndDate,
})
if err != nil {
Expand Down Expand Up @@ -242,9 +243,10 @@ func (w *PaymentFailedGracePeriodCheckWorker) paymentFailedGracePeriodCheck(ctx

// send email
err = w.admin.Email.SendInvoiceUnpaid(&email.InvoiceUnpaid{
ToEmail: org.BillingEmail,
ToName: org.Name,
OrgName: org.Name,
ToEmail: org.BillingEmail,
ToName: org.Name,
OrgName: org.Name,
PaymentURL: w.admin.URLs.PaymentPortal(org.Name),
})
if err != nil {
return fmt.Errorf("failed to send project hibernated due to payment overdue email for org %q: %w", org.Name, err)
Expand Down
9 changes: 6 additions & 3 deletions admin/jobs/river/trial_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func (w *TrialEndingSoonWorker) trialEndingSoon(ctx context.Context) error {
ToEmail: org.BillingEmail,
ToName: org.Name,
OrgName: org.Name,
UpgradeURL: w.admin.URLs.UpgradePlan(org.Name),
TrialEndDate: m.EndDate,
})
if err != nil {
Expand Down Expand Up @@ -160,6 +161,7 @@ func (w *TrialEndCheckWorker) trialEndCheck(ctx context.Context) error {
ToEmail: org.BillingEmail,
ToName: org.Name,
OrgName: org.Name,
UpgradeURL: w.admin.URLs.UpgradePlan(org.Name),
GracePeriodEndDate: m.GracePeriodEndDate,
})
if err != nil {
Expand Down Expand Up @@ -281,9 +283,10 @@ func (w *TrialGracePeriodCheckWorker) trialGracePeriodCheck(ctx context.Context)

// send email
err = w.admin.Email.SendTrialGracePeriodEnded(&email.TrialGracePeriodEnded{
ToEmail: org.BillingEmail,
ToName: org.Name,
OrgName: org.Name,
ToEmail: org.BillingEmail,
ToName: org.Name,
OrgName: org.Name,
UpgradeURL: w.admin.URLs.UpgradePlan(org.Name),
})
if err != nil {
return fmt.Errorf("failed to send trial grace period ended email for org %q: %w", org.Name, err)
Expand Down
57 changes: 41 additions & 16 deletions admin/server/billing.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,18 @@
return nil, status.Error(codes.Internal, err.Error())
}

//
//err = s.admin.Email.SendSubscriptionCancelled(&email.SubscriptionCancelled{

Check failure on line 237 in admin/server/billing.go

View workflow job for this annotation

GitHub Actions / lint

commentFormatting: put a space between `//` and comment text (gocritic)
// ToEmail: org.BillingEmail,
// ToName: org.Name,
// OrgName: org.Name,
// PlanName: "Team Plan", // TODO: will this ever be different?
// EndDate: endDate,
//})
//if err != nil {
// return nil, status.Error(codes.Internal, err.Error())
//}

s.logger.Named("billing").Warn("subscription cancelled", zap.String("org_id", org.ID), zap.String("org_name", org.Name))

return &adminv1.CancelBillingSubscriptionResponse{}, nil
Expand Down Expand Up @@ -362,6 +374,11 @@
return nil, status.Error(codes.FailedPrecondition, "payment customer not initialized yet for the organization")
}

// returnUrl is mandatory so if not passed default to home page
if req.ReturnUrl == "" {
req.ReturnUrl = s.admin.URLs.Frontend()
}

url, err := s.admin.PaymentProvider.GetBillingPortalURL(ctx, org.PaymentCustomerID, req.ReturnUrl)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
Expand Down Expand Up @@ -728,11 +745,11 @@
return &adminv1.Subscription{
Id: sub.ID,
Plan: billingPlanToDTO(sub.Plan),
StartDate: timestamppb.New(sub.StartDate),
EndDate: timestamppb.New(sub.EndDate),
CurrentBillingCycleStartDate: timestamppb.New(sub.CurrentBillingCycleStartDate),
CurrentBillingCycleEndDate: timestamppb.New(sub.CurrentBillingCycleEndDate),
TrialEndDate: timestamppb.New(sub.TrialEndDate),
StartDate: valOrNullTime(sub.StartDate),
EndDate: valOrNullTime(sub.EndDate),
CurrentBillingCycleStartDate: valOrNullTime(sub.CurrentBillingCycleStartDate),
CurrentBillingCycleEndDate: valOrNullTime(sub.CurrentBillingCycleEndDate),
TrialEndDate: valOrNullTime(sub.TrialEndDate),
}
}

Expand Down Expand Up @@ -815,17 +832,17 @@
return &adminv1.BillingIssueMetadata{
Metadata: &adminv1.BillingIssueMetadata_OnTrial{
OnTrial: &adminv1.BillingIssueMetadataOnTrial{
EndDate: timestamppb.New(m.(*database.BillingIssueMetadataOnTrial).EndDate),
GracePeriodEndDate: timestamppb.New(m.(*database.BillingIssueMetadataOnTrial).GracePeriodEndDate),
EndDate: valOrNullTime(m.(*database.BillingIssueMetadataOnTrial).EndDate),
GracePeriodEndDate: valOrNullTime(m.(*database.BillingIssueMetadataOnTrial).GracePeriodEndDate),
},
},
}
case database.BillingIssueTypeTrialEnded:
return &adminv1.BillingIssueMetadata{
Metadata: &adminv1.BillingIssueMetadata_TrialEnded{
TrialEnded: &adminv1.BillingIssueMetadataTrialEnded{
EndDate: timestamppb.New(m.(*database.BillingIssueMetadataTrialEnded).EndDate),
GracePeriodEndDate: timestamppb.New(m.(*database.BillingIssueMetadataTrialEnded).GracePeriodEndDate),
EndDate: valOrNullTime(m.(*database.BillingIssueMetadataTrialEnded).EndDate),
GracePeriodEndDate: valOrNullTime(m.(*database.BillingIssueMetadataTrialEnded).GracePeriodEndDate),
},
},
}
Expand All @@ -846,12 +863,13 @@
invoices := make([]*adminv1.BillingIssueMetadataPaymentFailedMeta, 0)
for k := range paymentFailed.Invoices {
invoices = append(invoices, &adminv1.BillingIssueMetadataPaymentFailedMeta{
InvoiceId: paymentFailed.Invoices[k].ID,
InvoiceNumber: paymentFailed.Invoices[k].Number,
InvoiceUrl: paymentFailed.Invoices[k].URL,
AmountDue: paymentFailed.Invoices[k].Amount,
Currency: paymentFailed.Invoices[k].Currency,
DueDate: timestamppb.New(paymentFailed.Invoices[k].DueDate),
InvoiceId: paymentFailed.Invoices[k].ID,
InvoiceNumber: paymentFailed.Invoices[k].Number,
InvoiceUrl: paymentFailed.Invoices[k].URL,
AmountDue: paymentFailed.Invoices[k].Amount,
Currency: paymentFailed.Invoices[k].Currency,
DueDate: valOrNullTime(paymentFailed.Invoices[k].DueDate),
GracePeriodEndDate: valOrNullTime(paymentFailed.Invoices[k].GracePeriodEndDate),
})
}
return &adminv1.BillingIssueMetadata{
Expand All @@ -865,7 +883,7 @@
return &adminv1.BillingIssueMetadata{
Metadata: &adminv1.BillingIssueMetadata_SubscriptionCancelled{
SubscriptionCancelled: &adminv1.BillingIssueMetadataSubscriptionCancelled{
EndDate: timestamppb.New(m.(*database.BillingIssueMetadataSubscriptionCancelled).EndDate),
EndDate: valOrNullTime(m.(*database.BillingIssueMetadataSubscriptionCancelled).EndDate),
},
},
}
Expand Down Expand Up @@ -917,6 +935,13 @@
return *v
}

func valOrNullTime(v time.Time) *timestamppb.Timestamp {
if v.IsZero() {
return nil
}
return timestamppb.New(v)
}

func biggerOfInt(ptr *int, def int) int {
if ptr == nil {
return def
Expand Down
11 changes: 11 additions & 0 deletions admin/urls.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,14 @@ func (u *URLs) AlertOpen(org, project, alert string) string {
func (u *URLs) AlertEdit(org, project, alert string) string {
return urlutil.MustJoinURL(u.Frontend(), org, project, "-", "alerts", alert)
}

// UpgradePlan returns the landing page URL to either upgrade to plan or redirect to payment portal if there are any issues.
func (u *URLs) UpgradePlan(org string) string {
return urlutil.MustJoinURL(u.Frontend(), org, "-", "settings", "billing", "upgrade")
}

// PaymentPortal returns the landing page url that redirects user to payment portal
// Since the payment link can expire it is generated in this landing page on demand.
func (u *URLs) PaymentPortal(org string) string {
return urlutil.MustJoinURL(u.Frontend(), org, "-", "settings", "billing", "payment")
}
13 changes: 13 additions & 0 deletions admin/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,19 @@ func (s *Service) CreateOrganizationForUser(ctx context.Context, userID, email,

s.Logger.Info("created org", zap.String("name", orgName), zap.String("user_id", userID))

// raise never subscribed billing issue in sync to prevent race condition where first project is deployed before issue is raised and thus trial not started
if s.Biller.Name() != "noop" {
_, err := s.DB.UpsertBillingIssue(ctx, &database.UpsertBillingIssueOptions{
OrgID: org.ID,
Type: database.BillingIssueTypeNeverSubscribed,
Metadata: database.BillingIssueMetadataNeverSubscribed{},
EventTime: org.CreatedOn,
})
if err != nil {
return nil, fmt.Errorf("failed to upsert billing error: %w", err)
}
}

// Submit job to init org billing // TODO modify river client to allow job submission as part of transaction
_, err = s.Jobs.InitOrgBilling(ctx, org.ID)
if err != nil {
Expand Down
101 changes: 67 additions & 34 deletions runtime/pkg/email/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,16 +362,19 @@ type InvoicePaymentFailed struct {
OrgName string
Currency string
Amount string
PaymentURL string
GracePeriodEndDate time.Time
}

func (c *Client) SendInvoicePaymentFailed(opts *InvoicePaymentFailed) error {
return c.SendInformational(&Informational{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Payment for %s has failed", opts.OrgName),
Title: fmt.Sprintf("Payment for %s has failed", opts.OrgName),
Body: template.HTML(fmt.Sprintf("The payment of %s%s for your %q Rill subscription has failed. Your projects will be hibenrated on %s if payment not received.", opts.Currency, opts.Amount, opts.OrgName, opts.GracePeriodEndDate.Format("January 2, 2006"))),
return c.SendCallToAction(&CallToAction{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Payment for %s has failed", opts.OrgName),
Title: fmt.Sprintf("Payment for %s has failed", opts.OrgName),
Body: template.HTML(fmt.Sprintf("The payment of %s%s for your %q Rill subscription has failed. Your projects will be hibenrated on %s if payment not received.", opts.Currency, opts.Amount, opts.OrgName, opts.GracePeriodEndDate.Format("January 2, 2006"))),
ButtonText: "Update Payment Info",
ButtonLink: opts.PaymentURL,
})
}

Expand All @@ -395,19 +398,40 @@ func (c *Client) SendInvoicePaymentSuccess(opts *InvoicePaymentSuccess) error {
}

type InvoiceUnpaid struct {
ToEmail string
ToName string
OrgName string
ToEmail string
ToName string
OrgName string
PaymentURL string
}

// SendInvoiceUnpaid sent after the payment grace period has ended
func (c *Client) SendInvoiceUnpaid(opts *InvoiceUnpaid) error {
return c.SendCallToAction(&CallToAction{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Payment for %s is overdue", opts.OrgName),
Title: fmt.Sprintf("Payment for %s is overdue", opts.OrgName),
Body: template.HTML(fmt.Sprintf("The payment for your Rill subscription on %q is overdue. Your projects have been hibernated.", opts.OrgName)),
ButtonText: "Update Payment Info",
ButtonLink: opts.PaymentURL,
})
}

type SubscriptionCancelled struct {
ToEmail string
ToName string
OrgName string
PlanName string
EndDate time.Time
}

func (c *Client) SendSubscriptionCancelled(opts *SubscriptionCancelled) error {
return c.SendInformational(&Informational{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Payment for %s is overdue", opts.OrgName),
Title: fmt.Sprintf("Payment for %s is overdue", opts.OrgName),
Body: template.HTML(fmt.Sprintf("The payment for your Rill subscription on %q is overdue. Your projects have been hibernated.", opts.OrgName)),
Subject: fmt.Sprintf("Subscription was cancelled %s", opts.OrgName),
Title: fmt.Sprintf("Subscription was cancelled %s", opts.OrgName),
Body: template.HTML(fmt.Sprintf("You’ve successfully canceled your %s plan. Your access will continue until %s", opts.PlanName, opts.EndDate.Format("January 2, 2006"))),
})
}

Expand Down Expand Up @@ -448,49 +472,58 @@ type TrialEndingSoon struct {
ToEmail string
ToName string
OrgName string
UpgradeURL string
TrialEndDate time.Time
}

func (c *Client) SendTrialEndingSoon(opts *TrialEndingSoon) error {
return c.SendInformational(&Informational{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your trial for %s is ending soon", opts.OrgName),
Title: fmt.Sprintf("Your trial for %s is ending soon", opts.OrgName),
Body: template.HTML(fmt.Sprintf("Your trial for %q is ending on %s. Upgrade to a paid plan to continue using Rill.", opts.OrgName, opts.TrialEndDate.Format("January 2, 2006"))),
return c.SendCallToAction(&CallToAction{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your trial for %s is ending soon", opts.OrgName),
Title: fmt.Sprintf("Your trial for %s is ending soon", opts.OrgName),
Body: template.HTML(fmt.Sprintf("Your trial for %q is ending on %s. Upgrade to a paid plan to continue using Rill.", opts.OrgName, opts.TrialEndDate.Format("January 2, 2006"))),
ButtonText: "Upgrade Now",
ButtonLink: opts.UpgradeURL,
})
}

type TrialEnded struct {
ToEmail string
ToName string
OrgName string
UpgradeURL string
GracePeriodEndDate time.Time
}

func (c *Client) SendTrialEnded(opts *TrialEnded) error {
return c.SendInformational(&Informational{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your trial for %s has ended", opts.OrgName),
Title: fmt.Sprintf("Your trial for %s has ended", opts.OrgName),
Body: template.HTML(fmt.Sprintf("Your trial for %q has ended. Your projects will be hibernated on %s. Upgrade to a paid plan to continue using Rill.", opts.OrgName, opts.GracePeriodEndDate.Format("January 2, 2006"))),
return c.SendCallToAction(&CallToAction{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your trial for %s has ended", opts.OrgName),
Title: fmt.Sprintf("Your trial for %s has ended", opts.OrgName),
Body: template.HTML(fmt.Sprintf("Your trial for %q has ended. Your projects will be hibernated on %s. Upgrade to a paid plan to continue using Rill.", opts.OrgName, opts.GracePeriodEndDate.Format("January 2, 2006"))),
ButtonText: "Upgrade to Team Plan",
pjain1 marked this conversation as resolved.
Show resolved Hide resolved
ButtonLink: opts.UpgradeURL,
})
}

type TrialGracePeriodEnded struct {
ToEmail string
ToName string
OrgName string
ToEmail string
ToName string
OrgName string
UpgradeURL string
}

func (c *Client) SendTrialGracePeriodEnded(opts *TrialGracePeriodEnded) error {
return c.SendInformational(&Informational{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your trial grace period has ended for %s", opts.OrgName),
Title: fmt.Sprintf("Your trial grace period has ended for %s", opts.OrgName),
Body: template.HTML(fmt.Sprintf("Your trial grace period has ended for %q. Your projects have been hibernated. Please visit the billing portal to enter payment method and upgrade your plan to continue using Rill.", opts.OrgName)),
return c.SendCallToAction(&CallToAction{
ToEmail: opts.ToEmail,
ToName: opts.ToName,
Subject: fmt.Sprintf("Your trial grace period has ended for %s", opts.OrgName),
Title: fmt.Sprintf("Your trial grace period has ended for %s", opts.OrgName),
Body: template.HTML(fmt.Sprintf("Your trial grace period has ended for %q. Your projects have been hibernated. Please visit the billing portal to enter payment method and upgrade your plan to continue using Rill.", opts.OrgName)),
ButtonText: "Upgrade to Team Plan",
pjain1 marked this conversation as resolved.
Show resolved Hide resolved
ButtonLink: opts.UpgradeURL,
})
}

Expand All @@ -507,6 +540,6 @@ func (c *Client) SendTrialExtended(opts *TrialExtended) error {
ToName: opts.ToName,
Subject: fmt.Sprintf("Your trial for %s has been extended", opts.OrgName),
Title: fmt.Sprintf("Your trial for %s has been extened", opts.OrgName),
Body: template.HTML(fmt.Sprintf("Welcome to Rill! Your trial for %q has been extended and will end on %s.", opts.OrgName, opts.TrialEndDate.Format("January 2, 2006"))),
Body: template.HTML(fmt.Sprintf("Your trial for %q has been extended and will end on %s.", opts.OrgName, opts.TrialEndDate.Format("January 2, 2006"))),
})
}
Loading
Loading