Skip to content

SaaS subscription and payment API ​

The SaaS subscription and payment API provides plan queries, payment creation, and subscription management. For integration with the platform's ready-made pages, see Third-party SaaS billing.

Enable the corresponding API product for the current Application and use its specified environment and version. Production is not open; product registration does not activate it.

1. Prepare permissions ​

Configure API permissions for the current application in Developer Console, then call the API as the customer user. The customer also needs valid organization membership, commercial details and payment authority.

Send the following headers with each request:

text
Authorization: Bearer ${GOODVISION_USER_ACCESS_TOKEN}
X-GV-Organization-ID: ${GOODVISION_CUSTOMER_ORGANIZATION_ID}
X-GV-Application-ID: ${GOODVISION_APPLICATION_ID}
EndpointMinimum Scope
GET /open-platform/v1/productscommerce.catalog.read
POST /open-platform/v1/checkout-sessionscommerce.checkout.create
POST /open-platform/v1/free-activationscommerce.checkout.create
GET /open-platform/v1/operations/{id}commerce.checkout.read
GET /open-platform/v1/subscriptionscommerce.subscription.read
GET /open-platform/v1/subscriptions/{id}commerce.subscription.read
POST /open-platform/v1/subscriptions/{id}:cancelcommerce.subscription.manage
POST /open-platform/v1/subscriptions/{id}:change-plancommerce.subscription.manage
POST /open-platform/v1/subscriptions/{id}:change-pricingcommerce.subscription.manage
GET /open-platform/v1/customer-quotes/{id}commerce.catalog.read
GET /open-platform/v1/customer-quotescommerce.catalog.read
POST /open-platform/v1/customer-quotes/{id}/acceptcommerce.checkout.create
POST /open-platform/v1/customer-quotes/{id}/declinecommerce.checkout.create

2. Read plans and create a checkout ​

Read public prices anonymously ​

GET /open-platform/v1/public/pricing requires no sign-in or API permission request. Supply application, environment and product query parameters together. It returns formally published public plans, prices, terms, Credits, seats, FAQs and purchase_availability in that exact scope. Customer-specific offers are omitted; first-month eligibility is unverified, with no selection_revision. not_enabled or suspended allows viewing prices but prevents purchase. Invalid selectors return422, absent public catalogs404 and unverifiable sources503, with no fallback to another application or stale price. The read-only endpoint accepts no customer organization and cannot replace sign-in, terms confirmation or Checkout creation.

environment=sandbox previews explicitly public current test plans without Production launch approval. Suspension, withdrawal and customer-specific visibility still apply. Production always uses the approved, explicitly published version. Authenticated test purchases verify one application with GET /open-platform/v1/application-listings?application_id=<application-id>&environment=sandbox. The marketplace list without query parameters contains only formally published applications.

Find quotations for an opportunity ​

Sellers can call GET /open-platform/v1/appmarket/quotes?opportunity_id=<opportunity-ID>&limit=20 with operations.opportunity.read.

  • The query includes all quotation statuses and supports search and status filters.
  • Keep the filters and next_cursor unchanged for subsequent pages. Start from the first page after changing the opportunity or filters.
  • A 404 means the opportunity is unavailable in the current scope. GET the quotation detail again to check its source and current state before displaying it.

The platform opportunity's quotation section is for viewing. Make changes through the quotation page or authorized quotation API. After customer acceptance, create the order and payment separately.

View received quotations ​

Customers can open Applications & billing → Received quotations in the customer workspace, using their current sign-in and selected application.

Custom pages can call GET /open-platform/v1/customer-quotes?limit=10 for the current customer organization and application, with at most 10 items per page. Pass next_cursor unchanged. Show errors with a retry action when the query fails. Clear the old cursor and unsubmitted confirmation when changing organizations or applications.

Call GET /open-platform/v1/customer-quotes/{id} to read a sent quotation. The customer must have a valid commercial relationship with the current application. Unsent revisions are unavailable.

