treasurer/payway/payway.go
2023-05-16 19:21:56 +03:00

394 lines
7.8 KiB
Go

package payway
import (
"context"
"encoding/json"
"fmt"
"time"
"bitbucket.org/skeris/treasurer/dal"
pw "bitbucket.org/skeris/treasurer/payway/payways"
"bitbucket.org/skeris/treasurer/payway/payways/fk"
"github.com/MarsherSusanin/pena_hub_packages_common/fastconfig"
"github.com/gofiber/fiber/v2"
"github.com/themakers/hlog"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Payways struct {
pws []*pw.Payway
conn *mongo.Client
db *mongo.Database
collSvcs *mongo.Collection
available map[string]createPW
templates map[string]string
errChan chan error
}
const collServices = "services"
type createPW func() *pw.Payway
func New(
ctx context.Context,
conn *mongo.Client,
db string,
errChan chan error,
available ...pw.PayWayI,
) (*Payways, error) {
a := map[string]createPW{}
t := map[string]string{}
for _, p := range available {
name := p.Type()
a[name] = p.Copy
bts, err := json.Marshal(p)
if err != nil {
return nil, err
}
t[name] = string(bts)
}
d := conn.Database(db)
return &Payways{
available: a,
templates: t,
conn: conn,
db: d,
errChan: errChan,
collSvcs: d.Collection(collServices),
}, nil
}
func (p *Payways) Init(ctx context.Context) error {
if err := p.YieldPayways(ctx, p.ProcessPayways); err != nil {
return err
}
go func() {
if err := p.WatchPayways(ctx, p.ProcessPayways); err != nil {
p.errChan <- err
}
}()
return nil
}
func (p *Payways) Register(api *fiber.App) *fiber.App {
api.Get("/services", p.getServices)
return api
}
func (p *Payways) getServices(c *fiber.Ctx) error {
fmt.Println("getServices")
return c.JSON(p.templates)
}
func (p *Payways) ProcessPayways(d *pw.Payway) error {
switch d.Svc {
case "fk":
way := fk.FreeKassa{}
if err := bson.Unmarshal(d.RawCredentials, &way); err != nil {
return err
}
d.Credentials = way
p.pws = append(p.pws, d)
}
return nil
}
func (p *Payways) WatchPayways(ctx context.Context, yield func(d *pw.Payway) error) error {
cs, err := p.collSvcs.Watch(ctx, mongo.Pipeline{}, options.ChangeStream().SetFullDocument(options.UpdateLookup))
if err != nil {
return err
}
for cs.Next(ctx) {
var change fastconfig.Change
if err := cs.Decode(&change); err != nil {
return err
}
var piece pw.Payway
if err := bson.Unmarshal(change.FullDocument, &piece); err != nil {
return err
}
if err := yield(&piece); err != nil {
return err
}
}
return nil
}
func (p *Payways) YieldPayways(ctx context.Context, yield func(d *pw.Payway) error) error {
cur, err := p.collSvcs.Find(ctx, bson.M{})
if err != nil {
return err
}
for cur.Next(ctx) {
var piece pw.Payway
if err := cur.Decode(&piece); err != nil {
return err
}
if err := yield(&piece); err != nil {
return err
}
}
return nil
}
type PaywayAPI interface {
GetWallet() error
CreateInvoice() error
CreatePayout() error
InvoiceListener() error
PayoutListener() error
GetInvoiceCurrencies() error
GetPayoutCurrencies() error
}
type LayerPayWay interface {
GetMongoID() string
GetName() string
UpdateData(data *dal.PayWay)
UpdatePayment(data []dal.PayWayPayment)
UpdatePayout(data []dal.PayoutType)
// Payment
CreatePaymentUrl(amount, orderID, paymentType, currency, lang, email, phone, requesterID, userIP string) (string, error)
PaymentListener(ctx context.Context, ip string, request interface{}, mc *dal.MongoConnection) error
GetPaymentList() ([]dal.PayWayPayment, error)
GetPaymentTypeList() []dal.PayWayPayment
GetPaymentType(paymentType string) *dal.PayWayPayment
// Payout
CreatePayout(amount, orderID, payoutType, currency, address string) (string, error)
PayoutListener(ctx context.Context, ip string, request interface{}, mc *dal.MongoConnection) error
GetPayoutList() ([]dal.PayoutType, error)
GetPayoutType(payoutType string) *dal.PayoutType
FindPayoutType(payType string, amount float64) *dal.PayoutType
GetWalletBalance() (map[string]float64, error)
}
type PayWay struct {
mongo *dal.MongoConnection
Cache
*QueuePayout
}
func NewPayWay(
ctx context.Context,
logger hlog.Logger,
mongoPayWay, mongoTreasurer *dal.MongoConnection) (*PayWay, error) {
fk, err := NewFk(ctx, mongoPayWay)
if err != nil {
return nil, err
}
bt, err := NewBt(ctx, mongoPayWay)
if err != nil {
return nil, err
}
// TODO: пока заполняем всё это вручную, стоит ли автоматизировать?
cache := Cache{
"fk": fk,
"bt": bt,
}
queue := NewQueuePayout()
// Заполняем очередь значениями из бд
ptList, err := mongoTreasurer.GetPaymentListByStatus(ctx, "open", "false")
if err != nil {
return nil, err
}
for _, payout := range ptList {
queue.Enqueue(payout.ID)
}
payWay := &PayWay{
Cache: cache,
QueuePayout: queue,
mongo: mongoPayWay,
}
worker := newQueueWorker(ctx, payWay, logger, mongoTreasurer)
// update workers
go payWay.WorkerUpdatePayment(ctx)
go payWay.WorkerUpdatePayout(ctx)
go worker.routine()
return payWay, nil
}
type Cache map[string]LayerPayWay
func (c Cache) GetPayWayById(id string) LayerPayWay {
for _, way := range c {
fmt.Println("way:", way.GetMongoID())
if id == way.GetMongoID() {
return way
}
}
return nil
}
func (c Cache) GetPayWayListByPayoutType(payoutType string, amount float64) map[string]LayerPayWay {
result := map[string]LayerPayWay{}
for name, pw := range c {
pt := pw.FindPayoutType(payoutType, amount)
if pt != nil {
result[name] = pw
}
}
return result
}
func (c Cache) GetPaymentList() (map[string][]dal.PayWayPayment, error) {
result := map[string][]dal.PayWayPayment{}
for k, v := range c {
arr, err := v.GetPaymentList()
if err != nil {
fmt.Printf("%v.GetPaymentListErr: %v \r\n", k, err)
return nil, err
}
result[k] = arr
}
return result, nil
}
func (p PayWay) WorkerUpdatePayment(ctx context.Context) {
for {
for name, pw := range p.Cache {
data, err := pw.GetPaymentList()
if err != nil {
fmt.Printf("worker.%v.GetPaymentListErr: %v \r\n", name, err)
}
for k, item := range data {
id := fmt.Sprintf("%v-%v", name, item.ApiId)
data[k].ID = id
payment, err := p.mongo.GetPayWayPayment(ctx, id)
if err != nil {
fmt.Printf("worker.%v.GetPayWayPaymentByApiIdAndPayWay: %v \r\n", name, err)
//payment.ID = xid.New().String()
}
if payment == nil {
payment = &dal.PayWayPayment{ID: id}
}
//if payment != nil {
err = p.mongo.UpdatePayWayPayment(ctx, dal.PayWayPayment{
ID: payment.ID,
ApiId: item.ApiId,
Name: item.Name,
PayWay: name,
Currency: item.Currency,
Commission: item.Commission,
Minimum: item.Minimum,
Maximum: item.Maximum,
Status: item.Status,
})
if err != nil {
fmt.Printf("worker.%v.GetPayWayPaymentByApiIdAndPayWay: %v \r\n", name, err)
}
//} else {
// if err != nil {
// fmt.Printf("worker.%v.GetPayWayPaymentByApiIdAndPayWay: %v \r\n", name, err)
// }
//}
}
pw.UpdatePayment(data)
}
// Прерывание
select {
case <-ctx.Done():
return
case <-time.After(10 * time.Minute): // Сладкий сон
continue
}
}
}
func (p PayWay) WorkerUpdatePayout(ctx context.Context) {
for {
for name, pw := range p.Cache {
data, err := pw.GetPayoutList()
if err != nil {
fmt.Printf("worker.%v.GetPaymentListErr: %v \r\n", name, pw)
}
fmt.Println("OTTO", name, err, data)
pw.UpdatePayout(data)
if err := p.mongo.UpdatePayWay(ctx, dal.PayWay{
ID: pw.GetMongoID(),
PayoutTypeList: data,
}); err != nil {
fmt.Println("UpdatePayoutErr", err)
}
}
// Прерывание
select {
case <-ctx.Done():
return
case <-time.After(10 * time.Minute): // Сладкий сон
continue
}
}
}