feat: notify supplier and client after payment is done

This commit is contained in:
Urko 2023-07-19 21:01:35 +02:00
parent b426a36570
commit e80e75cb8c
9 changed files with 161 additions and 39 deletions

View File

@ -25,10 +25,11 @@ func NewOrderHandler(walletAddress string, orderSrv *services.Order, conversor *
}
type orderReq struct {
OrderID string `json:"order_id"`
ClientID string `json:"client_id"`
Amount float64 `json:"amount"`
Currency domain.FiatCurrency `json:"currency"`
OrderID string `json:"order_id"`
ClientID string `json:"client_id"`
Amount float64 `json:"amount"`
Currency domain.FiatCurrency `json:"currency"`
ClientEmail string `json:"client_email"`
}
func (hdl *OrderHandler) Post(c *fiber.Ctx) error {
@ -42,7 +43,7 @@ func (hdl *OrderHandler) Post(c *fiber.Ctx) error {
return RenderError(c, fmt.Errorf("hdl.conversor.UsdToBtc %w", err), "")
}
order, err := hdl.orderSrv.NewOrder(c.Context(), req.OrderID, req.ClientID, btcAmount)
order, err := hdl.orderSrv.NewOrder(c.Context(), req.OrderID, req.ClientID, req.ClientEmail, btcAmount)
if err != nil {
return RenderError(c, fmt.Errorf("hdl.orderSrv.NewOrder %w", err), "")
}

View File

@ -102,18 +102,22 @@ func (s *RestServer) onNotification(ctx context.Context, notifChan chan domain.N
order.Block = notif.BlockHash
order.Tx = notif.Tx
if err := s.orderSrv.OrderCompleted(ctx, order); err != nil {
order, err = s.orderSrv.OrderCompleted(ctx, order)
if err != nil {
log.Println("OrderCompleted:", err)
continue
}
// Send email to client and provider
if err := s.mailSrv.SendProviderConfirm(mail.SendOK{
TxID: order.Tx,
BlockHash: order.Block,
DocHash: "",
To: "doc.Email",
Tx: order.Tx,
Block: order.Block,
Amount: order.Amount,
ExplorerUrl: s.btcService.Explorer(order.Tx),
CustomerID: order.ClientID,
OrderID: order.OrderID,
Timestamp: order.PaidAt,
To: order.Email,
}); err != nil {
log.Println("error while send confirm email:", err)
continue

View File

@ -10,6 +10,7 @@ type Order struct {
ID primitive.ObjectID `bson:"_id" json:"_id"`
OrderID string `json:"order_id"`
ClientID string `json:"client_id"`
Email string `json:"email"`
Amount float64 `bson:"amount" json:"amount"`
Tx string `bson:"tx" json:"tx"`
Block string `bson:"block" json:"block"`

View File

@ -64,7 +64,7 @@ func (repo *Repo) FromAmount(ctx context.Context, amount float64, timestamp time
return order, nil
}
func (repo *Repo) OrderCompleted(ctx context.Context, order *domain.Order) error {
func (repo *Repo) OrderCompleted(ctx context.Context, order *domain.Order) (*domain.Order, error) {
updateOpts, err := repo.collection.UpdateOne(ctx, bson.M{"_id": order.ID},
bson.M{
"tx": order.Tx,
@ -73,8 +73,8 @@ func (repo *Repo) OrderCompleted(ctx context.Context, order *domain.Order) error
},
)
if err != nil {
return fmt.Errorf("collection.UpdateOne: %s", err)
return nil, fmt.Errorf("collection.UpdateOne: %s", err)
}
log.Printf("OrderCompleted update %+v\n", updateOpts)
return nil
return order, nil
}

View File

@ -31,11 +31,13 @@ type MailService struct {
}
type SendOK struct {
Price float64
Amount float64
ExplorerUrl string
TxID string
BlockHash string
DocHash string
Tx string
CustomerID string
OrderID string
Block string
Timestamp time.Time
To string
}
@ -115,10 +117,11 @@ func (m *MailService) SendProviderConfirm(data SendOK) error {
return fmt.Errorf("os.ReadFile: %s", err)
}
template := strings.Replace(string(bts), "{{explorer_url}}", data.ExplorerUrl, -1)
template = strings.Replace(template, "{{tx_id}}", data.TxID, -1)
template = strings.Replace(template, "{{block_hash}}", data.BlockHash, -1)
template = strings.Replace(template, "{{doc_hash}}", data.DocHash, -1)
template = strings.Replace(template, "{{support_email}}", m.from, -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)
}
@ -129,10 +132,11 @@ func (m *MailService) SendClientConfirm(data SendOK) error {
return fmt.Errorf("os.ReadFile: %s", err)
}
template := strings.Replace(string(bts), "{{explorer_url}}", data.ExplorerUrl, -1)
template = strings.Replace(template, "{{tx_id}}", data.TxID, -1)
template = strings.Replace(template, "{{block_hash}}", data.BlockHash, -1)
template = strings.Replace(template, "{{doc_hash}}", data.DocHash, -1)
template = strings.Replace(template, "{{support_email}}", m.from, -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)
}
@ -144,9 +148,8 @@ func (m *MailService) SendFail(data SendOK) error {
return fmt.Errorf("os.ReadFile: %s", err)
}
template := strings.Replace(string(bts), "{{explorer_url}}", data.ExplorerUrl, -1)
template = strings.Replace(template, "{{tx_id}}", data.TxID, -1)
template = strings.Replace(template, "{{block_hash}}", data.BlockHash, -1)
template = strings.Replace(template, "{{doc_hash}}", data.DocHash, -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)
// TODO: Alert client too
msg := []byte(m.messageWithHeaders(okSubject, data.To, template))

View File

@ -3,6 +3,7 @@ package mail
import (
"net/smtp"
"testing"
"time"
"gitea.urkob.com/urko/btc-pay-checker/kit"
"gitea.urkob.com/urko/btc-pay-checker/kit/cfg"
@ -29,9 +30,13 @@ func init() {
func Test_mailService_SendOK(t *testing.T) {
dto := SendOK{
Price: 10.2,
Amount: 12.0,
ExplorerUrl: "test",
TxID: "test-hash",
Tx: "test-hash",
CustomerID: "client",
OrderID: "order",
Block: "block",
Timestamp: time.Now(),
To: config.MailTo,
}
@ -41,9 +46,13 @@ func Test_mailService_SendOK(t *testing.T) {
func Test_mailService_SendConfirm(t *testing.T) {
dto := SendOK{
Amount: 12.0,
ExplorerUrl: "test",
TxID: "test-hash",
BlockHash: "block-hash",
Tx: "test-hash",
CustomerID: "client",
OrderID: "order",
Block: "block",
Timestamp: time.Now(),
To: config.MailTo,
}

View File

@ -1 +1,51 @@
TODO:
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
color: #333;
}
.container {
border-radius: 5px;
background-color: #f2f2f2;
padding: 20px;
}
.header {
text-align: center;
padding: 10px;
color: white;
background-color: #4CAF50;
}
.content {
margin: 20px 0;
}
.footer {
text-align: center;
padding: 10px;
color: white;
background-color: #333;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>Payment Confirmation</h2>
</div>
<div class="content">
<p>Dear Customer,</p>
<p>We are pleased to inform you that we have received your Bitcoin payment. Here are the details of the transaction:</p>
<p><strong>Transaction ID:</strong> {{tx}}</p>
<p><strong>Block ID:</strong> {{block}}</p>
<p><strong>Timestamp:</strong> {{timestamp}}</p>
<p>You can view the transaction details at the following URL: <a href="{{explorer_url}}">{{explorer_url}}</a></p>
<p>Thank you for your payment!</p>
</div>
<div class="footer">
<p>© 2023 Bitcoin Payment Checker. All rights reserved.</p>
</div>
</div>
</body>
</html>

View File

@ -1 +1,53 @@
TODO:
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
color: #333;
}
.container {
border-radius: 5px;
background-color: #f2f2f2;
padding: 20px;
}
.header {
text-align: center;
padding: 10px;
color: white;
background-color: #4CAF50;
}
.content {
margin: 20px 0;
}
.footer {
text-align: center;
padding: 10px;
color: white;
background-color: #333;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>Payment Confirmation</h2>
</div>
<div class="content">
<p>Dear Supplier,</p>
<p>We are pleased to inform you that we have received a Bitcoin payment for a recent order. Here are the details of the transaction:</p>
<p><strong>Customer ID:</strong> {{customer_id}}</p>
<p><strong>Order ID:</strong> {{order_id}}</p>
<p><strong>Transaction ID:</strong> {{tx}}</p>
<p><strong>Block ID:</strong> {{block}}</p>
<p><strong>Timestamp:</strong> {{timestamp}}</p>
<p>You can view the transaction details at the following URL: <a href="{{explorer_url}}">{{explorer_url}}</a></p>
<p>We will proceed with the order fulfillment as per our agreement. Thank you for your cooperation!</p>
</div>
<div class="footer">
<p>© 2023 Bitcoin Payment Checker. All rights reserved.</p>
</div>
</div>
</body>
</html>

View File

@ -28,16 +28,18 @@ func (o *Order) WithExpiration(expiration time.Duration) *Order {
return o
}
func (o *Order) NewOrder(ctx context.Context, OrderID string, ClientID string, amount float64) (*domain.Order, error) {
func (o *Order) NewOrder(ctx context.Context, orderID, clientID, email string, amount float64) (*domain.Order, error) {
order := domain.Order{
ID: primitive.NewObjectID(),
OrderID: OrderID,
ClientID: ClientID,
OrderID: orderID,
ClientID: clientID,
Amount: amount,
PaidAt: time.Time{},
CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(o.expiration),
Email: email,
}
_, err := o.repo.Insert(ctx, &order)
if err != nil {
return nil, err
@ -49,6 +51,6 @@ func (o *Order) FromAmount(ctx context.Context, amount float64, timestamp time.T
return o.repo.FromAmount(ctx, amount, timestamp)
}
func (o *Order) OrderCompleted(ctx context.Context, order *domain.Order) error {
func (o *Order) OrderCompleted(ctx context.Context, order *domain.Order) (*domain.Order, error) {
return o.repo.OrderCompleted(ctx, order)
}