package payments import ( "context" "fmt" "time" "github.com/gofrs/uuid/v5" "github.com/saveinmed/backend-go/internal/domain" ) // AsaasGateway implements payment processing via Asaas (Brazilian gateway). // Supports Pix, Boleto, and Credit Card with marketplace split. type AsaasGateway struct { APIKey string WalletID string Environment string // "sandbox" or "production" MarketplaceCommission float64 } func NewAsaasGateway(apiKey, walletID, environment string, commission float64) *AsaasGateway { return &AsaasGateway{ APIKey: apiKey, WalletID: walletID, Environment: environment, MarketplaceCommission: commission, } } func (g *AsaasGateway) BaseURL() string { if g.Environment == "production" { return "https://api.asaas.com/v3" } return "https://sandbox.asaas.com/api/v3" } func (g *AsaasGateway) CreatePreference(ctx context.Context, order *domain.Order, payer *domain.User, sellerAcc *domain.SellerPaymentAccount) (*domain.PaymentPreference, error) { select { case <-ctx.Done(): return nil, ctx.Err() default: } fee := int64(float64(order.TotalCents) * (g.MarketplaceCommission / 100)) // In production, this would: // 1. Create customer if not exists // 2. Create charge with split configuration // 3. Return payment URL or Pix QR code pref := &domain.PaymentPreference{ OrderID: order.ID, Gateway: "asaas", CommissionPct: g.MarketplaceCommission, MarketplaceFee: fee, SellerReceivable: order.TotalCents - fee, PaymentURL: fmt.Sprintf("%s/checkout/%s", g.BaseURL(), order.ID.String()), } time.Sleep(10 * time.Millisecond) return pref, nil } // CreatePixPayment generates a Pix payment with QR code. func (g *AsaasGateway) CreatePixPayment(ctx context.Context, order *domain.Order) (*domain.PixPaymentResult, error) { select { case <-ctx.Done(): return nil, ctx.Err() default: } fee := int64(float64(order.TotalCents) * (g.MarketplaceCommission / 100)) expiresAt := time.Now().Add(30 * time.Minute) return &domain.PixPaymentResult{ PaymentID: uuid.Must(uuid.NewV7()).String(), OrderID: order.ID, Gateway: "asaas", PixKey: "chave@saveinmed.com", QRCode: fmt.Sprintf("00020126580014BR.GOV.BCB.PIX0136%s", order.ID.String()), QRCodeBase64: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...", // Simulated CopyPasta: fmt.Sprintf("00020126580014BR.GOV.BCB.PIX0136%s52040000", order.ID.String()), AmountCents: order.TotalCents, MarketplaceFee: fee, SellerReceivable: order.TotalCents - fee, ExpiresAt: expiresAt, Status: "pending", }, nil } // CreateBoletoPayment generates a Boleto payment. func (g *AsaasGateway) CreateBoletoPayment(ctx context.Context, order *domain.Order, customer *domain.Customer) (*domain.BoletoPaymentResult, error) { select { case <-ctx.Done(): return nil, ctx.Err() default: } fee := int64(float64(order.TotalCents) * (g.MarketplaceCommission / 100)) dueDate := time.Now().AddDate(0, 0, 3) // 3 days return &domain.BoletoPaymentResult{ PaymentID: uuid.Must(uuid.NewV7()).String(), OrderID: order.ID, Gateway: "asaas", BoletoURL: fmt.Sprintf("%s/boleto/%s", g.BaseURL(), order.ID.String()), BarCode: fmt.Sprintf("23793.38128 60000.000003 00000.000400 1 %d", order.TotalCents), DigitableLine: fmt.Sprintf("23793381286000000000300000000401%d", order.TotalCents), AmountCents: order.TotalCents, MarketplaceFee: fee, SellerReceivable: order.TotalCents - fee, DueDate: dueDate, Status: "pending", }, nil } // ConfirmPayment checks payment status. func (g *AsaasGateway) ConfirmPayment(ctx context.Context, paymentID string) (*domain.PaymentResult, error) { select { case <-ctx.Done(): return nil, ctx.Err() default: } return &domain.PaymentResult{ PaymentID: paymentID, Status: "confirmed", Gateway: "asaas", Message: "Pagamento confirmado via Asaas", ConfirmedAt: time.Now(), }, nil } // RefundPayment processes a refund. func (g *AsaasGateway) RefundPayment(ctx context.Context, paymentID string, amountCents int64) (*domain.RefundResult, error) { select { case <-ctx.Done(): return nil, ctx.Err() default: } return &domain.RefundResult{ RefundID: uuid.Must(uuid.NewV7()).String(), PaymentID: paymentID, AmountCents: amountCents, Status: "refunded", RefundedAt: time.Now(), }, nil } // CreateSubaccount creates a seller subaccount for split payments. func (g *AsaasGateway) CreateSubaccount(ctx context.Context, seller *domain.Company) (*domain.SellerPaymentAccount, error) { select { case <-ctx.Done(): return nil, ctx.Err() default: } return &domain.SellerPaymentAccount{ SellerID: seller.ID, Gateway: "asaas", AccountID: fmt.Sprintf("sub_%s", seller.ID.String()[:8]), AccountType: "subaccount", Status: "active", CreatedAt: time.Now(), }, nil }