timeax / dgp-services-sdk
Digital Growth Platform (DGP) Services SDK: capability-based contracts and typed request/response DTOs for Laravel-first service provider plugins (e.g., JustAnotherPanel), covering catalog, orders, status, refill, bulk ops, discovery, infra (auth/transport/retry/rate-limit/idempotency), and ops (hea
Requires
- php: ^8.2
- illuminate/support: v12.47.0
- timeax/ui-config-schema: ^0.0.2
This package is auto-updated.
Last update: 2026-04-09 09:30:32 UTC
README
A Laravel-first SDK that defines the service-driver contracts your DGP plugins implement, plus the typed DTO payloads (Requests/Responses/Types) they exchange with the host.
This package is not your plugin system. Your plugin system already:
- installs/enables plugins
- collects config requirements + renders admin settings UI
- loads the plugin’s exported class (default export)
- passes a
dgp_handler/ runtime context (and the plugin can also read its own config)
This SDK’s job is to standardize what that exported class implements, so the Host can call any provider in a uniform way.
What this package enables
1) Capability-based drivers
Drivers implement only what they support (refill, cancel, bulk, fast-schema, etc.). The Host checks support via instanceof (and/or a capability map).
if ($driver instanceof OrderRefillContract) { // refill supported }
2) Typed payloads (no magic arrays)
Every call uses typed Request DTOs and returns typed Response DTOs, wrapped in a consistent Result<T>.
3) A clean “Driver layer”
A registry + resolver + manager gives the Host a single way to:
- register available drivers
- resolve a driver by key
- call capability methods safely
- apply boundary wrappers (normalize errors, retry rules, rate-limit hints, etc.)
4) A baseline for panel-like providers
Using JustAnotherPanel as the reference model, the SDK targets common features:
services → order create/status → balance → optional cancel/refill/bulk.
Two-lane schema model (important)
Providers can integrate at two schema levels:
Lane A — Raw / Baseline (Host-owned normalization)
- Contract:
ServicesCatalogContract(MUST) - Returns:
ServiceDefinition[](raw list) - Host job: normalize raw services into the Host’s internal UI state (tags/fields/options/constraints) using Host rules.
This is the default and works for every provider.
Lane B — Fast lane / Pre-normalized (Advanced)
- Contract:
ServiceSchemaContract(OPTIONAL / ADVANCED) - Returns:
ServiceProps(TS-parity UI schema) - Host job: verify + clamp + cache the schema, then serve to frontend.
This lane exists for sophisticated plugins that already compute the full UI schema.
“Fast lane” does not mean blind trust. The Host validates and can reject/fallback to Lane A.
Decisions locked in
- Result-first return strategy: contract calls return
Result<ResponseDto>so business failures (insufficient funds, rate-limited, invalid params) are first-class outcomes. - Exceptions are internal: throw only for unrecoverable/system/invariant issues (transport, misconfiguration).
DriverManagerconverts them toResultat the boundary. - TS parity for
ServiceProps: schema types must serialize into the exact JSON keys your React UI expects (snake_case + camelCase as designed). - Pre-flight validation: validate DTO invariants before any HTTP call.
- Safe schema execution: any “code-like” fields (e.g., quantity
eval+code) must be treated as a DSL / restricted expression, never raw eval.
Critical implementation note: Lane B serialization keys
Your TS schema uses a mixed key convention (example: pricing_role but quantityDefault).
Do not rely on automatic property names for Lane B. Types/Schema/* should implement JsonSerializable (or your Arrayable) and explicitly output the correct keys:
public function jsonSerialize(): array { return array_filter([ 'id' => $this->id, 'pricing_role' => $this->pricing_role, 'quantityDefault' => $this->quantityDefault, ], fn ($v) => $v !== null); }
Folder structure map (src)
src/
DgpSdk.php
DgpSdkServiceProvider.php
Driver/
DriverContext.php // runtime context: config + services (transport/logger/cache/clock)
DriverRegistry.php // registered driver factories + metadata
DriverResolver.php // resolve driver instance by key (registry + context)
DriverManager.php // boundary wrapper: resolve + capability helpers + Result wrapping
AbstractServiceDriver.php // base class: core helpers + HealthCheck default
Contracts/
Catalog/
ServicesCatalogContract.php // CORE (MUST): raw service catalog (Lane A)
ServiceInputSchemaContract.php // OPTIONAL: minimal per-service input schema (Host still normalizes)
ServiceSchemaContract.php // OPTIONAL/ADVANCED: pre-normalized ServiceProps (Lane B fast lane)
OperationInputSchemaContract // OPTIONAL/ADVANCED
Orders/
OrderCreateContract.php // CORE (MUST): place order
OrderStatusContract.php // CORE (MUST): status lookup
OrderCancelContract.php // OPTIONAL: cancel
Balance/
BalanceContract.php // CORE (MUST for panel-like): provider balance
Refill/
OrderRefillContract.php // OPTIONAL: create refill
RefillStatusContract.php // OPTIONAL: refill status
Bulk/
BulkOrderCreateContract.php // OPTIONAL: bulk create
BulkOrderStatusContract.php // OPTIONAL: bulk status
Subscription/
SubscriptionContract.php // OPTIONAL: subscription lifecycle
DripFeed/
DripFeedContract.php // OPTIONAL: dripfeed lifecycle
Infra/
TransportContract.php // HOST-PROVIDED transport interface (Laravel adapter lives in host)
AuthStrategyContract.php // OPTIONAL: driver-specific auth strategy
ErrorNormalizerContract.php // CORE (MUST): normalize provider errors into DgpError
RetryPolicyContract.php // OPTIONAL: retry/backoff decisions
RateLimitPolicyContract.php // OPTIONAL: rate limit hints/windows
IdempotencyContract.php // OPTIONAL: idempotency rules
Discovery/
CapabilitiesContract.php // CORE (MUST): declare supported capabilities
ServiceMapperContract.php // OPTIONAL: host↔provider mapping helper
Ops/
HealthCheckContract.php // CORE (MUST via AbstractServiceDriver): health/ping
AuditTrailContract.php // OPTIONAL: audit hooks
WebhookIngestContract.php // OPTIONAL: webhook parsing/verification
Payloads/
Requests/
Catalog/
ListServicesRequest.php
GetServiceInputSchemaRequest.php
GetServiceSchemaSnapshotRequest.php
GetServiceSchemaForServiceRequest.php
Orders/
CreateOrderRequest.php
GetOrderStatusRequest.php
CancelOrderRequest.php
Balance/
GetBalanceRequest.php
Refill/
CreateRefillRequest.php
GetRefillStatusRequest.php
Bulk/
BulkCreateOrdersRequest.php
BulkGetOrderStatusRequest.php
Subscription/
CreateSubscriptionRequest.php
GetSubscriptionStatusRequest.php
CancelSubscriptionRequest.php
DripFeed/
CreateDripFeedRequest.php
GetDripFeedStatusRequest.php
CancelDripFeedRequest.php
Infra/
HttpRequestDto.php
AuthApplyRequest.php
NormalizeErrorRequest.php
RetryDecisionRequest.php
RateLimitHintRequest.php
MakeIdempotencyKeyRequest.php
Discovery/
GetCapabilitiesRequest.php
ResolveProviderServiceRequest.php
Ops/
HealthCheckRequest.php
AuditRecordRequest.php
ParseWebhookRequest.php
Responses/
Catalog/
ListServicesResponse.php
GetServiceInputSchemaResponse.php
GetServiceSchemaSnapshotResponse.php
GetServiceSchemaForServiceResponse.php
Orders/
CreateOrderResponse.php
GetOrderStatusResponse.php
CancelOrderResponse.php
Balance/
GetBalanceResponse.php
Refill/
CreateRefillResponse.php
GetRefillStatusResponse.php
Bulk/
BulkCreateOrdersResponse.php
BulkGetOrderStatusResponse.php
Subscription/
CreateSubscriptionResponse.php
GetSubscriptionStatusResponse.php
CancelSubscriptionResponse.php
DripFeed/
CreateDripFeedResponse.php
GetDripFeedStatusResponse.php
CancelDripFeedResponse.php
Infra/
HttpResponseDto.php
AuthApplyResponse.php
NormalizeErrorResponse.php
RetryDecisionResponse.php
RateLimitHintResponse.php
MakeIdempotencyKeyResponse.php
Discovery/
GetCapabilitiesResponse.php
ResolveProviderServiceResponse.php
Ops/
HealthCheckResponse.php
AuditRecordResponse.php
ParseWebhookResponse.php
Types/
Money.php
Currency.php
Service/
ServiceDefinition.php // raw baseline service item (strict core + meta bag)
ServiceCategory.php
ServiceTag.php
ServiceInputSchema.php // minimal per-service schema (Lane A helper)
ServiceField.php
ServiceFieldRule.php
Schema/ // TS-parity UI schema (Lane B: ServiceProps)
ServiceProps.php
Tag.php
Field.php
FieldOption.php
ServiceFallback.php
Ui/
UiNode.php
UiString.php
UiNumber.php
UiBoolean.php
UiAnyOf.php
UiArray.php
UiObject.php
Orders/
OrderRef.php
OrderStatus.php
OrderStatusCode.php
Balance/
ProviderBalance.php
Refill/
RefillRef.php
RefillStatus.php
RefillStatusCode.php
Subscription/
SubscriptionRef.php
SubscriptionStatus.php
SubscriptionStatusCode.php
DripFeed/
DripFeedRef.php
DripFeedStatus.php
DripFeedStatusCode.php
Infra/
CapabilityMap.php
ProviderRateLimit.php
ProviderRateLimitWindow.php
IdempotencyKey.php
HttpMethod.php
Ops/
HealthState.php
AuditRecord.php
WebhookEvent.php
WebhookEventType.php
Support/
Result.php // Result<T>: success/failure wrapper
DgpError.php
DgpErrorCode.php
Exceptions/
DgpException.php
TransportException.php
AuthException.php
ProviderException.php
RateLimitedException.php
ValidationException.php
Hydration/
HydratesFromArray.php // trait for DTO ::fromArray(...)
DtoHydrator.php // optional helper for host/controller DTO creation
Validation/
PayloadValidator.php // pre-flight validation + schema verification
Serialization/
Arrayable.php
Normalizes.php
Testing/
FakeTransport.php
FakeClock.php
The flow this SDK expects
- Host loads a plugin (your existing system) → obtains the exported driver class.
- Host resolves a driver instance via
DriverResolver/DriverManager. - Host calls capabilities (catalog/order/status/balance/etc.) via the driver interfaces.
- Driver returns typed DTO responses wrapped in
Result<T>, with normalized errors.
Driver layer (src/Driver)
DriverRegistry
Stores driver registrations:
driver_key→ factory/closure/class string- optional metadata (label, version, category)
Why: central place to list available drivers and ensure uniqueness.
DriverResolver
Creates driver instances from:
- registry entry
- runtime configuration (plugin settings, handler context, etc.)
Why: a single resolution path so the Host never does new directly.
DriverManager
High-level façade used by the Host:
resolve($key)- convenience helpers for “must support X contract”
- safe wrappers for common patterns (error normalization, retry/backoff decisions)
Why: keeps Host code clean and consistent.
AbstractServiceDriver (base class)
An abstract driver with shared core behavior, including Health as a baseline.
Recommended built-ins:
- implements
HealthCheckContract - common helpers:
ok(),fail(),wrapExceptions() - convenience access to handler/config
- shared normalization utilities
Why: eliminates boilerplate and standardizes minimal behavior across drivers.
Contract map (what each does)
Core contracts (MUST for a panel-like provider)
-
ServicesCatalogContract- Raw services list:
ServiceDefinition[]. - Enables discovery/mapping and Host-owned normalization (Lane A).
- Raw services list:
-
OrderCreateContract- Place an order.
-
OrderStatusContract- Fetch order status.
-
BalanceContract- Fetch provider balance.
-
CapabilitiesContract- Declare supported features (cancel/refill/bulk/schema/etc.).
-
ErrorNormalizerContract- Normalize any provider error shape into
DgpError.
- Normalize any provider error shape into
-
HealthCheckContract(provided byAbstractServiceDriver)- Quick health/ping/config sanity.
Schema contracts (optional, but powerful)
-
ServiceInputSchemaContract(optional helper for Lane A)- Returns minimal per-service fields (
ServiceInputSchema). - The Host still owns final UI normalization.
- Returns minimal per-service fields (
-
ServiceSchemaContract(optional/advanced: Lane B fast lane)-
Returns pre-normalized
ServiceProps. -
Must support both shapes:
- Snapshot: provider-wide schema for caching.
- Per-service: focused schema for a single service.
-
Host validates/clamps; can fallback to Lane A if invalid.
-
Common add-ons (optional)
- Cancel:
OrderCancelContract - Refill:
OrderRefillContract,RefillStatusContract - Bulk:
BulkOrderCreateContract,BulkOrderStatusContract - Subscription:
SubscriptionContract - Drip feed:
DripFeedContract
Infra policy hooks (optional)
- Transport:
TransportContract(host-provided interface) - Auth:
AuthStrategyContract - Retry:
RetryPolicyContract - Rate limit:
RateLimitPolicyContract - Idempotency:
IdempotencyContract
Ops hooks (optional)
- Audit:
AuditTrailContract - Webhooks:
WebhookIngestContract
DTOs (Payloads + Types)
Requests (src/Payloads/Requests)
Input DTOs passed by the Host.
Rules:
- explicit fields, no magic arrays
- allow
metaonly when intentionally flexible
Responses (src/Payloads/Responses)
Typed results returned by drivers.
Rules:
- canonical normalized data
- optional
metafor provider-specific extras
Shared Types (src/Types)
Reusable building blocks shared across payloads.
Key split:
Types/Service/*→ raw service list + minimal input schema (Lane A)Types/Schema/*→ TS-parityServicePropsUI schema (Lane B)
Support layer (src/Support)
Result + Errors
A consistent return model:
Result<T>— success/failure wrapperDgpError+DgpErrorCode— canonical error representation
Exceptions
Typed exceptions for internal ergonomics (TransportException, AuthException, etc.). Drivers can throw internally, while DriverManager wraps/normalizes into Result.
Validation / Serialization / Testing
- pre-flight validation helpers
- schema verification helpers (Lane B)
- serialization interfaces
- fakes (
FakeTransport,FakeClock) for unit tests
Implementation plan (what we do next, in order)
Step 1 — Foundation (everything depends on this)
Result<T>+DgpError+DgpErrorCodeDriverContext+DriverManagerboundary wrappingPayloadValidatorfor pre-flight + schema verification
Step 2 — Raw lane MVP (most providers)
ServicesCatalogContract+ServiceDefinitionOrderCreateContract,OrderStatusContract,BalanceContractErrorNormalizerContract,CapabilitiesContract- health check baseline
Step 3 — Optional helpers
ServiceInputSchemaContract+ServiceInputSchema- mapping helpers if needed
Step 4 — Fast lane (advanced)
Types/Schema/*(TS-parityServiceProps)ServiceSchemaContractsnapshot + per-service- strict validation + versioning + clamp rules
Step 5 — JAM add-ons
- cancel, refill, bulk
Step 6 — Remaining optional capabilities
- subscription, dripfeed
- infra hooks (retry/rate-limit/idempotency)
- ops hooks (audit/webhooks)
What this package deliberately does NOT do
- install/enable/disable plugins (your system already does this)
- store plugin settings (your system already does this)
- define host database schema
- enforce UI implementation details (it only defines contracts + typed shapes)