mirror of
https://github.com/hanzoai/commerce.git
synced 2026-10-30 15:08:42 +00:00
Every sale through subscribe/card, card or prepaid, coupon or not, sends a
plain-text notice to the addresses in BILLING_SALE_NOTIFY_TO: org, customer
email, plan, interval, amount charged, list price, promo and coupon
("50OFF — 50% off first month"), time, subscription, invoice and mode. It
is sent after the sale is sealed, on its own goroutine with a 30s bound,
never for a replay, and a failure is logged, never the sale's.
Mail goes through the host: EmbedConfig.Mail installs a mail.Sender (the
cloud binary passes its notify rail, credentials in KMS). Unset, nothing is
sent and mail.ErrNoSender says why.
Co-authored-by: Hanzo Dev <[email protected]>
52 lines
1.4 KiB
Go
52 lines
1.4 KiB
Go
// Copyright (c) 2014-present Hanzo AI, Inc.
|
|
// Licensed under MIT OR Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.
|
|
|
|
// Package mail is commerce's outbound plain-text mail, delivered by the HOST.
|
|
//
|
|
// One Sender, installed once at embed time (EmbedConfig.Mail), the same way the
|
|
// credit ledger and the secret reader are: commerce asks its host rather than
|
|
// holding a mail provider credential of its own. In the cloud binary the host's
|
|
// sender is its notify rail, whose provider credentials live in KMS.
|
|
//
|
|
// nil is a supported state: a standalone commerce with no host sends nothing,
|
|
// and Send says so with ErrNoSender instead of pretending.
|
|
package mail
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
)
|
|
|
|
// Sender delivers one plain-text message to every address in to.
|
|
type Sender interface {
|
|
Send(ctx context.Context, to []string, subject, body string) error
|
|
}
|
|
|
|
// ErrNoSender is Send without a host sender installed.
|
|
var ErrNoSender = errors.New("mail: the host installed no sender")
|
|
|
|
var (
|
|
mu sync.RWMutex
|
|
s Sender
|
|
)
|
|
|
|
// Set installs the host's sender; nil removes it. Called once, before routes
|
|
// register.
|
|
func Set(x Sender) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
s = x
|
|
}
|
|
|
|
// Send delivers through the installed sender.
|
|
func Send(ctx context.Context, to []string, subject, body string) error {
|
|
mu.RLock()
|
|
x := s
|
|
mu.RUnlock()
|
|
if x == nil {
|
|
return ErrNoSender
|
|
}
|
|
return x.Send(ctx, to, subject, body)
|
|
}
|