PaymentProvider
Payment driver: Code, Capabilities, CreatePayment, ParseWebhook, Refund. See
Payment drivers.
The interfaces of the pkg/ext package are the only stable Go APIs for those who extend the core: changing them is
a breaking change. Everything in internal/ can change without notice. pkg/ext imports nothing from internal/,
so an extension depends on public types only.
type Extensions struct { PaymentProviders []PaymentProvider // added to "manual" and "stripe"; same Code = replaces FiscalDrivers []FiscalDriver // added to "none" EventSinks []EventSink // receive every event after the outbox (at least once) ConcessionData ConcessionDataProvider // perimeter, imagery, expiry of the concession TenantResolver TenantResolver // establishment from host/header (custom domains) AuthProviders []AuthProvider // external sign-in, e.g. OIDC Notifiers []Notifier // channels beyond email: SMS, WhatsApp BotProtection BotProtection // invisible challenge on public creations EventBus EventBus // real-time bus, e.g. PostgreSQL LISTEN/NOTIFY HTTPRoutes func(r chi.Router) // extra routes (not on /api/admin/v1, /api/public/v1, /admin, /b) AdminUIPlugins []UIPluginManifest // menu entries and pages in the panel}The official binary uses only the core implementations. To add yours, build your own main that registers them:
package main
import ( "context" "log"
"github.com/coste360/arenile/internal/app" "github.com/coste360/arenile/internal/config" "github.com/coste360/arenile/pkg/ext"
"example.com/arenile-satispay")
func main() { cfg, err := config.FromEnv() if err != nil { log.Fatal(err) } a, err := app.New(cfg, app.WithExtensions(ext.Extensions{ PaymentProviders: []ext.PaymentProvider{satispay.New()}, })) if err != nil { log.Fatal(err) } defer a.Close() if err := a.Run(context.Background()); err != nil { log.Fatal(err) }}PaymentProvider
Payment driver: Code, Capabilities, CreatePayment, ParseWebhook, Refund. See
Payment drivers.
FiscalDriver
Issuing commercial documents: Code, Issue, Void, Status. Lines come from the price snapshot (gross amounts,
rates in basis points: 2200 = 22%).
EventSink
Name, Handle(ctx, Event): receives every domain event after the outbox. Must be idempotent on the id.
EventBus
Publish, Subscribe: replaces the in-memory real-time bus, for several instances in parallel.
Notifier
Channel, Send: new notification channels alongside email.
AuthProvider
Code, DisplayName, BeginLogin, CompleteLogin: sign-in with an external identity provider.
TenantResolver
ResolveSlug(r): resolves the public page’s establishment from the domain (e.g. prenota.lido-a.it).
ConcessionDataProvider
Lookup(ctx, ref): state-owned beach concession data from external sources.
BotProtection
ClientConfig, Verify: invisible challenge with no imposed service.
UIPluginManifest
Menu entries (labels per language, Italian required) and a page URL loaded in a dedicated area of the panel.
type Event struct { ID string `json:"id"` // "evt_<uuidv7>" Type string `json:"type"` // e.g. "booking.confirmed" EstablishmentID string `json:"establishment_id"` OccurredAt time.Time `json:"occurred_at"` Data json.RawMessage `json:"data"` // versioned payload Version int `json:"version"`}The list of types is in Events and outgoing webhooks.
int64 amounts in cents, never float.EventSink does not block the core, but it must handle its own retries and idempotency.