Skip to content

feat: customer portal sessions + subscription cancellationReason + canceled_for_nonpayment webhook - #59

Merged
sandervanhooft merged 1 commit into
mainfrom
sync/portal-sessions-cancellation-reason
Sep 2, 2026
Merged

feat: customer portal sessions + subscription cancellationReason + canceled_for_nonpayment webhook#59
sandervanhooft merged 1 commit into
mainfrom
sync/portal-sessions-cancellation-reason

Conversation

@sandervanhooft

Copy link
Copy Markdown
Member

Summary

Adopts three additions from the latest vatlify OpenAPI bundle (re-vendored into
openapi.yaml). Spec treated as source of truth.

1. Customer portal sessions — POST /v1/customers/{customerId}/portal-sessions

  • customers->createPortalSession($id, [...]) creates a short-lived, single-use
    hosted portal link. Returns a new Vatly\API\Types\PortalSession (url,
    expiresAt, returnUrl).
  • Request body is optional; returnUrl is a nullable absolute HTTPS URL
    (max 2048 bytes). The link is credential-bearing — the SDK never caches it.

2. Subscription cancellationReason

  • New nullable field on the Subscription resource: payment_failure /
    merchant_request / customer_request / null (now in the schema's required
    set), hydrated like any scalar.
  • New Vatly\API\Types\CancellationReason constant class (matches the
    TaxBehavior / UpdateStatus convention).

3. subscription.canceled_for_nonpayment webhook event

  • New WebhookEventName::SUBSCRIPTION_CANCELED_FOR_NONPAYMENT constant.
  • New typed DTO Vatly\API\Webhooks\Events\SubscriptionCanceledForNonpayment
    following the existing SubscriptionCanceledImmediately pattern — exposes
    customerId, subscriptionId, endsAt, testmode, plus cancellationReason
    (payment_failure). Wired into WebhookEventFactory and reported by
    getSupportedEvents() / isSupported().

Tests

  • CustomersEndpointTest: portal session create (with returnUrl, and
    body-less), asserting method/URL/body and the hydrated PortalSession.
  • SubscriptionEndpointTest: cancellationReason hydration (value + null).
  • WebhookEventFactoryTest + SubscriptionEventsTest: the new event maps to the
    typed DTO, carries the reason, defaults it when absent, and appears in the
    supported/isSupported lists; plus the event-name constant.
  • New PortalSessionTest (array + stdClass hydration, null/absent returnUrl).

Docs updated (Customers.md, Subscriptions.md, Webhooks.md) and CHANGELOG.md.

Verification

  • composer test273 tests, 1364 assertions, OK.
  • php -d memory_limit=1G vendor/bin/phpstan analyse src tests → No errors (level 5).
  • vendor/bin/php-cs-fixer fix --allow-risky=yes --dry-run → 0 files to fix.

Over to you to merge once CI is green.

…nonpayment webhook

Adopts three additions from the latest vatlify OpenAPI bundle (re-vendored).

1. Customer portal sessions — POST /v1/customers/{customerId}/portal-sessions.
   - customers->createPortalSession($id, [...]) returns a new
     Vatly\API\Types\PortalSession (url, expiresAt, returnUrl). Body optional
     (returnUrl: absolute HTTPS, max 2048 bytes). Credential-bearing link — not
     cached by the SDK.

2. Subscription->cancellationReason — nullable string
   (payment_failure | merchant_request | customer_request | null), now in the
   schema's required set. Added to the resource + a Vatly\API\Types\CancellationReason
   constant class (matches TaxBehavior/UpdateStatus).

3. subscription.canceled_for_nonpayment webhook — payment recovery exhausted.
   New WebhookEventName::SUBSCRIPTION_CANCELED_FOR_NONPAYMENT constant and typed
   DTO SubscriptionCanceledForNonpayment (follows SubscriptionCanceledImmediately;
   also exposes cancellationReason), wired into WebhookEventFactory and
   getSupportedEvents()/isSupported().

Re-vendored openapi.yaml from vatlify main. Tests added for all three (endpoint,
webhook factory + DTO, resource hydration, and PortalSession/type units). Docs
updated (Customers, Subscriptions, Webhooks) + CHANGELOG.

273 tests green; phpstan (level 5) and php-cs-fixer clean.
@sandervanhooft
sandervanhooft merged commit 82169e1 into main Sep 2, 2026
12 checks passed
@sandervanhooft
sandervanhooft deleted the sync/portal-sessions-cancellation-reason branch September 2, 2026 05:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant