394 lines
7.8 KiB
Go
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
|
||
|
}
|
||
|
}
|
||
|
}
|