package billing import ( "context" "errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) var errNotFound = errors.New("not found") type repository interface { getSubscriptionByNetworkID(ctx context.Context, networkID string) (*Subscription, error) upsertSubscription(ctx context.Context, sub *Subscription) error deleteSubscriptionByID(ctx context.Context, subscriptionID string) error getStripeCustomerID(ctx context.Context, networkID string) (string, error) setStripeCustomerID(ctx context.Context, networkID, customerID string) error } type repositoryImpl struct { pool *pgxpool.Pool } func newRepository(pool *pgxpool.Pool) repository { return &repositoryImpl{pool: pool} } const subscriptionColumns = `id, network_id, stripe_customer_id, status, price_id, cadence, quantity, cancel_at_period_end, current_period_start, current_period_end, created_at, updated_at` func scanSubscription(row pgx.Row, s *Subscription) error { return row.Scan( &s.ID, &s.NetworkID, &s.StripeCustomerID, &s.Status, &s.PriceID, &s.Cadence, &s.Quantity, &s.CancelAtPeriodEnd, &s.CurrentPeriodStart, &s.CurrentPeriodEnd, &s.CreatedAt, &s.UpdatedAt, ) } func (r *repositoryImpl) getSubscriptionByNetworkID(ctx context.Context, networkID string) (*Subscription, error) { var s Subscription err := scanSubscription( r.pool.QueryRow(ctx, `SELECT `+subscriptionColumns+` FROM network_subscriptions WHERE network_id = $1`, networkID, ), &s, ) if errors.Is(err, pgx.ErrNoRows) { return nil, errNotFound } if err != nil { return nil, err } return &s, nil } func (r *repositoryImpl) upsertSubscription(ctx context.Context, sub *Subscription) error { _, err := r.pool.Exec(ctx, ` INSERT INTO network_subscriptions ( id, network_id, stripe_customer_id, status, price_id, cadence, quantity, cancel_at_period_end, current_period_start, current_period_end ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ON CONFLICT (id) DO UPDATE SET stripe_customer_id = EXCLUDED.stripe_customer_id, status = EXCLUDED.status, price_id = EXCLUDED.price_id, cadence = EXCLUDED.cadence, quantity = EXCLUDED.quantity, cancel_at_period_end = EXCLUDED.cancel_at_period_end, current_period_start = EXCLUDED.current_period_start, current_period_end = EXCLUDED.current_period_end, updated_at = NOW() `, sub.ID, sub.NetworkID, sub.StripeCustomerID, sub.Status, sub.PriceID, sub.Cadence, sub.Quantity, sub.CancelAtPeriodEnd, sub.CurrentPeriodStart, sub.CurrentPeriodEnd, ) return err } func (r *repositoryImpl) deleteSubscriptionByID(ctx context.Context, subscriptionID string) error { _, err := r.pool.Exec(ctx, `DELETE FROM network_subscriptions WHERE id = $1`, subscriptionID, ) return err } func (r *repositoryImpl) getStripeCustomerID(ctx context.Context, networkID string) (string, error) { var customerID string err := r.pool.QueryRow(ctx, `SELECT stripe_customer_id FROM network_stripe_customers WHERE network_id = $1`, networkID, ).Scan(&customerID) if errors.Is(err, pgx.ErrNoRows) { return "", errNotFound } if err != nil { return "", err } return customerID, nil } func (r *repositoryImpl) setStripeCustomerID(ctx context.Context, networkID, customerID string) error { _, err := r.pool.Exec(ctx, ` INSERT INTO network_stripe_customers (network_id, stripe_customer_id) VALUES ($1, $2) ON CONFLICT (network_id) DO NOTHING `, networkID, customerID) return err }