diff --git a/server/emails/src/emails/notification_new_sale.tsx b/server/emails/src/emails/notification_new_sale.tsx
index c09deffcc7..692bb4adaf 100644
--- a/server/emails/src/emails/notification_new_sale.tsx
+++ b/server/emails/src/emails/notification_new_sale.tsx
@@ -1,136 +1,36 @@
-import { Hr, Img, Preview, Section, Text } from '@react-email/components'
-import Button from '../components/Button'
+import { Preview } from '@react-email/components'
import Footer from '../components/Footer'
+import IntroWithHi from '../components/IntroWithHi'
import PolarHeader from '../components/PolarHeader'
import Wrapper from '../components/Wrapper'
import type { schemas } from '../types'
export function NotificationNewSale({
- customer_email,
customer_name,
- billing_address_city,
- billing_address_line1,
formatted_price_amount,
- formatted_billing_reason,
- formatted_address_country,
product_name,
- product_image_url,
- order_date,
- order_url,
+ product_price_amount,
+ organization_name,
}: schemas['MaintainerNewProductSaleNotificationPayload']) {
- const displayName = customer_name || customer_email
-
- const formattedDate = new Date(order_date).toLocaleDateString('en-US', {
- month: "long",
- day: 'numeric',
- })
-
- const addressParts = [billing_address_line1, billing_address_city].filter(
- Boolean,
- )
- const formattedAddress =
- addressParts.length > 0 ? addressParts.join(', ') : null
-
return (
-
- {displayName} placed an order for {product_name}
-
+ New {product_name} sale
-
-
-
- {displayName} placed an order on {formattedDate}!
-
-
-
-
-
-
-
-
-
- Order Summary
-
-
-
-
- {product_image_url && (
-
-
- |
- )}
-
-
- {product_name}
-
-
- {formatted_price_amount}
-
- |
-
-
-
-
-
-
-
-
-
- Order Type
-
-
- {formatted_billing_reason}
-
-
-
-
-
- Customer
-
- {displayName}
- {customer_email}
- {formattedAddress && (
- {formattedAddress}
- )}
- {formatted_address_country && (
-
- {formatted_address_country}
-
- )}
-
-
+
+ {customer_name} purchased {product_name} for{' '}
+ {formatted_price_amount}.
+
)
}
NotificationNewSale.PreviewProps = {
- customer_email: 'bob@ross.com',
- customer_name: 'Bob Ross',
- billing_address_country: 'US',
- billing_address_city: 'San Francisco',
- billing_address_line1: '123 Main St',
+ customer_name: 'John Doe',
formatted_price_amount: '$45.95',
- formatted_billing_reason: 'One-time purchase',
- formatted_address_country: 'United States',
- product_name: 'Beginners guide to painting',
+ product_name: 'Ultimate Magento webshop template',
product_price_amount: 4595,
- product_image_url: 'https://placehold.co/64x64',
- order_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
- order_date: '2024-11-05T20:41:00Z',
- order_url:
- 'https://polar.sh/dashboard/acme-inc/sales/a1b2c3d4-e5f6-7890-abcd-ef1234567890',
organization_name: 'Acme Inc.',
- organization_slug: 'acme-inc',
- billing_reason: 'purchase',
}
export default NotificationNewSale
diff --git a/server/emails/src/types/openapi.ts b/server/emails/src/types/openapi.ts
index 557fe3fe06..85c2f02482 100644
--- a/server/emails/src/types/openapi.ts
+++ b/server/emails/src/types/openapi.ts
@@ -831,54 +831,16 @@ export interface components {
}
/** MaintainerNewProductSaleNotificationPayload */
MaintainerNewProductSaleNotificationPayload: {
- /** Customer Email */
- customer_email: string
- /**
- * Customer Name
- * @default null
- */
- customer_name: string | null
- /**
- * Billing Address Country
- * @default null
- */
- billing_address_country: string | null
- /**
- * Billing Address City
- * @default null
- */
- billing_address_city: string | null
- /**
- * Billing Address Line1
- * @default null
- */
- billing_address_line1: string | null
+ /** Customer Name */
+ customer_name: string
/** Product Name */
product_name: string
/** Product Price Amount */
product_price_amount: number
- /**
- * Product Image Url
- * @default null
- */
- product_image_url: string | null
- /** Order Id */
- order_id: string
- /** Order Date */
- order_date: string
/** Organization Name */
organization_name: string
- /** Organization Slug */
- organization_slug: string
- billing_reason: components['schemas']['OrderBillingReasonInternal']
/** Formatted Price Amount */
readonly formatted_price_amount: string
- /** Formatted Billing Reason */
- readonly formatted_billing_reason: string
- /** Formatted Address Country */
- readonly formatted_address_country: string | null
- /** Order Url */
- readonly order_url: string
}
/** NotificationCreateAccountEmail */
NotificationCreateAccountEmail: {
@@ -963,17 +925,6 @@ export interface components {
| 'subscription_create'
| 'subscription_cycle'
| 'subscription_update'
- /**
- * OrderBillingReasonInternal
- * @description Internal billing reasons with additional granularity.
- * @enum {string}
- */
- OrderBillingReasonInternal:
- | 'purchase'
- | 'subscription_create'
- | 'subscription_cycle'
- | 'subscription_cycle_after_trial'
- | 'subscription_update'
/** OrderConfirmationEmail */
OrderConfirmationEmail: {
/**
@@ -1273,25 +1224,6 @@ export interface components {
/** Url */
url: string
}
- /** OrganizationAccountUnlinkEmail */
- OrganizationAccountUnlinkEmail: {
- /**
- * Template
- * @default organization_account_unlink
- * @constant
- */
- template: 'organization_account_unlink'
- props: components['schemas']['OrganizationAccountUnlinkProps']
- }
- /** OrganizationAccountUnlinkProps */
- OrganizationAccountUnlinkProps: {
- /** Email */
- email: string
- /** Organization Kept Name */
- organization_kept_name: string
- /** Organizations Unlinked */
- organizations_unlinked: string[]
- }
/** OrganizationCustomerEmailSettings */
OrganizationCustomerEmailSettings: {
/** Order Confirmation */
@@ -1337,12 +1269,6 @@ export interface components {
* @default false
*/
wallets_enabled: boolean
- /**
- * Member Model Enabled
- * @description If this organization has the Member model enabled
- * @default false
- */
- member_model_enabled: boolean
}
/** OrganizationInviteEmail */
OrganizationInviteEmail: {
@@ -1432,8 +1358,6 @@ export interface components {
proration_behavior: components['schemas']['SubscriptionProrationBehavior']
/** Benefit Revocation Grace Period */
benefit_revocation_grace_period: number
- /** Prevent Trial Abuse */
- prevent_trial_abuse: boolean
}
/** OrganizationUnderReviewEmail */
OrganizationUnderReviewEmail: {
diff --git a/server/polar/notifications/notification.py b/server/polar/notifications/notification.py
index b2ff926dd3..21a88ee4d8 100644
--- a/server/polar/notifications/notification.py
+++ b/server/polar/notifications/notification.py
@@ -3,14 +3,11 @@
from enum import StrEnum
from typing import Annotated, Literal
-import pycountry
from babel.numbers import format_currency
from pydantic import UUID4, BaseModel, Discriminator, computed_field
-from polar.config import settings
from polar.email.react import render_email_template
from polar.kit.schemas import Schema
-from polar.models.order import OrderBillingReasonInternal
class NotificationType(StrEnum):
@@ -92,49 +89,15 @@ class MaintainerNewPaidSubscriptionNotification(NotificationBase):
class MaintainerNewProductSaleNotificationPayload(NotificationPayloadBase):
- customer_email: str
- customer_name: str | None = None
- billing_address_country: str | None = None
- billing_address_city: str | None = None
- billing_address_line1: str | None = None
+ customer_name: str
product_name: str
product_price_amount: int
- product_image_url: str | None = None
- order_id: str
- order_date: str
organization_name: str
- organization_slug: str
- billing_reason: OrderBillingReasonInternal
@computed_field
def formatted_price_amount(self) -> str:
return format_currency(self.product_price_amount / 100, "USD", locale="en_US")
- @computed_field
- def formatted_billing_reason(self) -> str:
- match self.billing_reason:
- case OrderBillingReasonInternal.purchase:
- return "One-time purchase"
- case OrderBillingReasonInternal.subscription_create:
- return "New subscription"
- case OrderBillingReasonInternal.subscription_cycle:
- return "Subscription renewal"
- case OrderBillingReasonInternal.subscription_cycle_after_trial:
- return "Subscription started after trial"
- case OrderBillingReasonInternal.subscription_update:
- return "Subscription update"
-
- @computed_field
- def formatted_address_country(self) -> str | None:
- if not self.billing_address_country:
- return None
- country = pycountry.countries.get(alpha_2=self.billing_address_country)
- return country.name if country else self.billing_address_country
-
- @computed_field
- def order_url(self) -> str:
- return f"{settings.FRONTEND_BASE_URL}/dashboard/{self.organization_slug}/sales/{self.order_id}"
-
def subject(self) -> str:
return f"You've made a new sale ({self.formatted_price_amount})!"
diff --git a/server/polar/order/service.py b/server/polar/order/service.py
index cd61223c56..aa2c1e95e7 100644
--- a/server/polar/order/service.py
+++ b/server/polar/order/service.py
@@ -34,7 +34,6 @@
from polar.event.system import OrderPaidMetadata, SystemEvent, build_system_event
from polar.eventstream.service import publish as eventstream_publish
from polar.exceptions import PolarError
-from polar.file.s3 import S3_SERVICES
from polar.held_balance.service import held_balance as held_balance_service
from polar.integrations.stripe.schemas import ProductType
from polar.integrations.stripe.service import stripe as stripe_service
@@ -1543,44 +1542,16 @@ async def send_admin_notification(
return
if organization.notification_settings["new_order"]:
- product_image_url: str | None = None
- try:
- if product.product_medias and len(product.product_medias) > 0:
- first_media = product.product_medias[0].file
- product_image_url = S3_SERVICES[first_media.service].get_public_url(
- first_media.path
- )
- except Exception:
- pass
-
- billing_address = order.billing_address
- customer = order.customer
-
await notifications_service.send_to_org_members(
session,
org_id=organization.id,
notif=PartialNotification(
type=NotificationType.maintainer_new_product_sale,
payload=MaintainerNewProductSaleNotificationPayload(
- customer_email=customer.email,
- customer_name=customer.name or order.billing_name,
- billing_address_country=billing_address.country
- if billing_address
- else None,
- billing_address_city=billing_address.city
- if billing_address
- else None,
- billing_address_line1=billing_address.line1
- if billing_address
- else None,
+ customer_name=order.customer.email,
product_name=product.name,
product_price_amount=order.net_amount,
- product_image_url=product_image_url,
- order_id=str(order.id),
- order_date=order.created_at.isoformat(),
- organization_name=organization.name,
- organization_slug=organization.slug,
- billing_reason=order.billing_reason,
+ organization_name=organization.slug,
),
),
)
diff --git a/server/tests/notifications/test_email.py b/server/tests/notifications/test_email.py
index e7790c9678..75ddb7adfa 100644
--- a/server/tests/notifications/test_email.py
+++ b/server/tests/notifications/test_email.py
@@ -4,7 +4,6 @@
import pytest
-from polar.models.order import OrderBillingReasonInternal
from polar.notifications.notification import (
MaintainerCreateAccountNotificationPayload,
MaintainerNewPaidSubscriptionNotificationPayload,
@@ -51,19 +50,10 @@ async def test_MaintainerNewPaidSubscriptionNotification() -> None:
@pytest.mark.asyncio
async def test_MaintainerNewProductSaleNotification() -> None:
n = MaintainerNewProductSaleNotificationPayload(
- customer_email="birk@polar.sh",
- customer_name="Birk",
- billing_address_country="US",
- billing_address_city="San Francisco",
- billing_address_line1="123 Main St",
+ customer_name="birk@polar.sh",
product_name="My Awesome Digital Product",
product_price_amount=500,
- product_image_url=None,
- order_id="a1b2c3d4-e5f6-7890-abcd-ef1234567890",
- order_date="2024-11-05T20:41:00Z",
organization_name="myorg",
- organization_slug="myorg",
- billing_reason=OrderBillingReasonInternal.purchase,
)
await check_diff(n.render())
@@ -84,15 +74,10 @@ async def test_MaintainerCreateAccountNotificationPayload() -> None:
"payload",
[
MaintainerNewProductSaleNotificationPayload(
- customer_email="{{ 123456 * 9 }}",
customer_name="{{ 123456 * 9 }}",
product_name="{{ 123456 * 9 }}",
product_price_amount=500,
- order_id="{{ 123456 * 9 }}",
- order_date="2024-11-05T20:41:00Z",
organization_name="{{ 123456 * 9 }}",
- organization_slug="{{ 123456 * 9 }}",
- billing_reason=OrderBillingReasonInternal.purchase,
),
MaintainerCreateAccountNotificationPayload(
organization_name="{{ 123456 * 9 }}",
@@ -126,15 +111,10 @@ async def test_all_notification_types() -> None:
)
elif notification_type == NotificationType.maintainer_new_product_sale:
n = MaintainerNewProductSaleNotificationPayload(
- customer_email="john@example.com",
customer_name="John Doe",
product_name="Ice cream sandwich",
product_price_amount=500,
- order_id="a1b2c3d4-e5f6-7890-abcd-ef1234567890",
- order_date="2024-11-05T20:41:00Z",
organization_name="Ice Cream Van",
- organization_slug="ice-cream-van",
- billing_reason=OrderBillingReasonInternal.purchase,
)
elif notification_type == NotificationType.maintainer_new_paid_subscription:
n = MaintainerNewPaidSubscriptionNotificationPayload(
diff --git a/server/tests/notifications/testdata/test_MaintainerNewProductSaleNotification.html b/server/tests/notifications/testdata/test_MaintainerNewProductSaleNotification.html
index 0f43d5a53f..d67bc10375 100644
--- a/server/tests/notifications/testdata/test_MaintainerNewProductSaleNotification.html
+++ b/server/tests/notifications/testdata/test_MaintainerNewProductSaleNotification.html
@@ -12,4 +12,4 @@
* {
font-family: 'Inter', sans-serif;
}
-
Birk placed an order for My Awesome Digital Product Birk placed an order on November 5! |
Order Summary My Awesome Digital Product $5.00 |
|
Order Type One-time purchase |
Customer Birk birk@polar.sh 123 Main St, San Francisco United States |
|