To accept or decline, submit the current resource_version, revision, unchanged terms_sha256 and reason with the original Idempotency-Key. Ask the customer to confirm the received version. Sellers cannot decide on their behalf.

Acceptance checks the quotation's current source and expiry. Retired offers or changed terms return 409; read again and ask the customer to confirm. Customers can still decline an unexpired sent quotation after its offer retires. Withdrawn, revised, expired or decided quotations cannot receive a new decision.

Recover unknown outcomes with the original request, then GET the current quotation. A decision saves acceptance or rejection. Orders, payments, subscriptions and product access require separate operations.

sh
curl --request GET \
  --url "${GOODVISION_API_BASE}/open-platform/v1/products" \
  --header "Authorization: Bearer ${GOODVISION_USER_ACCESS_TOKEN}" \
  --header "X-GV-Organization-ID: ${GOODVISION_CUSTOMER_ORGANIZATION_ID}" \
  --header "X-GV-Application-ID: ${GOODVISION_APPLICATION_ID}"

The response contains product and plans. Show the returned plan, price and terms for customer confirmation. The platform calculates the transaction amount.

Create a checkout with a fixed Idempotency-Key, the catalog selection's version and the customer's confirmed content:

json
{
  "plan_code": "<catalog plan code>",
  "plan_version": 3,
  "billing_interval": "month",
  "sales_offer_ref": "<catalog offer reference>",
  "sales_offer_version": 2,
  "selection_revision": "<catalog selection revision>",
  "accepted_terms_sha256": "<accepted terms SHA256>"
}

Use the following request parameters:

ParameterRequiredMeaning
Idempotency-Key🔴 RequiredHeader; keep the original value when recovering
plan_code, plan_version🔴 RequiredSend the selected catalog values
billing_interval🔴 Requiredmonth or year
selection_revision, accepted_terms_sha256🔴 ConditionalRequired when returned by the selected catalog; pass the revision and accepted terms unchanged
sales_offer_ref, sales_offer_version🔴 ConditionalSend together; omit both only when the current API configuration allows it
seat_quantity🔴 ConditionalRequired for seat_pricing, within its min/max; omit for fixed-price plans
return_referenceOptionalConfigured return reference; omit if no application return is requested

The request does not accept arbitrary return URLs, amounts, currencies or payment providers.

For in-app payments, use return_reference: "application-entry". Both success and cancellation return to the application's reviewed published entry. Configure the correct HTTPS address in the application's publication settings first, in the same application environment. The original purchase retains that address; later publication changes preserve its payment destination. Platform workspaces can keep their configured return references.

The return parameters payment, payment_order, organization, application, environment, product and subscription payment_subscription only select the original authorized operation. payment=success does not confirm payment or activation. Read the original operation first. Use bounded automatic reads while awaiting confirmation, with manual recovery after an error or deadline. Do not create another purchase.

When the catalog includes seat_pricing, the price, monthly base Credits and monthly campaign Credits apply per seat. Submit seat_quantity within minimum_seats and maximum_seats. You may show an estimated total; the platform verifies the order amount. Fixed-price plans do not accept a seat multiplier.

Recover unknown outcomes with the original body, quantity and key. Keep the original quantity when recovering an order.

To configure plans, open Commerce → Subscription plans in the current application. Select a prepared product and provider to create a draft, or copy an existing plan. Drafts support monthly or annual prices, Credits, seat limits or fixed capacity, terms, recommendations and FAQs. Prices and plans require separate approval and updated application publication before sale. Saving a draft preserves historical orders.

After receiving 202:

  1. Save operation_id, resource_version and the short-lived checkout_url.
  2. Redirect to checkout_url to complete payment.
  3. Call GET /open-platform/v1/operations/{id} for the original operation's status, next_step and current version.

202 means the request was accepted. Check payment and subscription state after the customer returns before displaying success.

3. Read, cancel and change subscriptions ​

Activate a free plan ​

Use POST /open-platform/v1/free-activations for a zero-price monthly plan. Send the same plan selection body as for a new checkout, without return_reference. Retain the catalog revisions, selection fingerprint and accepted terms.

