refactor: abstract billing operations behind a new domain.BillingProvider interface and update services to use it.
This commit is contained in:
parent
4838c6d37c
commit
4f422db1a0
7 changed files with 30 additions and 26 deletions
|
|
@ -82,7 +82,7 @@ func (s *billingService) ingestMeterEventToPolar(ctx context.Context, organizati
|
||||||
// Filter: name equals "invoice.processed"
|
// Filter: name equals "invoice.processed"
|
||||||
// Amount: 1 (one invoice processed)
|
// Amount: 1 (one invoice processed)
|
||||||
meterSlug := invoicesProcessedMeterSlug // Event name MUST match meter filter exactly (with dot)
|
meterSlug := invoicesProcessedMeterSlug // Event name MUST match meter filter exactly (with dot)
|
||||||
if err := s.polarAdapter.IngestMeterEvent(ctx, externalID, meterSlug, 1); err != nil {
|
if err := s.billingProvider.IngestMeterEvent(ctx, externalID, meterSlug, 1); err != nil {
|
||||||
s.logger.Error("Failed to ingest meter event to Polar", map[string]any{
|
s.logger.Error("Failed to ingest meter event to Polar", map[string]any{
|
||||||
"organization_id": organizationID,
|
"organization_id": organizationID,
|
||||||
"external_id": externalID,
|
"external_id": externalID,
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ func (m *Module) Configure(container *dig.Container) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register PolarAdapter
|
// Register BillingProvider (Polar implementation)
|
||||||
if err := container.Provide(func(client *polarpkg.Client) PolarAdapter {
|
if err := container.Provide(func(client *polarpkg.Client) domain.BillingProvider {
|
||||||
return polar.NewPolarAdapter(client)
|
return polar.NewPolarAdapter(client)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -45,10 +45,10 @@ func (m *Module) Configure(container *dig.Container) error {
|
||||||
if err := container.Provide(func(
|
if err := container.Provide(func(
|
||||||
repo domain.SubscriptionRepository,
|
repo domain.SubscriptionRepository,
|
||||||
orgAdapter domain.OrganizationAdapter,
|
orgAdapter domain.OrganizationAdapter,
|
||||||
polarAdapter PolarAdapter,
|
billingProvider domain.BillingProvider,
|
||||||
logger logger.Logger,
|
logger logger.Logger,
|
||||||
) BillingService {
|
) BillingService {
|
||||||
return NewBillingService(repo, orgAdapter, polarAdapter, logger)
|
return NewBillingService(repo, orgAdapter, billingProvider, logger)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,30 +71,22 @@ type BillingService interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type billingService struct {
|
type billingService struct {
|
||||||
repo domain.SubscriptionRepository
|
repo domain.SubscriptionRepository
|
||||||
orgAdapter domain.OrganizationAdapter
|
orgAdapter domain.OrganizationAdapter
|
||||||
polarAdapter PolarAdapter
|
billingProvider domain.BillingProvider
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBillingService(
|
func NewBillingService(
|
||||||
repo domain.SubscriptionRepository,
|
repo domain.SubscriptionRepository,
|
||||||
orgAdapter domain.OrganizationAdapter,
|
orgAdapter domain.OrganizationAdapter,
|
||||||
polarAdapter PolarAdapter,
|
billingProvider domain.BillingProvider,
|
||||||
logger logger.Logger,
|
logger logger.Logger,
|
||||||
) BillingService {
|
) BillingService {
|
||||||
return &billingService{
|
return &billingService{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
orgAdapter: orgAdapter,
|
orgAdapter: orgAdapter,
|
||||||
polarAdapter: polarAdapter,
|
billingProvider: billingProvider,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PolarAdapter defines the interface for Polar API operations
|
|
||||||
type PolarAdapter interface {
|
|
||||||
GetSubscription(ctx context.Context, externalCustomerID string) (*domain.Subscription, error)
|
|
||||||
GetCheckoutSession(ctx context.Context, sessionID string) (*domain.CheckoutSessionResponse, error)
|
|
||||||
GetCheckoutSessionWithPolling(ctx context.Context, sessionID string) (*domain.CheckoutSessionResponse, error)
|
|
||||||
IngestMeterEvent(ctx context.Context, externalCustomerID string, meterSlug string, amount int32) error
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ func (s *billingService) SyncSubscriptionFromPolar(ctx context.Context, organiza
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch subscription from Polar
|
// Fetch subscription from Polar
|
||||||
subscription, err := s.polarAdapter.GetSubscription(ctx, externalID)
|
subscription, err := s.billingProvider.GetSubscription(ctx, externalID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to fetch subscription from Polar: %w", err)
|
return fmt.Errorf("failed to fetch subscription from Polar: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
func (s *billingService) VerifyPaymentFromCheckout(ctx context.Context, sessionID string) (*domain.BillingStatus, error) {
|
func (s *billingService) VerifyPaymentFromCheckout(ctx context.Context, sessionID string) (*domain.BillingStatus, error) {
|
||||||
// Step 1: Get checkout session from Polar with polling
|
// Step 1: Get checkout session from Polar with polling
|
||||||
checkoutSession, err := s.polarAdapter.GetCheckoutSessionWithPolling(ctx, sessionID)
|
checkoutSession, err := s.billingProvider.GetCheckoutSessionWithPolling(ctx, sessionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("❌ [VerifyPayment] Failed to verify checkout session %s: %v\n", sessionID, err)
|
fmt.Printf("❌ [VerifyPayment] Failed to verify checkout session %s: %v\n", sessionID, err)
|
||||||
return nil, fmt.Errorf("failed to get checkout session: %w", err)
|
return nil, fmt.Errorf("failed to get checkout session: %w", err)
|
||||||
|
|
@ -42,7 +42,7 @@ func (s *billingService) VerifyPaymentFromCheckout(ctx context.Context, sessionI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: Fetch full subscription details from Polar
|
// Step 5: Fetch full subscription details from Polar
|
||||||
subscription, err := s.polarAdapter.GetSubscription(ctx, externalCustomerID)
|
subscription, err := s.billingProvider.GetSubscription(ctx, externalCustomerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch subscription from Polar: %w", err)
|
return nil, fmt.Errorf("failed to fetch subscription from Polar: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,3 +23,12 @@ type OrganizationAdapter interface {
|
||||||
GetStytchOrgID(ctx context.Context, organizationID int32) (string, error)
|
GetStytchOrgID(ctx context.Context, organizationID int32) (string, error)
|
||||||
GetOrganizationIDByStytchOrgID(ctx context.Context, stytchOrgID string) (int32, error)
|
GetOrganizationIDByStytchOrgID(ctx context.Context, stytchOrgID string) (int32, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BillingProvider defines operations for external billing providers
|
||||||
|
// This interface abstracts the billing provider (e.g., Polar.sh) from the app layer
|
||||||
|
type BillingProvider interface {
|
||||||
|
GetSubscription(ctx context.Context, externalCustomerID string) (*Subscription, error)
|
||||||
|
GetCheckoutSession(ctx context.Context, sessionID string) (*CheckoutSessionResponse, error)
|
||||||
|
GetCheckoutSessionWithPolling(ctx context.Context, sessionID string) (*CheckoutSessionResponse, error)
|
||||||
|
IngestMeterEvent(ctx context.Context, externalCustomerID string, meterSlug string, amount int32) error
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,14 @@ import (
|
||||||
polarpkg "github.com/moasq/go-b2b-starter/pkg/polar"
|
polarpkg "github.com/moasq/go-b2b-starter/pkg/polar"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Ensure polarAdapter implements domain.BillingProvider at compile time
|
||||||
|
var _ domain.BillingProvider = (*polarAdapter)(nil)
|
||||||
|
|
||||||
type polarAdapter struct {
|
type polarAdapter struct {
|
||||||
client *polarpkg.Client
|
client *polarpkg.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPolarAdapter(client *polarpkg.Client) *polarAdapter {
|
func NewPolarAdapter(client *polarpkg.Client) domain.BillingProvider {
|
||||||
return &polarAdapter{
|
return &polarAdapter{
|
||||||
client: client,
|
client: client,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue