SaaSdemo: a minimal third-party SaaS integration
SaaSdemo demonstrates GoodVision sign-in, subscription, and payment integration for third-party SaaS products. The example provides its own business pages and sign-in session. GoodVision's customer workspace provides plan, subscription, payment, billing, and entitlement management.
SaaSdemo displays currently published free or paid plans with monthly/yearly selection, Credits, seats, FAQs and original order recovery. It queries free activation, payment, allowance and product access separately and keeps no independent balance or eligibility table.
Store upgrade state separately by subscription ID. Check current member access after the old free subscription ends.
Example boundary
SaaSdemo is an independent reference integration for third-party SaaS teams, not a GoodVision internal micro-application, service, or operations demo. It demonstrates published OIDC integration and page functions. Before calling commercial APIs, confirm the application’s SaaS Billing API Product in Developer Console and use its paths, scopes, audiences and event schemas.
Page functions
| Capability | SaaSdemo | GoodVision platform |
|---|---|---|
| Registration, sign-in, sign-out | Provides entry points and a same-origin BFF session | GoodVision's managed identity service owns identity, credentials, and the OIDC session |
| Partner product pages | Renders the third party's own product features | Does not host partner product features |
| Plans, subscriptions, payments, bills | Provides a platform link only | The customer workspace renders and executes commercial operations |
| Applications, API Products, webhooks | Provides a Developer Platform link only | Developer Console owns technical integration |
| API parameters and errors | Follow the current specification | API Product supplies version-specific OpenAPI/AsyncAPI |
The platform manages plans, orders, payments, billing and access. Third-party pages cannot maintain a separate result or simulate these states with localStorage.
Account header and current-user information
The example header provides Developer Console, applications and sign-in controls. Signed-in users see their avatar, name and email, and can read their profile, change language, sign in to another account or sign out. Application links open server-registered independent origins without transferring a session or permissions.
These routes belong to SaaSdemo's own same-origin BFF as implementation examples; they are not new platform business APIs:
| Route | Purpose |
|---|---|
GET /auth/session | Check sign-in and read the current user projection and application CSRF information |
GET /api/me | Recheck the current session and application scope, then read name, email, local user ID, open_id and union_id |
GET /auth/login?switch_account=true | Confirm another account through managed sign-in and return to the original page |
POST /auth/logout | Validate the application's CSRF token and clear the current session |
const response = await fetch('/api/me', {
credentials: 'same-origin',
cache: 'no-store',
headers: { Accept: 'application/json' },
});
if (!response.ok) throw new Error(`Profile unavailable: ${response.status}`);
const { user } = await response.json();The profile route reads only the signed-in user and accepts no user, organization or application overrides. It returns 401 for missing authentication, 403 for denied scope and 503 for an unavailable dependency, without returning stale profile data. Closing the profile dialog or changing users clears its temporary data. Tokens stay on the server; application access and subscriptions remain subject to their platform APIs.
OIDC configuration
SaaSdemo uses user identity permissions with Authorization Code + PKCE (S256) for sign-in. Enable application identity permissions separately only when scheduled jobs or backend APIs are also required. The two purposes use different clients, and Sandbox never shares clients with Production.
| Setting | Example or source |
|---|---|
| Issuer | Current environment’s GOODVISION_IDENTITY_ISSUER |
| Client ID | Client ID shown under User identity permissions in Developer Console |
| Client Secret | None; user identity uses a public client with PKCE |
| Redirect URI | Exact /auth/callback URL on the third-party domain |
| Post-logout URI | Registered third-party product home page |
| Scopes | openid profile email plus the least-privilege scopes approved for user identity |
| Token storage | Server-side BFF session; the browser receives only an HttpOnly cookie |
The BFF validates state, nonce, PKCE, issuer, audience, azp, exp, and iat. Tokens must never appear in browser code, static environment variables, URLs, logs, or analytics. Only the optional application identity has a Client Secret, and it must remain in controlled server-side secret storage.
Get integration settings from Developer Console
Open the target Application and obtain SaaSdemo's server-side settings from the relevant module:
| Developer Console module | SaaSdemo settings |
|---|---|
| OAuth/OIDC configuration | User identity GOODVISION_IDENTITY_ISSUER, GOODVISION_USER_CLIENT_ID, redirect URI, and post-logout URI; no Client Secret |
| Credentials and basic information | Only for optional backend API access: application identity GOODVISION_SERVICE_CLIENT_ID, plus dedicated reveal or rotation of GOODVISION_SERVICE_CLIENT_SECRET |
| APIs and permissions | First select user or application identity, then copy that identity's approved GOODVISION_API_BASE, operation paths, audience, and scopes |
| Events and webhooks | GOODVISION_WEBHOOK_PATH, GOODVISION_WEBHOOK_SECRET_VERSION, and signing-secret delivery state |
Copy API bases, operation paths, audiences, and scopes only from the API access grant approved for the current environment and identity. Bulk copy contains non-sensitive settings only and excludes OAuth Client Secrets, Webhook Secrets, and tokens.
After delivery starts, use the delivery log under Events and Webhooks to inspect event_id, delivery_id, and correlation_id. Deduplicate by event_id, locate a concrete retry by delivery_id, and correlate payment, subscription, and access processing by correlation_id. The log exposes redacted status only and never displays the payload, signing secret, Authorization header, or cookie.
Demo BFF routes
These routes belong to the example project. They are not GoodVision Open APIs:
| Route | Purpose |
|---|---|
GET /auth/login | Generate an OIDC sign-in request |
GET /auth/register | Generate a registration request with prompt=create |
GET /auth/callback | Exchange the authorization code and establish a same-origin session |
GET /auth/session | Return necessary user details; anonymous state also returns 200 |
GET /auth/config | Return redacted Application, OIDC, security, and platform-entry configuration |
POST /auth/logout | Validate CSRF, destroy the session, and return the managed identity service logout URL |
/auth/config may return only clientSecretConfigured: true|false. It must never return the secret value.
Configure subscription and billing links
SaaSdemo reads these entries from server-side runtime configuration:
platform.subscriptionUrl # Subscription and billing in the customer workspace
platform.billingUrl # Paid invoices in the customer workspace
platform.developerConsoleUrl # Developer Console
platform.developerPortalOrigin # Developer documentationSaaSdemo's /app/pricing hosts the platform /console/pricing iframe inside the workspace sidebar for prices, comparisons and FAQs. The sidebar's Pricing item opens this route. /pricing remains a separate host page with the public site header. Both entries share the same pricing iframe; “My subscription” embeds /console/subscription in a dialog, with independent links available. Published scope selectors are application=<slug>, environment=sandbox|production and optional product=<code>; display parameters are embed=1, locale=zh-CN|en-US, view=subscription|billing and theme=light|dark. They select an already authorized scope and cannot change the instance's server-side binding. Invalid or duplicate scope input is refused. The Production enum does not enable Production API access. Do not append:
application_id,organization_id, orproduct_tenant_id;plan_code, amount, currency, provider, or payment status;- an arbitrary
return_url, token, client secret, or webhook secret.
Independent pages use the platform session. Approved embedding may use auth=parent-bff through the host's same-origin BFF; services still verify the current user, organization, application, environment and capability. Both sides verify the exact Origin and original iframe. Tokens, cookies, CSRF secrets and third-party handoff tokens never cross URLs or messages.
- An unauthenticated iframe sends
goodvision.customer-billing.auth_required. - SaaSdemo completes GoodVision sign-in in the top-level window using a fixed
targetOrigin, then refreshes the iframe. - Payments started in an iframe open Stripe Hosted Checkout in the top-level window.
The browser cannot specify Organization, ProductTenant, plan, amount, currency, Provider, Token, Secret or arbitrary return URLs. Published application scope selectors grant no permission.
Minimal implementation flow
- Request an Application for the target environment and enable user identity permissions. Enable application identity permissions only when backend API access is required.
- Implement Authorization Code + PKCE, server-side session storage, and CSRF in the third-party BFF.
- After sign-in, render product pages and the platform pricing host page. New customers first create an organization, submit their customer profile, and complete platform review and independent commercial preparation.
- Keep pricing on a separate page and embed subscription management in a dialog. Also offer independent
platform.subscriptionUrlandplatform.billingUrllinks; useplatform.developerConsoleUrlfor technical configuration. - After returning to the product, query ProductAccess/Entitlement through enabled APIs. Confirm product access before enabling features.
- Implement backend APIs or signed webhook consumption only after the corresponding identity's API access grant is approved.
Webhook receiver reference implementation
SaaSdemo includes the following runnable server implementations and tests:
server/index.mjs: endpoint verification, raw-body receipt, capability and application/environment checks, quick acknowledgement and background access queries;server/integration.mjs: time-window, Nonce, HMAC, Verification Token, optional decryption, closed event Schema,event_iddeduplication, version checks and redacted state records;server/integration.test.mjscovers payment, refund, subscription, fulfillment, product activation, and access-invalidation events, including cross-Application and cross-environment denial;server/access-readback.integration.test.mjs: starts the example process to check signed receipt, application Token,access.reconcile.read, restricted access after query failure and necessary permission updates after success;
Process received events in this order:
- Validate transport security and the current application/environment.
- Persist the event in Inbox and acknowledge quickly.
- Query current state in a background task.
oem.product.activated.v1 updates the product summary. For oem.access_context.changed.v1, restrict local access first, then replace the member permission record atomically after a successful query.
Optional fulfillment integration
fulfillment.webhook currently supports creating a tenant in the third-party product. The platform delivers oem.fulfillment.requested.v1 to an approved Webhook. After activation, return succeeded, failed or unknown through the server-side result endpoint provided in the integration configuration.
This endpoint is for servers and is unavailable to browsers. Products cannot change the event’s Organization, Application, subscription or Edition references.
The receiver must:
- deduplicate by
event_idand handle ordering gaps withresource_version; - submit results using the configured server-side endpoint and
Idempotency-Key; - return only a redacted
product_tenant_idon success, or a stable error code for failure/unknown; - keep timeouts, dependency failures, and unknown outcomes unavailable until the platform retries or an operator compensates them;
- enable protected features after successful activation and confirmed product access.
SaaSdemo provides a reference receiver for this flow. Implement activation for your own product and use the current application’s Profile, Grant and Webhook subscription states to determine available capabilities.
Plans and Credits reference
SaaSdemo's separate pricing host page embeds personal/business plans, monthly/annual billing, pricing cards, benefit comparisons, confirmation and FAQs. Subscription management embeds a separate platform page in a dialog. Integrated applications can use the same integration with authorized URL scope selection. Configure prices, Credits, seats and terms in Commerce → Subscription plans in Developer Console, then approve and publish them. The first plan can start from current product/provider configuration without copying another plan. Free must be explicitly configured; paid-only products need not offer it.
For per-seat plans, ask customers to confirm quantity, price per seat and total. Annual plans still release Credits monthly. Applications with an available model catalog and complete commercial configuration can offer one-time add-ons in the same allowance scope.
Use product and original-order values for validity and conversion. Display balances with the declared Credits ratio and retain actual Token usage and deduction details. Clear results after changing organization, application or environment. After payment returns, query the credited allowance.
See SaaS subscription and payment API for requests and recovery.
Paid flow: subscribe → confirmation dialog → top-level Stripe → trusted payment callback → original order/subscription → monthly Credits → signed fulfillment and access readback. Explicit Free acceptance activates without Stripe. A page return proves neither payment, allowance nor access; failures and unknown outcomes retain original-order recovery.
Acceptance checklist
- Sign-in and registration use the environment client, exact callback URI, and PKCE S256.
- User tokens exist only in the BFF server-side session; user identity has no Client Secret.
- When application identity is enabled, its Client Secret exists only in controlled server-side secret storage.
- Pricing host pages and subscription dialogs reuse platform pages without a second source of plan, Checkout, billing or access facts.
- Platform commercial and Developer Console links come from server configuration, not browser inference.
- The commercial entry contains no unpublished query parameter, handoff token, or arbitrary return URL.
- Use current API product specifications for APIs, scopes, Audience, Webhooks and error codes.
- A payment-page return, successful login, or webhook delivery never directly proves ProductAccess is active.
Continue with Third-party SaaS Billing, Authentication, Copy-ready integration templates, and Integration Entry Points and Permissions.
Customer space in the reference example
The reference BFF consumes Users and customer spaces and uses verified customer scope. Space failures preserve verified login and the original operation. Controlled internal readback and organization adapters are not standard third-party APIs; use only contracts enabled for your environment.