Files
llink/go/internal/billing/repository.go
T
Arjun Patel 67826b92c0 implement paywall (#161)
* implement core foundation

* inject deps

* fix incorrect migration

* tail migration

* use transaction for migration

* fix: inject deps for tests

* cleanup billing management for admin

* upgrade stripe sdk to v85

* set price env variables

* cleanup billing management

* allow multiple dev windows

* fix: settings scroll

* feat: show nice video thumbnail in listview

* feat: implement freemium restrictions

* remove unnecessary comments

* refactor

* docs

* format

* tweak network settings better hierarchy
2026-04-14 15:18:32 -07:00

113 lines
3.5 KiB
Go

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
}