saveinmed/backend-old/internal/payments/asaas.go
2026-01-16 10:51:52 -03:00

167 lines
4.9 KiB
Go

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) (*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
}