A successful 201 response contains order_id, subscription_id, status and resource_version. No payment method is required and no checkout session is returned. Repeat requests with the original body and key. Zero-price annual plans are unavailable; paid checkout rejects zero amounts.

Free subscriptions return billing_mode: free. Monthly Credits follow the accepted cycle, and unused Credits expire at the period end. End-of-period cancellation preserves service until the returned deadline.

Upgrade a free subscription ​

Read the current free subscription, then include both upgrade_from_subscription_id and upgrade_from_subscription_version in the new paid checkout. A changed source returns 409; read again and ask the customer to confirm. Recover unknown results with the original source, version, body and key.

The free plan remains active until payment succeeds. The new paid subscription is pending_payment. Resume payment using the new paid subscription and original operation. Cancelling payment preserves free service. Verified payment activates the paid plan and stops the remaining free Credits.

Check subscription, payment, allowance and product access separately. A return redirect only indicates that the customer returned to the page.

Read subscriptions ​

Lists return items; details return subscription information for the current customer and application. Save the subscription ID and resource_version. Use commercial subscription IDs and API access grant IDs for their respective purposes.

Cancel or change a plan ​

Send the original operation's Idempotency-Key and set If-Match: "<resource_version>" using the version just read.

  • Cancel: send {"cancel_mode":"end_of_period","reason":"Customer stops renewal"}. Use immediate for immediate cancellation.
  • Change: send {"new_plan_code":"<catalog plan code>","new_plan_version":3}. Provide optional effective_at and reason as specified by the current API.

The platform determines prices. Refunds require a separate operation after cancellation.

A 202 response means the request was accepted. Read the original subscription again and update state using subscribed lifecycle events. Use the current response and SaaS billing contract for expiry, effective dates and access.

4. Errors and recovery ​

Status or conditionAction
401/403Check the current user, organization, application, scopes and customer authority
409Read the version, catalog and terms again; ask the customer to confirm changed conditions
400/422Correct parameters according to the API specification
503 or a lost responseQuery the original operation or subscription, then recover with its original body and key

Keep the original operation and key when payment is uncertain to avoid a second payment. Recovery retains the originally confirmed terms.

Product, payment, commercial subscription and access are separate states. See Copy-ready templates and Idempotency and retries.

5. Configure subscription plans and Credits ​

Enable free activation ​

free_activation_enabled controls new free activations for the current application, product and environment. It defaults to off for the first plan.

ConfigurationBehavior
Omitted when copyingInherit the source setting
Explicit falseDisable new free activations
nullReject the request

Only zero-price monthly plans can enable this option. Approval and publication make them available in the customer catalog. Disabling acquisition preserves existing subscriptions, original order recovery and accepted allowances.

Configure welcome, daily activity and campaign Credits separately. A quantity of 0 disables each benefit. Paid plans can also offer these benefits.

Open Transactions → Subscription plans in the current application. Create a draft from an existing commercial product source, with an independent USD monthly or yearly price, audience, recommendation, total member capacity, plain-text sales terms and ordered FAQs. The price and plan require review and publication before sale. This does not change the source offer or historical orders. The initial product source requires commercial onboarding.

Configure Credits ​

Specify monthly allowance, campaign dates and weighted consumption units per Credit. Annual plans also release Credits monthly, with each month's allowance expiring at its period end. Add-ons retain the amount, model rates and expiry accepted at purchase.

Subscription and add-on balances can share a Credits display when their scope and conversion ratio match. Retain raw Token usage and weighted deductions. Historical allowances without a declared ratio, or with mixed ratios, keep their original weighted units. The browser cannot rename balances or merge different products.

Configure a first-month offer ​

First-month offers default to off. Set introductory_pricing: {"first_month_amount_scaled":700} on a draft for a $7 first month. The independent price defines the regular monthly fee, for example $10.

Only paid, fixed-price monthly USD plans support this option. The first price must be positive and lower than the regular price. Annual, free and per-seat plans are unavailable. Omit the field when copying to inherit it; explicit null disables it on the new draft. Existing orders retain their accepted conditions.

