Documentation
¶
Overview ¶
Package marketplace provides a unified framework for two-sided marketplace applications including listings, licenses, and subscriptions.
Index ¶
- Constants
- Variables
- func MergeSchema(appSchema string) string
- func PlanTiers() []string
- type API
- type APIConfig
- type ArchiveListingInput
- type ArchiveListingOutput
- type AssignSeatInput
- type AssignSeatOutput
- type AuthzSyncer
- type CheckoutRequest
- type CheckoutService
- type CheckoutSession
- type CreateCheckoutInput
- type CreateCheckoutOutput
- type CreateListingInput
- type CreateListingOutput
- type DeleteListingInput
- type EntLicenseService
- func (s *EntLicenseService) AssignSeat(ctx context.Context, assignment *SeatAssignment) error
- func (s *EntLicenseService) Check(ctx context.Context, listingID, orgID uuid.UUID) (bool, error)
- func (s *EntLicenseService) CheckPrincipal(ctx context.Context, listingID, principalID uuid.UUID) (bool, error)
- func (s *EntLicenseService) Get(ctx context.Context, id uuid.UUID) (*License, error)
- func (s *EntLicenseService) GetByListingAndOrg(ctx context.Context, listingID, orgID uuid.UUID) (*License, error)
- func (s *EntLicenseService) Grant(ctx context.Context, l *License) error
- func (s *EntLicenseService) List(ctx context.Context, orgID uuid.UUID) ([]*License, error)
- func (s *EntLicenseService) ListByListing(ctx context.Context, listingID uuid.UUID) ([]*License, error)
- func (s *EntLicenseService) ListSeatAssignments(ctx context.Context, licenseID uuid.UUID) ([]*SeatAssignment, error)
- func (s *EntLicenseService) Revoke(ctx context.Context, id uuid.UUID) error
- func (s *EntLicenseService) UnassignSeat(ctx context.Context, licenseID, principalID uuid.UUID) error
- func (s *EntLicenseService) Update(ctx context.Context, l *License) error
- type EntListingService
- func (s *EntListingService) Archive(ctx context.Context, id uuid.UUID) error
- func (s *EntListingService) Create(ctx context.Context, l *Listing) error
- func (s *EntListingService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *EntListingService) Get(ctx context.Context, id uuid.UUID) (*Listing, error)
- func (s *EntListingService) GetByProduct(ctx context.Context, productType string, productID uuid.UUID) (*Listing, error)
- func (s *EntListingService) List(ctx context.Context, opts ListListingsOptions) ([]*Listing, error)
- func (s *EntListingService) ListByCreator(ctx context.Context, creatorOrgID uuid.UUID) ([]*Listing, error)
- func (s *EntListingService) Publish(ctx context.Context, id uuid.UUID) error
- func (s *EntListingService) Update(ctx context.Context, l *Listing) error
- type EntService
- type EntSubscriptionService
- func (s *EntSubscriptionService) Cancel(ctx context.Context, id uuid.UUID) error
- func (s *EntSubscriptionService) CancelImmediately(ctx context.Context, id uuid.UUID) error
- func (s *EntSubscriptionService) ChangePlan(ctx context.Context, id uuid.UUID, newPlan string) error
- func (s *EntSubscriptionService) Create(ctx context.Context, sub *Subscription) error
- func (s *EntSubscriptionService) Get(ctx context.Context, id uuid.UUID) (*Subscription, error)
- func (s *EntSubscriptionService) GetByOrg(ctx context.Context, orgID uuid.UUID) (*Subscription, error)
- func (s *EntSubscriptionService) GetPlanTier(ctx context.Context, orgID uuid.UUID) (string, error)
- func (s *EntSubscriptionService) IsActive(ctx context.Context, orgID uuid.UUID) (bool, error)
- func (s *EntSubscriptionService) Reactivate(ctx context.Context, id uuid.UUID) error
- func (s *EntSubscriptionService) Update(ctx context.Context, sub *Subscription) error
- type GetLicenseInput
- type GetLicenseOutput
- type GetListingInput
- type GetListingOutput
- type License
- type LicenseResponse
- type LicenseService
- type LicenseType
- type ListLicensesInput
- type ListLicensesOutput
- type ListListingsInput
- type ListListingsOptions
- type ListListingsOutput
- type ListSeatsInput
- type ListSeatsOutput
- type Listing
- type ListingResponse
- type ListingService
- type ListingStatus
- type PricingModel
- type PublishListingInput
- type PublishListingOutput
- type RevenueShare
- type SeatAssignment
- type SeatAssignmentResponse
- type Service
- type SpiceDBSyncer
- func (s *SpiceDBSyncer) SyncLicense(ctx context.Context, license *License) error
- func (s *SpiceDBSyncer) SyncLicenseRevocation(ctx context.Context, license *License) error
- func (s *SpiceDBSyncer) SyncListing(ctx context.Context, listing *Listing) error
- func (s *SpiceDBSyncer) SyncSeatAssignment(ctx context.Context, assignment *SeatAssignment) error
- func (s *SpiceDBSyncer) SyncSeatUnassignment(ctx context.Context, licenseID, principalID uuid.UUID) error
- func (s *SpiceDBSyncer) SyncSubscription(ctx context.Context, sub *Subscription) error
- type Subscription
- type SubscriptionService
- type SubscriptionStatus
- type UnassignSeatInput
- type UpdateListingInput
- type UpdateListingOutput
- type WebhookInput
Constants ¶
const ( ResourceTypeListing = "listing" ResourceTypeLicense = "license" ResourceTypeSubscription = "subscription" ResourceTypeOrganization = "organization" ResourceTypeCreatorOrg = "creator_org" ResourceTypePrincipal = "principal" )
SpiceDB resource types for marketplace entities.
const ( RelationCreatorOrg = "creator_org" RelationOwner = "owner" RelationLicensedOrg = "licensed_org" RelationListing = "listing" RelationOrganization = "organization" RelationPurchasedBy = "purchased_by" RelationSeatHolder = "seat_holder" RelationSubscriber = "subscriber" )
SpiceDB relations for marketplace entities.
const ( PlanTierFree = "free" PlanTierStarter = "starter" PlanTierPro = "pro" PlanTierEnterprise = "enterprise" )
PlanTier constants for common subscription tiers.
Variables ¶
var ( // ErrListingNotFound is returned when a listing cannot be found. ErrListingNotFound = errors.New("listing not found") // ErrListingNotPublished is returned when accessing an unpublished listing. ErrListingNotPublished = errors.New("listing not published") // ErrLicenseNotFound is returned when a license cannot be found. ErrLicenseNotFound = errors.New("license not found") // ErrLicenseExpired is returned when a license has expired. ErrLicenseExpired = errors.New("license expired") // ErrLicenseNotYetValid is returned when a license hasn't started. ErrLicenseNotYetValid = errors.New("license not yet valid") // ErrNoSeatsAvailable is returned when all seats are assigned. ErrNoSeatsAvailable = errors.New("no seats available") // ErrSeatAlreadyAssigned is returned when a user already has a seat. ErrSeatAlreadyAssigned = errors.New("seat already assigned to this user") // ErrSeatNotAssigned is returned when trying to unassign a non-existent seat. ErrSeatNotAssigned = errors.New("seat not assigned to this user") // ErrSubscriptionNotFound is returned when a subscription cannot be found. ErrSubscriptionNotFound = errors.New("subscription not found") // ErrSubscriptionInactive is returned when a subscription is not active. ErrSubscriptionInactive = errors.New("subscription not active") ErrInvalidRevenueShare = errors.New("revenue share must add up to 100%") // ErrInvalidPricingModel is returned for unknown pricing models. ErrInvalidPricingModel = errors.New("invalid pricing model") // ErrInvalidLicenseType is returned for unknown license types. ErrInvalidLicenseType = errors.New("invalid license type") // ErrAlreadyLicensed is returned when an org already has a license. ErrAlreadyLicensed = errors.New("organization already licensed for this listing") // ErrCannotPurchaseOwnListing is returned when a creator tries to purchase their own listing. ErrCannotPurchaseOwnListing = errors.New("cannot purchase your own listing") // ErrPaymentRequired is returned when payment is required but not provided. ErrPaymentRequired = errors.New("payment required") // ErrPaymentFailed is returned when payment processing fails. ErrPaymentFailed = errors.New("payment failed") )
Sentinel errors for marketplace operations.
var MarketplaceSchema string
Functions ¶
func MergeSchema ¶
MergeSchema combines the marketplace schema with an application-specific schema. The app schema should define app-specific resource types that reference marketplace types (listing, license, etc.).
Types ¶
type API ¶
type API struct {
// contains filtered or unexported fields
}
API handles marketplace HTTP endpoints.
type APIConfig ¶
type APIConfig struct {
// BasePath is the API base path (e.g., "/api/v1").
BasePath string
}
APIConfig holds API configuration.
type ArchiveListingInput ¶
type ArchiveListingInput struct {
ID string `path:"id" format:"uuid"`
}
ArchiveListingInput is the request for archiving a listing.
type ArchiveListingOutput ¶
type ArchiveListingOutput struct {
Body *ListingResponse
}
ArchiveListingOutput is the response for archiving a listing.
type AssignSeatInput ¶
type AssignSeatInput struct {
LicenseID string `path:"license_id" format:"uuid"`
Body struct {
PrincipalID string `json:"principal_id" required:"true" format:"uuid"`
AssignedBy string `json:"assigned_by" required:"true" format:"uuid"`
}
}
AssignSeatInput is the request for assigning a seat.
type AssignSeatOutput ¶
type AssignSeatOutput struct {
Body *SeatAssignmentResponse
}
AssignSeatOutput is the response for assigning a seat.
type AuthzSyncer ¶
type AuthzSyncer interface {
// SyncListing syncs a listing to the authorization system.
SyncListing(ctx context.Context, listing *Listing) error
// SyncLicense syncs a license grant to the authorization system.
SyncLicense(ctx context.Context, license *License) error
// SyncLicenseRevocation syncs a license revocation to the authorization system.
SyncLicenseRevocation(ctx context.Context, license *License) error
// SyncSeatAssignment syncs a seat assignment to the authorization system.
SyncSeatAssignment(ctx context.Context, assignment *SeatAssignment) error
// SyncSeatUnassignment syncs a seat removal to the authorization system.
SyncSeatUnassignment(ctx context.Context, licenseID, principalID uuid.UUID) error
}
AuthzSyncer syncs marketplace entities to the authorization system.
type CheckoutRequest ¶
type CheckoutRequest struct {
// ListingID is the listing to purchase.
ListingID uuid.UUID
// OrganizationID is the purchasing organization.
OrganizationID uuid.UUID
// PurchaserID is the principal making the purchase.
PurchaserID uuid.UUID
// Seats is the number of seats (for per-seat pricing).
Seats *int
// SuccessURL is the redirect URL on success.
SuccessURL string
// CancelURL is the redirect URL on cancel.
CancelURL string
}
CheckoutRequest contains parameters for creating a checkout session.
type CheckoutService ¶
type CheckoutService interface {
// CreateCheckoutSession creates a Stripe checkout session for a listing.
CreateCheckoutSession(ctx context.Context, req CheckoutRequest) (*CheckoutSession, error)
// ProcessWebhook handles Stripe webhook events.
ProcessWebhook(ctx context.Context, payload []byte, signature string) error
}
CheckoutService provides operations for purchasing licenses.
type CheckoutSession ¶
type CheckoutSession struct {
// SessionID is the Stripe checkout session ID.
SessionID string
// URL is the checkout URL to redirect the user to.
URL string
}
CheckoutSession contains the result of creating a checkout session.
type CreateCheckoutInput ¶
type CreateCheckoutInput struct {
Body struct {
ListingID string `json:"listing_id" required:"true" format:"uuid"`
OrganizationID string `json:"organization_id" required:"true" format:"uuid"`
PurchaserID string `json:"purchaser_id" required:"true" format:"uuid"`
Seats *int `json:"seats,omitempty" minimum:"1"`
SuccessURL string `json:"success_url" required:"true" format:"uri"`
CancelURL string `json:"cancel_url" required:"true" format:"uri"`
}
}
CreateCheckoutInput is the request for creating a checkout session.
type CreateCheckoutOutput ¶
type CreateCheckoutOutput struct {
Body struct {
SessionID string `json:"session_id"`
URL string `json:"url"`
}
}
CreateCheckoutOutput is the response for creating a checkout session.
type CreateListingInput ¶
type CreateListingInput struct {
Body struct {
CreatorOrgID string `json:"creator_org_id" required:"true" format:"uuid"`
OwnerID string `json:"owner_id" required:"true" format:"uuid"`
ProductType string `json:"product_type" required:"true" minLength:"1" maxLength:"50"`
ProductID *string `json:"product_id,omitempty" format:"uuid"`
Title string `json:"title" required:"true" minLength:"1" maxLength:"200"`
Description string `json:"description,omitempty" maxLength:"5000"`
PricingModel string `json:"pricing_model" required:"true" enum:"free,one_time,subscription,per_seat"`
PriceCents int64 `json:"price_cents" minimum:"0"`
Currency string `json:"currency" required:"true" minLength:"3" maxLength:"3" default:"USD"`
Metadata map[string]any `json:"metadata,omitempty"`
}
}
CreateListingInput is the request for creating a listing.
type CreateListingOutput ¶
type CreateListingOutput struct {
Body *ListingResponse
}
CreateListingOutput is the response for creating a listing.
type DeleteListingInput ¶
type DeleteListingInput struct {
ID string `path:"id" format:"uuid"`
}
DeleteListingInput is the request for deleting a listing.
type EntLicenseService ¶
type EntLicenseService struct {
// contains filtered or unexported fields
}
EntLicenseService is an Ent-backed implementation of LicenseService.
func NewEntLicenseService ¶
func NewEntLicenseService(client *ent.Client, authzSync AuthzSyncer) *EntLicenseService
NewEntLicenseService creates a new Ent-backed license service.
func (*EntLicenseService) AssignSeat ¶
func (s *EntLicenseService) AssignSeat(ctx context.Context, assignment *SeatAssignment) error
AssignSeat assigns a user to a license seat.
func (*EntLicenseService) Check ¶
Check checks if an organization has a valid license for a listing.
func (*EntLicenseService) CheckPrincipal ¶
func (s *EntLicenseService) CheckPrincipal(ctx context.Context, listingID, principalID uuid.UUID) (bool, error)
CheckPrincipal checks if a principal has access via license.
func (*EntLicenseService) GetByListingAndOrg ¶
func (s *EntLicenseService) GetByListingAndOrg(ctx context.Context, listingID, orgID uuid.UUID) (*License, error)
GetByListingAndOrg retrieves a license for a specific listing and organization.
func (*EntLicenseService) Grant ¶
func (s *EntLicenseService) Grant(ctx context.Context, l *License) error
Grant creates a new license for an organization.
func (*EntLicenseService) ListByListing ¶
func (s *EntLicenseService) ListByListing(ctx context.Context, listingID uuid.UUID) ([]*License, error)
ListByListing retrieves all licenses for a listing.
func (*EntLicenseService) ListSeatAssignments ¶
func (s *EntLicenseService) ListSeatAssignments(ctx context.Context, licenseID uuid.UUID) ([]*SeatAssignment, error)
ListSeatAssignments retrieves all seat assignments for a license.
func (*EntLicenseService) UnassignSeat ¶
func (s *EntLicenseService) UnassignSeat(ctx context.Context, licenseID, principalID uuid.UUID) error
UnassignSeat removes a user from a license seat.
type EntListingService ¶
type EntListingService struct {
// contains filtered or unexported fields
}
EntListingService is an Ent-backed implementation of ListingService.
func NewEntListingService ¶
func NewEntListingService(client *ent.Client, authzSync AuthzSyncer) *EntListingService
NewEntListingService creates a new Ent-backed listing service.
func (*EntListingService) Create ¶
func (s *EntListingService) Create(ctx context.Context, l *Listing) error
Create creates a new listing in draft status.
func (*EntListingService) GetByProduct ¶
func (s *EntListingService) GetByProduct(ctx context.Context, productType string, productID uuid.UUID) (*Listing, error)
GetByProduct retrieves a listing by product type and ID.
func (*EntListingService) List ¶
func (s *EntListingService) List(ctx context.Context, opts ListListingsOptions) ([]*Listing, error)
List retrieves listings with optional filters.
func (*EntListingService) ListByCreator ¶
func (s *EntListingService) ListByCreator(ctx context.Context, creatorOrgID uuid.UUID) ([]*Listing, error)
ListByCreator retrieves all listings for a creator organization.
type EntService ¶
type EntService struct {
// contains filtered or unexported fields
}
EntService is the Ent-backed implementation of the marketplace Service interface.
func NewEntService ¶
func NewEntService(client *ent.Client, authzSync AuthzSyncer) *EntService
NewEntService creates a new Ent-backed marketplace service. The authzSync parameter is optional and can be nil if authorization sync is not needed.
func (*EntService) Checkout ¶
func (s *EntService) Checkout() CheckoutService
Checkout returns the checkout service.
func (*EntService) Licenses ¶
func (s *EntService) Licenses() LicenseService
Licenses returns the license service.
func (*EntService) Listings ¶
func (s *EntService) Listings() ListingService
Listings returns the listing service.
func (*EntService) Subscriptions ¶
func (s *EntService) Subscriptions() SubscriptionService
Subscriptions returns the subscription service.
func (*EntService) WithCheckout ¶
func (s *EntService) WithCheckout(checkout CheckoutService) *EntService
WithCheckout sets the checkout service.
type EntSubscriptionService ¶
type EntSubscriptionService struct {
// contains filtered or unexported fields
}
EntSubscriptionService is an Ent-backed implementation of SubscriptionService.
func NewEntSubscriptionService ¶
func NewEntSubscriptionService(client *ent.Client) *EntSubscriptionService
NewEntSubscriptionService creates a new Ent-backed subscription service.
func (*EntSubscriptionService) CancelImmediately ¶
CancelImmediately cancels a subscription immediately.
func (*EntSubscriptionService) ChangePlan ¶
func (s *EntSubscriptionService) ChangePlan(ctx context.Context, id uuid.UUID, newPlan string) error
ChangePlan changes the subscription plan.
func (*EntSubscriptionService) Create ¶
func (s *EntSubscriptionService) Create(ctx context.Context, sub *Subscription) error
Create creates a new subscription.
func (*EntSubscriptionService) Get ¶
func (s *EntSubscriptionService) Get(ctx context.Context, id uuid.UUID) (*Subscription, error)
Get retrieves a subscription by ID.
func (*EntSubscriptionService) GetByOrg ¶
func (s *EntSubscriptionService) GetByOrg(ctx context.Context, orgID uuid.UUID) (*Subscription, error)
GetByOrg retrieves the subscription for an organization.
func (*EntSubscriptionService) GetPlanTier ¶
GetPlanTier returns the current plan tier for an organization.
func (*EntSubscriptionService) IsActive ¶
IsActive checks if an organization has an active subscription.
func (*EntSubscriptionService) Reactivate ¶
Reactivate reactivates a canceled subscription.
func (*EntSubscriptionService) Update ¶
func (s *EntSubscriptionService) Update(ctx context.Context, sub *Subscription) error
Update updates a subscription's details.
type GetLicenseInput ¶
type GetLicenseInput struct {
ID string `path:"id" format:"uuid"`
}
GetLicenseInput is the request for getting a license.
type GetLicenseOutput ¶
type GetLicenseOutput struct {
Body *LicenseResponse
}
GetLicenseOutput is the response for getting a license.
type GetListingInput ¶
type GetListingInput struct {
ID string `path:"id" format:"uuid"`
}
GetListingInput is the request for getting a listing.
type GetListingOutput ¶
type GetListingOutput struct {
Body *ListingResponse
}
GetListingOutput is the response for getting a listing.
type License ¶
type License struct {
// ID is the unique identifier for this license.
ID uuid.UUID `json:"id"`
// ListingID references the marketplace listing.
ListingID uuid.UUID `json:"listingId"`
// OrganizationID is the organization that holds this license.
OrganizationID uuid.UUID `json:"organizationId"`
// LicenseType defines the scope of access.
LicenseType LicenseType `json:"licenseType"`
// Seats is the number of allowed users (nil for unlimited).
Seats *int `json:"seats,omitempty"`
// UsedSeats is the current number of assigned users.
UsedSeats int `json:"usedSeats"`
// ValidFrom is when the license becomes active.
ValidFrom time.Time `json:"validFrom"`
// ValidUntil is when the license expires (nil for perpetual).
ValidUntil *time.Time `json:"validUntil,omitempty"`
// StripeSubscriptionID is the Stripe subscription (for recurring).
StripeSubscriptionID *string `json:"stripeSubscriptionId,omitempty"`
// PurchasedBy is the principal who purchased this license.
PurchasedBy uuid.UUID `json:"purchasedBy"`
// CreatedAt is when the license was created.
CreatedAt time.Time `json:"createdAt"`
// UpdatedAt is when the license was last modified.
UpdatedAt time.Time `json:"updatedAt"`
}
License represents an entitlement to use a product.
func (*License) HasAvailableSeats ¶
HasAvailableSeats returns true if seats are available for assignment.
func (*License) SeatsRemaining ¶
SeatsRemaining returns the number of seats available. Returns -1 for unlimited licenses.
type LicenseResponse ¶
type LicenseResponse struct {
ID string `json:"id"`
ListingID string `json:"listing_id"`
OrganizationID string `json:"organization_id"`
LicenseType string `json:"license_type"`
Seats *int `json:"seats,omitempty"`
UsedSeats int `json:"used_seats"`
ValidFrom time.Time `json:"valid_from"`
ValidUntil *time.Time `json:"valid_until,omitempty"`
StripeSubscriptionID *string `json:"stripe_subscription_id,omitempty"`
PurchasedBy string `json:"purchased_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
LicenseResponse is the API representation of a license.
type LicenseService ¶
type LicenseService interface {
// Grant creates a new license for an organization.
Grant(ctx context.Context, license *License) error
// Get retrieves a license by ID.
Get(ctx context.Context, id uuid.UUID) (*License, error)
// GetByListingAndOrg retrieves a license for a specific listing and organization.
GetByListingAndOrg(ctx context.Context, listingID, orgID uuid.UUID) (*License, error)
// Update updates a license's details.
Update(ctx context.Context, license *License) error
// Revoke revokes a license, removing access.
Revoke(ctx context.Context, id uuid.UUID) error
// Check checks if an organization has a valid license for a listing.
Check(ctx context.Context, listingID, orgID uuid.UUID) (bool, error)
// CheckPrincipal checks if a principal has access via license.
CheckPrincipal(ctx context.Context, listingID, principalID uuid.UUID) (bool, error)
// List retrieves licenses for an organization.
List(ctx context.Context, orgID uuid.UUID) ([]*License, error)
// ListByListing retrieves all licenses for a listing.
ListByListing(ctx context.Context, listingID uuid.UUID) ([]*License, error)
// AssignSeat assigns a user to a license seat.
AssignSeat(ctx context.Context, assignment *SeatAssignment) error
// UnassignSeat removes a user from a license seat.
UnassignSeat(ctx context.Context, licenseID, principalID uuid.UUID) error
// ListSeatAssignments retrieves all seat assignments for a license.
ListSeatAssignments(ctx context.Context, licenseID uuid.UUID) ([]*SeatAssignment, error)
}
LicenseService provides operations for licenses and entitlements.
type LicenseType ¶
type LicenseType string
LicenseType defines the scope of a license.
const ( // LicenseSeatBased grants access to a specific number of users. LicenseSeatBased LicenseType = "seat_based" // LicenseTeam grants access to all members of a team/group. LicenseTeam LicenseType = "team" // LicenseUnlimited grants unlimited access within an organization. LicenseUnlimited LicenseType = "unlimited" )
type ListLicensesInput ¶
type ListLicensesInput struct {
OrganizationID string `query:"organization_id" required:"true" format:"uuid"`
}
ListLicensesInput is the request for listing licenses.
type ListLicensesOutput ¶
type ListLicensesOutput struct {
Body struct {
Licenses []*LicenseResponse `json:"licenses"`
}
}
ListLicensesOutput is the response for listing licenses.
type ListListingsInput ¶
type ListListingsInput struct {
Status *string `query:"status" enum:"draft,pending_review,published,archived"`
ProductType *string `query:"product_type"`
CreatorOrgID *string `query:"creator_org_id" format:"uuid"`
Published *bool `query:"published"`
Limit int `query:"limit" default:"20" minimum:"1" maximum:"100"`
Offset int `query:"offset" default:"0" minimum:"0"`
}
ListListingsInput is the request for listing listings.
type ListListingsOptions ¶
type ListListingsOptions struct {
// Status filters by listing status.
Status *ListingStatus
// ProductType filters by product type.
ProductType *string
// CreatorOrgID filters by creator organization.
CreatorOrgID *uuid.UUID
// PublishedOnly returns only published listings.
PublishedOnly bool
// Limit is the maximum number of results.
Limit int
// Offset is the pagination offset.
Offset int
// OrderBy specifies the sort order.
OrderBy string
}
ListListingsOptions configures listing queries.
type ListListingsOutput ¶
type ListListingsOutput struct {
Body struct {
Listings []*ListingResponse `json:"listings"`
Total int `json:"total"`
}
}
ListListingsOutput is the response for listing listings.
type ListSeatsInput ¶
type ListSeatsInput struct {
LicenseID string `path:"license_id" format:"uuid"`
}
ListSeatsInput is the request for listing seat assignments.
type ListSeatsOutput ¶
type ListSeatsOutput struct {
Body struct {
Seats []*SeatAssignmentResponse `json:"seats"`
TotalSeats *int `json:"total_seats,omitempty"`
UsedSeats int `json:"used_seats"`
AvailableSeats int `json:"available_seats"`
}
}
ListSeatsOutput is the response for listing seat assignments.
type Listing ¶
type Listing struct {
// ID is the unique identifier for this listing.
ID uuid.UUID `json:"id"`
// CreatorOrgID is the organization that created this listing.
CreatorOrgID uuid.UUID `json:"creatorOrgId"`
// OwnerID is the principal who owns this listing.
OwnerID uuid.UUID `json:"ownerId"`
// ProductType identifies the type of product (app-specific).
// Examples: "course", "dashboard_template", "data_connector"
ProductType string `json:"productType"`
// ProductID references the actual product in the app's domain.
ProductID uuid.UUID `json:"productId"`
// Title is the display name for the listing.
Title string `json:"title"`
// Description is the detailed description of the product.
Description string `json:"description,omitempty"`
// PricingModel defines how the product is priced.
PricingModel PricingModel `json:"pricingModel"`
// PriceCents is the price in cents (0 for free).
PriceCents int64 `json:"priceCents"`
// Currency is the ISO 4217 currency code.
Currency string `json:"currency"`
// Status is the publication state.
Status ListingStatus `json:"status"`
// Metadata contains app-specific data.
Metadata map[string]any `json:"metadata,omitempty"`
// CreatedAt is when the listing was created.
CreatedAt time.Time `json:"createdAt"`
// UpdatedAt is when the listing was last modified.
UpdatedAt time.Time `json:"updatedAt"`
// PublishedAt is when the listing was published (nil if not published).
PublishedAt *time.Time `json:"publishedAt,omitempty"`
}
Listing represents a product available on the marketplace.
func (*Listing) IsPublished ¶
IsPublished returns true if the listing is live on the marketplace.
type ListingResponse ¶
type ListingResponse struct {
ID string `json:"id"`
CreatorOrgID string `json:"creator_org_id"`
OwnerID string `json:"owner_id"`
ProductType string `json:"product_type"`
ProductID *string `json:"product_id,omitempty"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
PricingModel string `json:"pricing_model"`
PriceCents int64 `json:"price_cents"`
Currency string `json:"currency"`
Status string `json:"status"`
Metadata map[string]any `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PublishedAt *time.Time `json:"published_at,omitempty"`
}
ListingResponse is the API representation of a listing.
type ListingService ¶
type ListingService interface {
// Create creates a new listing in draft status.
Create(ctx context.Context, listing *Listing) error
// Get retrieves a listing by ID.
Get(ctx context.Context, id uuid.UUID) (*Listing, error)
// GetByProduct retrieves a listing by product type and ID.
GetByProduct(ctx context.Context, productType string, productID uuid.UUID) (*Listing, error)
// Update updates a listing's details.
Update(ctx context.Context, listing *Listing) error
// Delete removes a listing (must be draft or archived).
Delete(ctx context.Context, id uuid.UUID) error
// Publish publishes a listing to the marketplace.
Publish(ctx context.Context, id uuid.UUID) error
// Archive archives a listing, removing it from the marketplace.
Archive(ctx context.Context, id uuid.UUID) error
// List retrieves listings with optional filters.
List(ctx context.Context, opts ListListingsOptions) ([]*Listing, error)
// ListByCreator retrieves all listings for a creator organization.
ListByCreator(ctx context.Context, creatorOrgID uuid.UUID) ([]*Listing, error)
}
ListingService provides operations for marketplace listings.
type ListingStatus ¶
type ListingStatus string
ListingStatus represents the publication state of a listing.
const ( // ListingStatusDraft indicates the listing is being prepared. ListingStatusDraft ListingStatus = "draft" // ListingStatusPendingReview indicates the listing is awaiting approval. ListingStatusPendingReview ListingStatus = "pending_review" // ListingStatusPublished indicates the listing is live on the marketplace. ListingStatusPublished ListingStatus = "published" // ListingStatusArchived indicates the listing has been retired. ListingStatusArchived ListingStatus = "archived" )
func ListingStatuses ¶
func ListingStatuses() []ListingStatus
ListingStatuses returns all valid listing statuses.
type PricingModel ¶
type PricingModel string
PricingModel defines how a product is priced.
const ( // PricingFree indicates no charge (may require attribution). PricingFree PricingModel = "free" // PricingOneTime indicates a single purchase for perpetual access. PricingOneTime PricingModel = "one_time" // PricingSubscription indicates recurring monthly/annual billing. PricingSubscription PricingModel = "subscription" // PricingPerSeat indicates per-user pricing within an organization. PricingPerSeat PricingModel = "per_seat" )
func PricingModels ¶
func PricingModels() []PricingModel
PricingModels returns all valid pricing models.
type PublishListingInput ¶
type PublishListingInput struct {
ID string `path:"id" format:"uuid"`
}
PublishListingInput is the request for publishing a listing.
type PublishListingOutput ¶
type PublishListingOutput struct {
Body *ListingResponse
}
PublishListingOutput is the response for publishing a listing.
type RevenueShare ¶
type RevenueShare struct {
CreatorPercent int `json:"creatorPercent"`
PlatformPercent int `json:"platformPercent"`
}
RevenueShare defines the revenue split for marketplace sales.
func DefaultRevenueShare ¶
func DefaultRevenueShare() RevenueShare
DefaultRevenueShare returns the default revenue split (70/30).
func (RevenueShare) Validate ¶
func (r RevenueShare) Validate() error
Validate checks if the revenue share adds up to 100%.
type SeatAssignment ¶
type SeatAssignment struct {
// ID is the unique identifier for this assignment.
ID uuid.UUID `json:"id"`
// LicenseID references the license.
LicenseID uuid.UUID `json:"licenseId"`
// PrincipalID is the user assigned to this seat.
PrincipalID uuid.UUID `json:"principalId"`
// AssignedBy is who made this assignment.
AssignedBy uuid.UUID `json:"assignedBy"`
// AssignedAt is when the seat was assigned.
AssignedAt time.Time `json:"assignedAt"`
}
SeatAssignment represents a user's assignment to a license seat.
type SeatAssignmentResponse ¶
type SeatAssignmentResponse struct {
ID string `json:"id"`
LicenseID string `json:"license_id"`
PrincipalID string `json:"principal_id"`
AssignedBy string `json:"assigned_by"`
AssignedAt time.Time `json:"assigned_at"`
}
SeatAssignmentResponse is the API representation of a seat assignment.
type Service ¶
type Service interface {
Listings() ListingService
Licenses() LicenseService
Subscriptions() SubscriptionService
Checkout() CheckoutService
}
Service combines all marketplace services.
type SpiceDBSyncer ¶
type SpiceDBSyncer struct {
// contains filtered or unexported fields
}
SpiceDBSyncer implements AuthzSyncer using SpiceDB.
func NewSpiceDBSyncer ¶
func NewSpiceDBSyncer(client *spicedb.Client) *SpiceDBSyncer
NewSpiceDBSyncer creates a new SpiceDB syncer for marketplace entities.
func (*SpiceDBSyncer) SyncLicense ¶
func (s *SpiceDBSyncer) SyncLicense(ctx context.Context, license *License) error
SyncLicense syncs a license grant to SpiceDB. Creates relationships:
- listing:listingId -> licensed_org -> organization:orgId
- license:id -> listing -> listing:listingId
- license:id -> organization -> organization:orgId
- license:id -> purchased_by -> principal:purchasedBy
func (*SpiceDBSyncer) SyncLicenseRevocation ¶
func (s *SpiceDBSyncer) SyncLicenseRevocation(ctx context.Context, license *License) error
SyncLicenseRevocation removes a license from SpiceDB. Removes relationships:
- listing:listingId -> licensed_org -> organization:orgId
func (*SpiceDBSyncer) SyncListing ¶
func (s *SpiceDBSyncer) SyncListing(ctx context.Context, listing *Listing) error
SyncListing syncs a listing to SpiceDB. Creates relationships:
- listing:id -> creator_org -> creator_org:creatorOrgId
- listing:id -> owner -> principal:ownerId
func (*SpiceDBSyncer) SyncSeatAssignment ¶
func (s *SpiceDBSyncer) SyncSeatAssignment(ctx context.Context, assignment *SeatAssignment) error
SyncSeatAssignment syncs a seat assignment to SpiceDB. Creates relationship:
- license:licenseId -> seat_holder -> principal:principalId
func (*SpiceDBSyncer) SyncSeatUnassignment ¶
func (s *SpiceDBSyncer) SyncSeatUnassignment(ctx context.Context, licenseID, principalID uuid.UUID) error
SyncSeatUnassignment removes a seat assignment from SpiceDB. Removes relationship:
- license:licenseId -> seat_holder -> principal:principalId
func (*SpiceDBSyncer) SyncSubscription ¶
func (s *SpiceDBSyncer) SyncSubscription(ctx context.Context, sub *Subscription) error
SyncSubscription syncs a subscription to SpiceDB. Creates relationships:
- subscription:id -> organization -> organization:orgId
type Subscription ¶
type Subscription struct {
// ID is the unique identifier for this subscription.
ID uuid.UUID `json:"id"`
// OrganizationID is the subscribing organization.
OrganizationID uuid.UUID `json:"organizationId"`
// PlanTier is the subscription plan name.
PlanTier string `json:"planTier"`
// Status is the subscription state.
Status SubscriptionStatus `json:"status"`
// CurrentPeriodStart is when the current billing period began.
CurrentPeriodStart time.Time `json:"currentPeriodStart"`
// CurrentPeriodEnd is when the current billing period ends.
CurrentPeriodEnd time.Time `json:"currentPeriodEnd"`
// StripeSubscriptionID is the Stripe subscription ID.
StripeSubscriptionID string `json:"stripeSubscriptionId"`
// StripeCustomerID is the Stripe customer ID.
StripeCustomerID string `json:"stripeCustomerId"`
// CancelAtPeriodEnd indicates if subscription will cancel at period end.
CancelAtPeriodEnd bool `json:"cancelAtPeriodEnd"`
// CreatedAt is when the subscription was created.
CreatedAt time.Time `json:"createdAt"`
// UpdatedAt is when the subscription was last modified.
UpdatedAt time.Time `json:"updatedAt"`
}
Subscription represents a platform-level subscription for an organization.
func (*Subscription) DaysRemaining ¶
func (s *Subscription) DaysRemaining() int
DaysRemaining returns the days until the current period ends.
func (*Subscription) IsActive ¶
func (s *Subscription) IsActive() bool
IsActive returns true if the subscription is currently active.
func (*Subscription) IsInTrial ¶
func (s *Subscription) IsInTrial() bool
IsInTrial returns true if the subscription is in trial period.
type SubscriptionService ¶
type SubscriptionService interface {
// Create creates a new subscription.
Create(ctx context.Context, sub *Subscription) error
// Get retrieves a subscription by ID.
Get(ctx context.Context, id uuid.UUID) (*Subscription, error)
// GetByOrg retrieves the subscription for an organization.
GetByOrg(ctx context.Context, orgID uuid.UUID) (*Subscription, error)
// Update updates a subscription's details.
Update(ctx context.Context, sub *Subscription) error
// Cancel cancels a subscription at period end.
Cancel(ctx context.Context, id uuid.UUID) error
// CancelImmediately cancels a subscription immediately.
CancelImmediately(ctx context.Context, id uuid.UUID) error
// Reactivate reactivates a canceled subscription.
Reactivate(ctx context.Context, id uuid.UUID) error
// ChangePlan changes the subscription plan.
ChangePlan(ctx context.Context, id uuid.UUID, newPlan string) error
// IsActive checks if an organization has an active subscription.
IsActive(ctx context.Context, orgID uuid.UUID) (bool, error)
// GetPlanTier returns the current plan tier for an organization.
GetPlanTier(ctx context.Context, orgID uuid.UUID) (string, error)
}
SubscriptionService provides operations for platform subscriptions.
type SubscriptionStatus ¶
type SubscriptionStatus string
SubscriptionStatus represents the state of a platform subscription.
const ( // SubscriptionStatusActive indicates the subscription is current. SubscriptionStatusActive SubscriptionStatus = "active" // SubscriptionStatusTrialing indicates the subscription is in trial period. SubscriptionStatusTrialing SubscriptionStatus = "trialing" // SubscriptionStatusPastDue indicates payment has failed. SubscriptionStatusPastDue SubscriptionStatus = "past_due" // SubscriptionStatusCanceled indicates the subscription has been canceled. SubscriptionStatusCanceled SubscriptionStatus = "canceled" // SubscriptionStatusUnpaid indicates multiple payment failures. SubscriptionStatusUnpaid SubscriptionStatus = "unpaid" )
func SubscriptionStatuses ¶
func SubscriptionStatuses() []SubscriptionStatus
SubscriptionStatuses returns all valid subscription statuses.
type UnassignSeatInput ¶
type UnassignSeatInput struct {
LicenseID string `path:"license_id" format:"uuid"`
PrincipalID string `path:"principal_id" format:"uuid"`
}
UnassignSeatInput is the request for unassigning a seat.
type UpdateListingInput ¶
type UpdateListingInput struct {
ID string `path:"id" format:"uuid"`
Body struct {
Title *string `json:"title,omitempty" maxLength:"200"`
Description *string `json:"description,omitempty" maxLength:"5000"`
PricingModel *string `json:"pricing_model,omitempty" enum:"free,one_time,subscription,per_seat"`
PriceCents *int64 `json:"price_cents,omitempty" minimum:"0"`
Currency *string `json:"currency,omitempty" minLength:"3" maxLength:"3"`
ProductID *string `json:"product_id,omitempty" format:"uuid"`
Metadata map[string]any `json:"metadata,omitempty"`
}
}
UpdateListingInput is the request for updating a listing.
type UpdateListingOutput ¶
type UpdateListingOutput struct {
Body *ListingResponse
}
UpdateListingOutput is the response for updating a listing.
type WebhookInput ¶
WebhookInput is the request for Stripe webhooks.