Skip to content

Commit f80d64e

Browse files
authored
Sales: send a Slack message to be able to contact the customer (#11437)
* Sales: send a Slack message to be able to contact the customer Closes readthedocs/readthedocs-corporate#1797 * Add comment for the setting
1 parent 1396849 commit f80d64e

File tree

2 files changed

+92
-1
lines changed

2 files changed

+92
-1
lines changed

readthedocs/settings/base.py

+4
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,10 @@ def SOCIALACCOUNT_PROVIDERS(self):
848848
# we can't make use of the DJSTRIPE_SUBSCRIBER_MODEL setting.
849849
DJSTRIPE_SUBSCRIBER_CUSTOMER_KEY = None
850850

851+
# Webhook URL for BotDog to post messages in Slack #sales channel:
852+
# https://api.slack.com/apps/A01ML7J7N4T/incoming-webhooks
853+
SLACK_WEBHOOK_SALES_CHANNEL = None # https://hooks.slack.com/services/...
854+
851855
# Do Not Track support
852856
DO_NOT_TRACK_ENABLED = False
853857

readthedocs/subscriptions/event_handlers.py

+88-1
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,20 @@
33
44
https://docs.dj-stripe.dev/en/master/usage/webhooks/.
55
"""
6+
import requests
67
import structlog
78
from django.conf import settings
9+
from django.contrib import humanize
10+
from django.db.models import Sum
811
from django.utils import timezone
912
from djstripe import models as djstripe
1013
from djstripe import webhooks
11-
from djstripe.enums import SubscriptionStatus
14+
from djstripe.enums import ChargeStatus, SubscriptionStatus
1215

1316
from readthedocs.organizations.models import Organization
1417
from readthedocs.payments.utils import cancel_subscription as cancel_stripe_subscription
18+
from readthedocs.projects.models import Domain
19+
from readthedocs.sso.models import SSOIntegration
1520
from readthedocs.subscriptions.notifications import (
1621
SubscriptionEndedNotification,
1722
SubscriptionRequiredNotification,
@@ -191,6 +196,88 @@ def subscription_canceled(event):
191196
notification.send()
192197
log.info("Notification sent.", recipient=owner)
193198

199+
total_spent = (
200+
stripe_subscription.customer.charges.filter(status=ChargeStatus.succeeded)
201+
.aggregate(total=Sum("amount"))
202+
.get("total")
203+
or 0
204+
)
205+
if settings.SLACK_WEBHOOK_SALES_CHANNEL and total_spent > 0:
206+
start_date = stripe_subscription.start_date.strftime("%b %-d, %Y")
207+
timesince = humanize.naturaltime(stripe_subscription.start_date).split(",")[0]
208+
domains = Domain.objects.filter(
209+
project__organizations__in=[organization]
210+
).count()
211+
try:
212+
sso_integration = organization.ssointegration.provider
213+
except SSOIntegration.DoesNotExist:
214+
sso_integration = "Read the Docs Auth"
215+
216+
slack_message = {
217+
"blocks": [
218+
{
219+
"type": "section",
220+
"text": {"type": "mrkdwn", "text": ":x: *Subscription canceled*"},
221+
},
222+
{"type": "divider"},
223+
{
224+
"type": "section",
225+
"fields": [
226+
{
227+
"type": "mrkdwn",
228+
"text": f":office: *Name:* {organization.name}",
229+
},
230+
{
231+
"type": "mrkdwn",
232+
"text": f":dollar: *Plan:* {stripe_subscription.plan.id}",
233+
},
234+
{
235+
"type": "mrkdwn",
236+
"text": f":hash: *Slug:* {organization.slug}",
237+
},
238+
{
239+
"type": "mrkdwn",
240+
"text": f":person_frowning: *Stripe customer:* <https://dashboard.stripe.com/customers/{stripe_subscription.customer_id}|{stripe_subscription.customer_id}>",
241+
},
242+
{
243+
"type": "mrkdwn",
244+
"text": f":date: *Customer since:* {start_date} (~{timesince})",
245+
},
246+
{
247+
"type": "mrkdwn",
248+
"text": f":books: *Projects:* {organization.projects.count()}",
249+
},
250+
{"type": "mrkdwn", "text": f":link: *Domains:* {domains}"},
251+
{
252+
"type": "mrkdwn",
253+
"text": f":closed_lock_with_key: *Authentication:* {sso_integration}",
254+
},
255+
{
256+
"type": "mrkdwn",
257+
"text": f":busts_in_silhouette: *Teams:* {organization.teams.count()}",
258+
},
259+
],
260+
},
261+
{
262+
"type": "context",
263+
"elements": [
264+
{
265+
"type": "mrkdwn",
266+
"text": "We should contact this customer and see if we can get some feedback from them.",
267+
}
268+
],
269+
},
270+
]
271+
}
272+
try:
273+
requests.post(
274+
settings.SLACK_WEBHOOK_SALES_CHANNEL,
275+
data=slack_message,
276+
timeout=3,
277+
)
278+
except requests.Timeout:
279+
log.warning("Timeout sending a message to Slack webhook")
280+
194281

195282
@handler("customer.updated")
196283
def customer_updated_event(event):

0 commit comments

Comments
 (0)