The customer catalog's first_month_offer returns the discounted amount and current eligibility:

StateDisplay and action
eligibleShow the first-month price
not_eligible, unverifiedShow the regular price
pendingShow the regular price and prompt the user to resume the unfinished discounted payment in its original workspace

Each verified person can use the offer once within the current application, actual product and environment. Previous trials or payments exclude eligibility. Cancellation, resubscription and refunds do not restore it.

Show the first payment and the regular price from month two separately. Return the original selection_revision; the platform verifies price and eligibility. After 409, read the catalog and confirm again.

Failed or unknown payments retain the original order and key. Only a platform-verified terminal payment cancellation releases an unfinished offer. Display the original order's recurring_amount_scaled as the monthly price accepted for month two onward.

Plan settings also have these requirements:

  • Free plans use monthly periods and may have a zero price. Total member capacity and per-seat pricing cannot be combined.
  • Welcome and daily activity Credits require configured eligibility checks and issuance capabilities. Policy fields do not issue Credits directly.
  • A full refund can stop remaining subscription allowances. Use the current integration configuration and guidance for automatic renewal cancellation, complete refunds and access policies.

For an unknown result, recover the original request and verify the returned price, capacity, terms and FAQs before updating the UI. Clear unsaved input when changing applications or environments. Customer checkout and cancellation still require current user authorization and allowed actions; developer management access does not grant payment authority.

Personal, business and SaaSdemo reference plans ​

The console provides five editable templates:

TemplatePriceMonthly base CreditsMembers
Free$0/month1001
Pro monthly$7 first month, then $10/month10001
Pro yearly$96/year, equivalent to $8/month10001
Team monthly$40/seat/month1000/seatChoose 2–100 seats; configure campaign Credits separately
Team yearly$480/seat/year1000/seatChoose 2–100 seats; configure campaign Credits separately

Templates fill new drafts; saving, approval and publication are separate steps. You can remove Free or change names, prices, seats and Credits. Only SaaSdemo's first creation form selects Free by default. Other applications keep free acquisition off.

Before saving, provide eligible model references and rates. Complete missing references; sample models do not grant authorization. Welcome, activity and campaign benefits default to off and require configured issuance capabilities. Annual plans release Credits monthly. Business payments and Credits scale with the purchased seat quantity.

To prepare a demo catalog:

  1. Choose Prepare plan suite beside the commercial source. SaaSdemo includes Free by default; other applications include four paid plans. Adjust Include Free in the plan suite as needed.
  2. Review and edit each plan's price, Credits, member capacity, terms and FAQs.
  3. Choose Save this plan and continue. Verified models and rates carry into the next form; configure benefits separately for each plan.

Each plan uses a separate request and idempotency key. Unknown results or responses inconsistent with the request lock the current plan. Choose Retry original request and confirm its result before submitting the next plan.

Skip this plan creates no resource. Finish setup later retains saved plans; unsaved forms are not persisted. Preparing again reuses only plans whose code, product and provider exactly match the current directory. Check duplicate names or inconsistent commercial sources first. Manage custom plan codes individually in the directory.

Follow the Approval and publication checklist to approve each price and plan and publish the plans, then select their current revisions for the same product and environment in Application publication. Customer pages read only the published catalog. Suite preparation does not automatically approve, publish or activate customers. Changing templates explicitly clears incompatible first-month, fixed-capacity or per-seat policies. Existing orders and subscriptions retain their accepted terms.

Publish plans before configuring the product address ​

In Application publication, select approved and published plans for the current application and environment. If the product address is not ready, explicitly clear it and save with at least one valid plan selected. This publishes the public catalog without activating payment or product access. Non-empty addresses still require HTTPS publication validation. Configure the correct product address later and verify actual entry separately. A publication without an address cannot be used for redirects or external billing. Prices remain visible when subscription purchase is not enabled or is suspended, with purchasing unavailable.

Independent pricing page and URL selectors ​

