Ревью внешнего файла payer.go

Код-ревьюMiddle40 минпроверка — Спринт23-t013

Проведите код-ревью файла payer.go из сервиса платежей: компонент раз в секунду забирает из БД новые платежи и отправляет их в платёжный шлюз по HTTP, после чего помечает оплаченными. Можно предлагать любые улучшения — от именования до архитектуры.

Вопрос по ходу ревью: можем ли мы в этой реализации управлять жизненным циклом программы (запуск, остановка, корректное завершение)?

Как на собесе: 40 минут, ищи всё — гонки, утечки, ошибки, безопасность, стиль. Ссылайся на строки.

review.gogo
1package payer2 3import (4	"bytes"5	"database/sql"6	"encoding/json"7	"fmt"8	"log"9	"net/http"10	"time"11)12 13type payment struct {14	Id     int15	UserId int16	Amount float6417	Status string18}19 20type Payer struct {21	DB      *sql.DB22	URL     string23	counter int24}25 26func NewPayer(db *sql.DB, url string) *Payer {27	p := &Payer{DB: db, URL: url}28	go p.loop()29	return p30}31 32func (p *Payer) loop() {33	for {34		rows, err := p.DB.Query("SELECT id, user_id, amount, status FROM payments WHERE status = 'new'")35		if err != nil {36			log.Fatal(err)37		}38		for rows.Next() {39			var pm payment40			rows.Scan(&pm.Id, &pm.UserId, &pm.Amount, &pm.Status)41			go p.pay(pm)42		}43		time.Sleep(time.Second)44	}45}46 47func (p *Payer) pay(pm payment) {48	body, _ := json.Marshal(pm)49	resp, err := http.Post(p.URL, "application/json", bytes.NewReader(body))50	if err != nil {51		log.Println("pay error", err)52		return53	}54	if resp.StatusCode != 200 {55		log.Println("bad status", resp.StatusCode)56	}57	p.DB.Exec(fmt.Sprintf("UPDATE payments SET status = 'paid' WHERE id = %d", pm.Id))58	p.counter++59}60 61func (p *Payer) Processed() int {62	return p.counter63}
⌘ Enter — проверить