refactor: use external email package
This commit is contained in:
parent
da1228082a
commit
bc29b63114
3
Makefile
3
Makefile
|
@ -55,4 +55,5 @@ clean:
|
|||
rm -rf $(BIN_DIR)
|
||||
|
||||
.PHONY: rebuild
|
||||
rebuild: clean build_linux_amd64 build_linux_arm64 build_windows_amd64 build_windows_386 build_mac_amd64 build_mac_arm64
|
||||
#rebuild: clean build_linux_amd64 build_linux_arm64 build_windows_amd64 build_windows_386 build_mac_amd64 build_mac_arm64
|
||||
rebuild: clean build_linux_amd64 build_windows_amd64 build_windows_386 build_mac_amd64
|
||||
|
|
|
@ -15,9 +15,9 @@ import (
|
|||
"gitea.urkob.com/urko/btc-pay-checker/internal/platform/mongodb/order"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/btc"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/mail"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/price"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/kit/cfg"
|
||||
"gitea.urkob.com/urko/emailsender/pkg/email"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
@ -61,17 +61,14 @@ func main() {
|
|||
|
||||
priceSrv := price.NewPriceConversor(config.ConversorApi, config.ConversorApi)
|
||||
btcSrv := btc.NewBitcoinService(config.RpcHost, config.RpcAuth, config.RpcZmq, config.WalletAddress).WithTestnet()
|
||||
mailSrv := mail.NewMailService(
|
||||
mail.MailServiceConfig{
|
||||
emailSrv := email.NewSecure(email.MailServiceConfig{
|
||||
Auth: smtp.PlainAuth("", config.MailUser, config.MailPassword, config.MailHost),
|
||||
Host: config.MailHost,
|
||||
Port: config.MailPort,
|
||||
From: config.MailFrom,
|
||||
TemplatesDir: config.MailTemplatesDir,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
restServer := api.NewRestServer(config, orderSrv, btcSrv, priceSrv, mailSrv)
|
||||
restServer := api.NewRestServer(config, orderSrv, btcSrv, priceSrv, emailSrv)
|
||||
go func() {
|
||||
if err = restServer.Start(ctx, config.ApiPort, config.Views); err != nil {
|
||||
panic(fmt.Errorf("restServer.Start: %w", err))
|
||||
|
|
5
go.mod
5
go.mod
|
@ -1,8 +1,9 @@
|
|||
module gitea.urkob.com/urko/btc-pay-checker
|
||||
|
||||
go 1.20
|
||||
go 1.21.1
|
||||
|
||||
require (
|
||||
gitea.urkob.com/urko/emailsender v0.0.0-20231226090954-aca310503955
|
||||
gitea.urkob.com/urko/go-root-dir v0.0.0-20230311113851-2f6d4355888a
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/gofiber/fiber/v2 v2.48.0
|
||||
|
@ -12,7 +13,6 @@ require (
|
|||
github.com/pebbe/zmq4 v1.2.10
|
||||
github.com/stretchr/testify v1.8.4
|
||||
go.mongodb.org/mongo-driver v1.12.0
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -22,6 +22,7 @@ require (
|
|||
github.com/gofiber/template v1.8.2 // indirect
|
||||
github.com/gofiber/utils v1.1.0 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/klauspost/compress v1.16.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -1,3 +1,5 @@
|
|||
gitea.urkob.com/urko/emailsender v0.0.0-20231226090954-aca310503955 h1:wQE6MlojyWmOlEp3j88BMGIM0A/ZWqByTFZzXbsbtuQ=
|
||||
gitea.urkob.com/urko/emailsender v0.0.0-20231226090954-aca310503955/go.mod h1:V0m9luBiPICIA72Yr7GJKIOS0GZ+UK0aajtl3Eugqqw=
|
||||
gitea.urkob.com/urko/go-root-dir v0.0.0-20230311113851-2f6d4355888a h1:s73cd3bRR6v0LGiBei841iIolbBJN2tbkUwN54X9vVg=
|
||||
gitea.urkob.com/urko/go-root-dir v0.0.0-20230311113851-2f6d4355888a/go.mod h1:mU9nRHl70tBhJFbgKotpoXMV+s0wx+1uJ988p4oEpSo=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
|
@ -20,6 +22,7 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
|||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
|
@ -72,8 +75,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -117,5 +118,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/mail"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/price"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/kit/cfg"
|
||||
"gitea.urkob.com/urko/emailsender/pkg/email"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -30,7 +31,7 @@ type RestServer struct {
|
|||
config *cfg.Config
|
||||
btcService *btc.BitcoinService
|
||||
orderSrv *services.Order
|
||||
mailSrv *mail.MailService
|
||||
emailSrv *mail.MailService
|
||||
priceSrv *price.PriceConversor
|
||||
}
|
||||
|
||||
|
@ -39,14 +40,14 @@ func NewRestServer(
|
|||
orderSrv *services.Order,
|
||||
btcService *btc.BitcoinService,
|
||||
priceSrv *price.PriceConversor,
|
||||
mailSrv *mail.MailService,
|
||||
emailSrv *email.EmailService,
|
||||
) *RestServer {
|
||||
return &RestServer{
|
||||
config: config,
|
||||
orderSrv: orderSrv,
|
||||
btcService: btcService,
|
||||
priceSrv: priceSrv,
|
||||
mailSrv: mailSrv,
|
||||
emailSrv: mail.NewMailService(emailSrv),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +119,7 @@ func (s *RestServer) onNotification(ctx context.Context, notifChan chan domain.N
|
|||
}
|
||||
|
||||
// Send email to client and provider
|
||||
if err := s.mailSrv.SendProviderConfirm(mail.SendOK{
|
||||
if err := s.emailSrv.SendProviderConfirm(mail.SendOK{
|
||||
Tx: order.Tx,
|
||||
Block: order.Block,
|
||||
Amount: order.Amount,
|
||||
|
|
|
@ -15,6 +15,6 @@ type Order struct {
|
|||
Tx string `bson:"tx" json:"tx"`
|
||||
Block string `bson:"block" json:"block"`
|
||||
PaidAt time.Time `bson:"paid_at" json:"paid_at"`
|
||||
CreatedAt time.Time `bson:"created_at" json:"created_at"`
|
||||
ExpiresAt time.Time `bson:"expires_at" json:"expires_at"`
|
||||
CreatedAt time.Time `bson:"created_at" json:"-"`
|
||||
ExpiresAt time.Time `bson:"expires_at" json:"-"`
|
||||
}
|
||||
|
|
|
@ -1,33 +1,27 @@
|
|||
package mail
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/mail/templates"
|
||||
"gitea.urkob.com/urko/emailsender/pkg/email"
|
||||
)
|
||||
|
||||
const (
|
||||
mime = "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
|
||||
okSubject = "Proof-of-Evidence record successful"
|
||||
failSubject = "Proof-of-Evidence record failed"
|
||||
templateError = "errror.html"
|
||||
templateClientConfirm = "client_confirm.html"
|
||||
templateProviderConfirm = "provider_confirm.html"
|
||||
okSubject = "BTC Pay Checker successful"
|
||||
failSubject = "BTC Pay Checker failed"
|
||||
)
|
||||
|
||||
type MailService struct {
|
||||
auth smtp.Auth
|
||||
host string
|
||||
port string
|
||||
from string
|
||||
templatesDir string
|
||||
tlsconfig *tls.Config
|
||||
emailSrv *email.EmailService
|
||||
}
|
||||
|
||||
func NewMailService(emailSrv *email.EmailService) *MailService {
|
||||
return &MailService{
|
||||
emailSrv: emailSrv,
|
||||
}
|
||||
}
|
||||
|
||||
type SendOK struct {
|
||||
|
@ -39,180 +33,49 @@ type SendOK struct {
|
|||
Block string
|
||||
Timestamp time.Time
|
||||
To string
|
||||
SupportEmail string
|
||||
}
|
||||
|
||||
type MailServiceConfig struct {
|
||||
Auth smtp.Auth
|
||||
Host string
|
||||
Port string
|
||||
From string // Sender email address
|
||||
TemplatesDir string // Should end with slash '/'
|
||||
}
|
||||
|
||||
var validCommonNames = []string{"ISRG Root X1", "R3", "DST Root CA X3"}
|
||||
|
||||
func NewMailService(config MailServiceConfig) *MailService {
|
||||
return &MailService{
|
||||
auth: config.Auth,
|
||||
host: config.Host,
|
||||
port: config.Port,
|
||||
from: config.From,
|
||||
templatesDir: config.TemplatesDir,
|
||||
tlsconfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: config.Host,
|
||||
VerifyConnection: func(cs tls.ConnectionState) error {
|
||||
|
||||
// // Check the server's common name
|
||||
// for _, cert := range cs.PeerCertificates {
|
||||
// log.Println("cert.DNSNames", cert.DNSNames)
|
||||
// if err := cert.VerifyHostname(config.Host); err != nil {
|
||||
// return fmt.Errorf("invalid common name: %w", err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// Check the certificate chain
|
||||
opts := x509.VerifyOptions{
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
for _, cert := range cs.PeerCertificates[1:] {
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
_, err := cs.PeerCertificates[0].Verify(opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid certificate chain: %w", err)
|
||||
}
|
||||
|
||||
// Iterate over the certificates again to perform custom checks
|
||||
for _, cert := range cs.PeerCertificates {
|
||||
// Add your own custom checks here...
|
||||
if time.Now().After(cert.NotAfter) {
|
||||
return fmt.Errorf("certificate has expired")
|
||||
}
|
||||
if time.Now().Add(30 * 24 * time.Hour).After(cert.NotAfter) {
|
||||
return fmt.Errorf("certificate will expire within 30 days")
|
||||
}
|
||||
|
||||
if !slices.Contains(validCommonNames, cert.Issuer.CommonName) {
|
||||
return fmt.Errorf("certificate is not issued by a trusted CA")
|
||||
}
|
||||
// log.Println("cert.ExtKeyUsage", cert.ExtKeyUsage)
|
||||
// if cert.KeyUsage&x509.KeyUsageDigitalSignature == 0 || len(cert.ExtKeyUsage) == 0 || !slices.Contains(cert.ExtKeyUsage, x509.ExtKeyUsageServerAuth) {
|
||||
// log.Printf("%+v", cert)
|
||||
// return fmt.Errorf("certificate cannot be used for server authentication")
|
||||
// }
|
||||
if cert.PublicKeyAlgorithm != x509.RSA {
|
||||
return fmt.Errorf("unsupported public key algorithm")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
func (m *MailService) SendProviderConfirm(data SendOK) error {
|
||||
bts, err := os.ReadFile(m.templatesDir + templateProviderConfirm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("os.ReadFile: %s", err)
|
||||
}
|
||||
template := strings.Replace(string(bts), "{{explorer_url}}", data.ExplorerUrl, -1)
|
||||
template := strings.Replace(templates.ProviderConfirm, "{{explorer_url}}", data.ExplorerUrl, -1)
|
||||
template = strings.Replace(template, "{{customer_id}}", data.CustomerID, -1)
|
||||
template = strings.Replace(template, "{{order_id}}", data.OrderID, -1)
|
||||
template = strings.Replace(template, "{{tx}}", data.Tx, -1)
|
||||
template = strings.Replace(template, "{{block}}", data.Block, -1)
|
||||
template = strings.Replace(template, "{{timestamp}}", data.Timestamp.Format(time.RFC3339), -1)
|
||||
msg := []byte(m.messageWithHeaders(okSubject, data.To, template))
|
||||
return m.send(data.To, msg)
|
||||
|
||||
return m.emailSrv.SendEmail(email.EmailMessage{
|
||||
To: data.To,
|
||||
Subject: okSubject,
|
||||
Body: template,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *MailService) SendClientConfirm(data SendOK) error {
|
||||
bts, err := os.ReadFile(m.templatesDir + templateClientConfirm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("os.ReadFile: %s", err)
|
||||
}
|
||||
template := strings.Replace(string(bts), "{{explorer_url}}", data.ExplorerUrl, -1)
|
||||
template := strings.Replace(templates.ClientConfirm, "{{explorer_url}}", data.ExplorerUrl, -1)
|
||||
template = strings.Replace(template, "{{customer_id}}", data.CustomerID, -1)
|
||||
template = strings.Replace(template, "{{order_id}}", data.OrderID, -1)
|
||||
template = strings.Replace(template, "{{tx}}", data.Tx, -1)
|
||||
template = strings.Replace(template, "{{block}}", data.Block, -1)
|
||||
template = strings.Replace(template, "{{timestamp}}", data.Timestamp.Format(time.RFC3339), -1)
|
||||
msg := []byte(m.messageWithHeaders(okSubject, data.To, template))
|
||||
return m.send(data.To, msg)
|
||||
|
||||
return m.emailSrv.SendEmail(email.EmailMessage{
|
||||
To: data.To,
|
||||
Subject: okSubject,
|
||||
Body: template,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *MailService) SendFail(data SendOK) error {
|
||||
//templateError
|
||||
bts, err := os.ReadFile(m.templatesDir + templateError)
|
||||
if err != nil {
|
||||
return fmt.Errorf("os.ReadFile: %s", err)
|
||||
}
|
||||
template := strings.Replace(string(bts), "{{explorer_url}}", data.ExplorerUrl, -1)
|
||||
template := strings.Replace(templates.Error, "{{explorer_url}}", data.ExplorerUrl, -1)
|
||||
template = strings.Replace(template, "{{tx_id}}", data.Tx, -1)
|
||||
template = strings.Replace(template, "{{block_hash}}", data.Block, -1)
|
||||
template = strings.Replace(template, "{{support_email}}", m.from, -1)
|
||||
template = strings.Replace(template, "{{support_email}}", data.SupportEmail, -1)
|
||||
// TODO: Alert client too
|
||||
msg := []byte(m.messageWithHeaders(okSubject, data.To, template))
|
||||
return m.send(data.To, msg)
|
||||
}
|
||||
|
||||
func (m *MailService) send(to string, msg []byte) error {
|
||||
c, err := smtp.Dial(m.host + ":" + m.port)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DIAL: %s", err)
|
||||
}
|
||||
|
||||
if err = c.StartTLS(m.tlsconfig); err != nil {
|
||||
return fmt.Errorf("c.StartTLS: %s", err)
|
||||
}
|
||||
|
||||
// Auth
|
||||
if err = c.Auth(m.auth); err != nil {
|
||||
return fmt.Errorf("c.Auth: %s", err)
|
||||
}
|
||||
|
||||
// To && From
|
||||
if err = c.Mail(m.from); err != nil {
|
||||
return fmt.Errorf("c.Mail: %s", err)
|
||||
}
|
||||
|
||||
if err = c.Rcpt(to); err != nil {
|
||||
return fmt.Errorf("c.Rcpt: %s", err)
|
||||
}
|
||||
|
||||
// Data
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
return fmt.Errorf("c.Data: %s", err)
|
||||
}
|
||||
|
||||
_, err = w.Write(msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("w.Write: %s", err)
|
||||
}
|
||||
|
||||
if err = w.Close(); err != nil {
|
||||
return fmt.Errorf("w.Close: %s", err)
|
||||
}
|
||||
|
||||
if err = c.Quit(); err != nil {
|
||||
return fmt.Errorf("w.Quit: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MailService) messageWithHeaders(subject, to, body string) string {
|
||||
headers := make(map[string]string)
|
||||
headers["From"] = m.from
|
||||
headers["To"] = to
|
||||
headers["Subject"] = subject
|
||||
headers["MIME-Version"] = "1.0"
|
||||
|
||||
message := ""
|
||||
for k, v := range headers {
|
||||
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
||||
}
|
||||
message += "Content-Type: text/html; charset=utf-8\r\n" + body
|
||||
|
||||
return message
|
||||
|
||||
return m.emailSrv.SendEmail(email.EmailMessage{
|
||||
To: data.To,
|
||||
Subject: failSubject,
|
||||
Body: template,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
package mail
|
||||
|
||||
import (
|
||||
"net/smtp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.urkob.com/urko/btc-pay-checker/kit"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/kit/cfg"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
mailSrv *MailService
|
||||
config *cfg.Config
|
||||
)
|
||||
|
||||
func init() {
|
||||
config = cfg.NewConfig(kit.RootDir() + "/.test.env")
|
||||
mailSrv = NewMailService(
|
||||
MailServiceConfig{
|
||||
Auth: smtp.PlainAuth("", config.MailUser, config.MailPassword, config.MailHost),
|
||||
Host: config.MailHost,
|
||||
Port: config.MailPort,
|
||||
From: config.MailFrom,
|
||||
TemplatesDir: config.MailTemplatesDir,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func Test_mailService_SendOK(t *testing.T) {
|
||||
dto := SendOK{
|
||||
Amount: 12.0,
|
||||
ExplorerUrl: "test",
|
||||
Tx: "test-hash",
|
||||
CustomerID: "client",
|
||||
OrderID: "order",
|
||||
Block: "block",
|
||||
Timestamp: time.Now(),
|
||||
To: config.MailTo,
|
||||
}
|
||||
|
||||
err := mailSrv.SendClientConfirm(dto)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_mailService_SendConfirm(t *testing.T) {
|
||||
dto := SendOK{
|
||||
Amount: 12.0,
|
||||
ExplorerUrl: "test",
|
||||
Tx: "test-hash",
|
||||
CustomerID: "client",
|
||||
OrderID: "order",
|
||||
Block: "block",
|
||||
Timestamp: time.Now(),
|
||||
To: config.MailTo,
|
||||
}
|
||||
|
||||
err := mailSrv.SendProviderConfirm(dto)
|
||||
require.NoError(t, err)
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
package templates
|
||||
|
||||
var ClientConfirm = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
|
@ -48,4 +50,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>`
|
|
@ -0,0 +1,3 @@
|
|||
package templates
|
||||
|
||||
var Error = ``
|
|
@ -1 +0,0 @@
|
|||
TODO:
|
|
@ -1,4 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
package templates
|
||||
|
||||
var ProviderConfirm = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
|
@ -50,4 +52,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>`
|
Loading…
Reference in New Issue