Build an independent URL from the issued integration configuration:

ts
const url = new URL('/console/pricing', configuration.platform.consoleOrigin);
url.searchParams.set('application', configuration.application.slug);
url.searchParams.set('environment', configuration.application.environment);
url.searchParams.set('locale', 'en-US');
// Anonymous pricing requires the configured product code.
url.searchParams.set('product', configuredProductCode);

application is the application slug, environment is sandbox or production, product is required for anonymous pricing, and locale supports zh-CN or en-US. SaaSdemo also exposes /pricing, whose selectors must match its configured application binding. The page has no workspace sidebar and provides personal/business selection, monthly/yearly billing, comparison, FAQ and subscription management. Yearly comparison retains the monthly Free option.

The independent pricing page requires no sign-in and displays only formally published public plans in this exact scope. Customer-specific quotations remain private. Viewing prices creates no organization, order or subscription. Selecting a plan or My subscription starts sign-in and preserves the application, environment, product and selected plan. Purchase confirmation revalidates eligibility and full terms, with no automatic submission. Anonymous first-month offers state their conditions; eligibility is checked after sign-in. Selectors are not credentials. Invalid or unauthorized selectors cannot fall back to another application. Embedding retains embed=1&view=subscription&auth=parent-bff; language and theme follow the host and content height adjusts automatically. Prices, promotional eligibility and access are confirmed through the current catalog and purchase flow, never supplied as URL authority.

This entry supports every Open Platform application with completed commercial integration and published plans. It has no application-name allowlist. SaaSdemo embeds pricing in an iframe and provides Open pricing page; both retain the same application, environment and product. Pricing remains separate from current subscription and payment settings. After sign-in, My subscription opens a management dialog; /console/subscription provides the full management page in the same scope. Each application explicitly configures and publishes its free strategy; paid-only applications show no Free option.

The same SaaSdemo build supports different application instances. Each instance derives its application and environment from the issued configuration; visitor parameters cannot change that binding. Navigation between subscriptions, billing and pricing retains the selected product. Invalid or duplicate selectors stop loading and disable subscription management. The standalone platform pricing page can show other applications formally published public plans; purchasing checks the current user’s authorization.

For embedding, reuse the standalone URL and add presentation and host authentication parameters, retaining the application's same-origin bridge:

ts
const embedded = new URL(url);
embedded.searchParams.set('embed', '1');
embedded.searchParams.set('view', 'subscription');
embedded.searchParams.set('auth', 'parent-bff');
iframe.src = embedded.toString();
standaloneLink.href = url.toString();
standaloneLink.target = '_blank';
standaloneLink.rel = 'noreferrer';

auth=parent-bff requires the integrator's authenticated host bridge, using the current application session and permitted subscription operations. Setting an iframe source alone does not establish sign-in or a commercial relationship. The host validates the exact message origin, current iframe window and fields; its server validates the session, CSRF and current authorization. Credentials must never enter URLs or cross-window messages. New customers without a payer and commercial details must complete commercial preparation first; another customer's successful subscription cannot establish that relationship.

Configure one-time Credits add-ons ​

Open Commerce → Credits add-ons in the current Application. Select an available model product and prepare an independent USD price and package draft. The reference values are 500 Credits, $15, one calendar month and no refunds; adjust them to your product policy. Configure Credits conversion, supported models and rates, days or calendar months, eligible plan codes, purchase terms and refunds. Standalone purchases require no subscription rule. Enabling free plan eligibility is a separate explicit choice and does not enable free subscription operations.

The price and add-on are saved together as drafts. Approve the price, then separately approve and publish the add-on before customer purchase. Drafts create no customer orders or allowance; historical orders retain their accepted versions.

An unknown result, or a response inconsistent with the requested price, expiry, eligibility or refund policy, locks the original content and key. Recover the same request. Clear temporary state when changing organization, application, environment or permissions.

See the Token API guide for purchase eligibility, order recovery and expiry. After checking payment, verify allowance arrival separately.

Developer documentation. Internal operations access is not required.