mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
Add the License-panel Pro trial front door (pull-only)
Completes the approved trial flow end to end. The License panel's plan comparison gains a 'Start 14-day free Pro trial' action (shown only when unlicensed, trial-eligible, and never-trialed) that rides the existing purchase-start handoff with trial=1; the handler already forwards non-reserved params. Pulse Account portal reads the trial arrival flag, relabels the pro monthly checkout button, shows the card-required note, and sends trial:true to the checkout session endpoint (server support deployed in pulse-pro 4c3a28c). Organic portal and public pricing arrivals see no trial anywhere, per the no-upsell doctrine; retired local trial-acquisition routes remain 404. Portal dist rebuilt.
This commit is contained in:
parent
34f64e68fc
commit
b0792890b0
13 changed files with 229 additions and 22 deletions
|
|
@ -79,6 +79,11 @@ interface ProLicensePlanSectionProps {
|
|||
label: string;
|
||||
destination: UpgradeDestination;
|
||||
} | null;
|
||||
trialAction: {
|
||||
label: string;
|
||||
note: string;
|
||||
destination: UpgradeDestination;
|
||||
} | null;
|
||||
};
|
||||
planSelectionPrompt: ActionNotice | null;
|
||||
planStatus: {
|
||||
|
|
@ -373,17 +378,34 @@ export const ProLicensePlanSection: Component<ProLicensePlanSectionProps> = (pro
|
|||
)}
|
||||
</For>
|
||||
</div>
|
||||
<Show when={props.planComparisonSummary.action}>
|
||||
{(action) => (
|
||||
<UpgradeButtonLink
|
||||
variant="outline"
|
||||
size="settingsActionXs"
|
||||
class="mt-4 gap-1"
|
||||
destination={action().destination}
|
||||
>
|
||||
{action().label}
|
||||
</UpgradeButtonLink>
|
||||
)}
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<Show when={props.planComparisonSummary.trialAction}>
|
||||
{(trialAction) => (
|
||||
<UpgradeButtonLink
|
||||
variant="primary"
|
||||
size="settingsActionXs"
|
||||
class="mt-4 gap-1"
|
||||
destination={trialAction().destination}
|
||||
>
|
||||
{trialAction().label}
|
||||
</UpgradeButtonLink>
|
||||
)}
|
||||
</Show>
|
||||
<Show when={props.planComparisonSummary.action}>
|
||||
{(action) => (
|
||||
<UpgradeButtonLink
|
||||
variant="outline"
|
||||
size="settingsActionXs"
|
||||
class="mt-4 gap-1"
|
||||
destination={action().destination}
|
||||
>
|
||||
{action().label}
|
||||
</UpgradeButtonLink>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={props.planComparisonSummary.trialAction}>
|
||||
{(trialAction) => <p class="mt-2 text-xs text-muted">{trialAction().note}</p>}
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
import { describe, expect, it, afterEach } from 'vitest';
|
||||
import { cleanup, render, screen } from '@solidjs/testing-library';
|
||||
import { Route, Router } from '@solidjs/router';
|
||||
import type { Component } from 'solid-js';
|
||||
import { ProLicensePlanSection } from '../ProLicensePlanSection';
|
||||
import { SELF_HOSTED_PRO_BILLING_PRESENTATION } from '../selfHostedBillingPresentation';
|
||||
import { resolveUpgradeDestination } from '@/utils/upgradeNavigation';
|
||||
|
||||
const baseProps = () => ({
|
||||
activationSuccessSummary: null,
|
||||
commercialMigrationNotice: null,
|
||||
commercialPlanModel: { summary: [], details: [] },
|
||||
currentPlanSummary: {
|
||||
title: 'Community',
|
||||
body: 'Core monitoring',
|
||||
badgeClass: '',
|
||||
statusLabel: 'Free',
|
||||
unlockedFeaturesLabel: 'Included',
|
||||
unlockedFeatures: [],
|
||||
includedExtras: [],
|
||||
supplementalBadges: [],
|
||||
},
|
||||
entitlements: null,
|
||||
grandfatheredPriceNotice: null,
|
||||
hasLicenseDetails: false,
|
||||
loading: false,
|
||||
onReload: () => {},
|
||||
planSelectionPrompt: null,
|
||||
planStatus: null,
|
||||
purchaseActivationNotice: null,
|
||||
purchaseActivationAction: null,
|
||||
});
|
||||
|
||||
const renderInRouter = (component: Component) => {
|
||||
render(() => (
|
||||
<Router>
|
||||
<Route path="/" component={component} />
|
||||
</Router>
|
||||
));
|
||||
};
|
||||
|
||||
describe('ProLicensePlanSection trial action', () => {
|
||||
afterEach(() => cleanup());
|
||||
|
||||
it('renders the trial button with its card-required note and trial link', () => {
|
||||
renderInRouter(() => (
|
||||
<ProLicensePlanSection
|
||||
{...baseProps()}
|
||||
planComparisonSummary={{
|
||||
cards: [{ title: 'Pro', body: 'Everything', highlights: [] }],
|
||||
action: {
|
||||
label: SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonActionLabel,
|
||||
destination: resolveUpgradeDestination(
|
||||
'/auth/license-purchase-start?feature=self_hosted_plan',
|
||||
),
|
||||
},
|
||||
trialAction: {
|
||||
label: SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonTrialActionLabel,
|
||||
note: SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonTrialActionNote,
|
||||
destination: resolveUpgradeDestination(
|
||||
'/auth/license-purchase-start?trial=1&feature=self_hosted_plan',
|
||||
),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
const trialLink = screen.getByRole('link', {
|
||||
name: SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonTrialActionLabel,
|
||||
});
|
||||
expect(trialLink.getAttribute('href')).toContain('trial=1');
|
||||
expect(trialLink.getAttribute('href')).toContain('feature=self_hosted_plan');
|
||||
expect(
|
||||
screen.getByText(SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonTrialActionNote),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders no trial button when trialAction is null', () => {
|
||||
renderInRouter(() => (
|
||||
<ProLicensePlanSection
|
||||
{...baseProps()}
|
||||
planComparisonSummary={{
|
||||
cards: [{ title: 'Pro', body: 'Everything', highlights: [] }],
|
||||
action: {
|
||||
label: SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonActionLabel,
|
||||
destination: resolveUpgradeDestination(
|
||||
'/auth/license-purchase-start?feature=self_hosted_plan',
|
||||
),
|
||||
},
|
||||
trialAction: null,
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
expect(
|
||||
screen.queryByText(SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonTrialActionLabel),
|
||||
).toBeNull();
|
||||
expect(
|
||||
screen.queryByText(SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonTrialActionNote),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
@ -12,6 +12,8 @@ export interface SelfHostedProBillingPresentation {
|
|||
planSectionDescription: string;
|
||||
planComparisonSectionTitle: string;
|
||||
planComparisonActionLabel: string;
|
||||
planComparisonTrialActionLabel: string;
|
||||
planComparisonTrialActionNote: string;
|
||||
usageSectionTitle: string;
|
||||
hiddenShellTitle: string;
|
||||
hiddenShellDescription: string;
|
||||
|
|
@ -46,6 +48,9 @@ export const SELF_HOSTED_PRO_BILLING_PRESENTATION: SelfHostedProBillingPresentat
|
|||
planSectionDescription: 'Current tier and enabled capabilities.',
|
||||
planComparisonSectionTitle: 'Available plans',
|
||||
planComparisonActionLabel: 'View plans',
|
||||
planComparisonTrialActionLabel: 'Start 14-day free Pro trial',
|
||||
planComparisonTrialActionNote:
|
||||
'Card required. You will not be charged if you cancel during the trial.',
|
||||
usageSectionTitle: 'Usage',
|
||||
hiddenShellTitle: 'Demo mode',
|
||||
hiddenShellDescription: 'Commercial settings are hidden for this session.',
|
||||
|
|
|
|||
|
|
@ -394,21 +394,43 @@ export function useProLicensePanelState() {
|
|||
};
|
||||
});
|
||||
const planStatus = createMemo(() => getSelfHostedPlanStatusPresentation(entitlements()));
|
||||
const trialActionEligible = createMemo(() => {
|
||||
const current = entitlements();
|
||||
const state = subscriptionState();
|
||||
if (state === 'active' || state === 'trial') return false;
|
||||
if (current?.trial_expires_at) return false;
|
||||
if (current?.trial_eligible === false) return false;
|
||||
const tier = current?.tier;
|
||||
return !tier || tier === 'free';
|
||||
});
|
||||
|
||||
const planComparisonSummary = createMemo(() => {
|
||||
if (!showPlanSelectionPrompt()) {
|
||||
return { cards: [], action: null };
|
||||
return { cards: [], action: null, trialAction: null };
|
||||
}
|
||||
const comparison = getSelfHostedPlanComparisonPresentation({
|
||||
entitlements: entitlements(),
|
||||
});
|
||||
const showActions =
|
||||
comparison.cards.length > 0 && purchaseActivationResult().trim().length === 0;
|
||||
return {
|
||||
...comparison,
|
||||
action:
|
||||
comparison.cards.length > 0 && purchaseActivationResult().trim().length === 0
|
||||
action: showActions
|
||||
? {
|
||||
label: SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonActionLabel,
|
||||
destination: resolveSelfHostedPurchaseStartDestination(
|
||||
SELF_HOSTED_PRO_BILLING_PLAN_SELECTION_INTENT,
|
||||
),
|
||||
}
|
||||
: null,
|
||||
trialAction:
|
||||
showActions && trialActionEligible()
|
||||
? {
|
||||
label: SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonActionLabel,
|
||||
label: SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonTrialActionLabel,
|
||||
note: SELF_HOSTED_PRO_BILLING_PRESENTATION.planComparisonTrialActionNote,
|
||||
destination: resolveSelfHostedPurchaseStartDestination(
|
||||
SELF_HOSTED_PRO_BILLING_PLAN_SELECTION_INTENT,
|
||||
new URLSearchParams({ trial: '1' }),
|
||||
),
|
||||
}
|
||||
: null,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"source_hash": "ece2119b2e5fc7be4f49d6b42ab4a214497afac1670c5cd14486678ee54b78e8",
|
||||
"source_hash": "90e5d2ec651ba11669d98c23aa107ea7dc8e07614097c0c655b3e75ded720d17",
|
||||
"build_inputs": [
|
||||
"package.json",
|
||||
"tsconfig.json",
|
||||
|
|
|
|||
24
internal/cloudcp/portal/dist/portal_app.js
vendored
24
internal/cloudcp/portal/dist/portal_app.js
vendored
|
|
@ -1185,6 +1185,7 @@
|
|||
return {
|
||||
openBillingPanelID: "",
|
||||
upgradeFeatureKey: "",
|
||||
upgradeTrialRequested: false,
|
||||
upgradePortalHandoffID: "",
|
||||
upgradePortalHandoff: createQueryState(null),
|
||||
upgradePricing: createQueryState(null),
|
||||
|
|
@ -1812,8 +1813,11 @@
|
|||
return '<article class="billing-upgrade-plan-card' + (plan.highlight ? " highlight" : "") + '">' + (plan.badge ? '<div class="billing-upgrade-plan-badge">' + escapeText(plan.badge) + "</div>" : "") + '<div class="billing-upgrade-plan-header"><div class="billing-upgrade-plan-kicker">' + escapeText(plan.tierKicker) + "</div><h4>" + escapeText(plan.title) + '</h4><div class="billing-upgrade-plan-price">' + escapeText(plan.price) + '</div><div class="billing-upgrade-plan-period">' + escapeText(plan.period) + '</div></div><p class="billing-upgrade-plan-blurb">' + escapeText(plan.blurb) + '</p><ul class="billing-upgrade-plan-features">' + plan.features.map(function(feature) {
|
||||
return '<li class="billing-upgrade-plan-feature tone-' + escapeAttribute(feature.tone) + '"><span class="billing-upgrade-plan-feature-copy">' + String(feature.html || "") + "</span></li>";
|
||||
}).join("") + "</ul>" + (plan.note ? '<div class="helper-text">' + escapeText(plan.note) + "</div>" : "") + '<div class="form-actions">' + checkoutButtons.map(function(button) {
|
||||
return '<button type="button" class="' + escapeAttribute(button.className || "btn-primary") + '" data-account-billing-action="upgrade-start-checkout" data-upgrade-plan-key="' + escapeAttribute(button.planKey || "") + '" data-upgrade-tier="' + escapeAttribute(button.tier || "") + '" data-upgrade-billing-cycle="' + escapeAttribute(button.billingCycle || "") + '"' + (checkoutDisabled ? " disabled" : "") + ">" + escapeText(button.label) + "</button>";
|
||||
}).join("") + "</div></article>";
|
||||
var isTrialButton = billingState.upgradeTrialRequested === true && button.tier === "pro" && button.billingCycle === "monthly";
|
||||
return '<button type="button" class="' + escapeAttribute(button.className || "btn-primary") + '" data-account-billing-action="upgrade-start-checkout" data-upgrade-plan-key="' + escapeAttribute(button.planKey || "") + '" data-upgrade-tier="' + escapeAttribute(button.tier || "") + '" data-upgrade-billing-cycle="' + escapeAttribute(button.billingCycle || "") + '"' + (checkoutDisabled ? " disabled" : "") + ">" + escapeText(isTrialButton ? "Start 14-day free trial" : button.label) + "</button>";
|
||||
}).join("") + "</div>" + (billingState.upgradeTrialRequested === true && checkoutButtons.some(function(button) {
|
||||
return button.tier === "pro" && button.billingCycle === "monthly";
|
||||
}) ? '<div class="helper-text">Card required. You will not be charged if you cancel during the 14-day trial.</div>' : "") + "</article>";
|
||||
}).join("") + "</div>";
|
||||
}
|
||||
function renderUpgradePanel(billingState, _bootstrap) {
|
||||
|
|
@ -2227,11 +2231,13 @@
|
|||
beginMutationState(nextBillingState.upgradeCheckout);
|
||||
});
|
||||
try {
|
||||
var trialRequested = billingState.upgradeTrialRequested === true && tier === "pro" && billingCycle === "monthly";
|
||||
var data = await api.postCommercialJSON("/v1/checkout/session", {
|
||||
plan_key: planKey,
|
||||
tier,
|
||||
billing_cycle: billingCycle,
|
||||
portal_handoff_id: portalHandoffID
|
||||
portal_handoff_id: portalHandoffID,
|
||||
...trialRequested ? { trial: true } : {}
|
||||
});
|
||||
if (!data || !data.url) {
|
||||
throw new Error("Checkout URL was not returned.");
|
||||
|
|
@ -3590,6 +3596,10 @@
|
|||
function normalizeUpgradeFeatureKey2(value) {
|
||||
return String(value || "").trim();
|
||||
}
|
||||
function normalizeUpgradeTrialRequested(value) {
|
||||
var trimmed = String(value || "").trim().toLowerCase();
|
||||
return trimmed === "1" || trimmed === "true";
|
||||
}
|
||||
function readPortalRuntimeHandoff(locationHref = window.location.href) {
|
||||
try {
|
||||
var params = new URL(locationHref).searchParams;
|
||||
|
|
@ -3602,14 +3612,16 @@
|
|||
email: normalizeHandoffEmail(params.get("email")),
|
||||
openBillingPanelID,
|
||||
upgradePortalHandoffID,
|
||||
upgradeFeatureKey: normalizeUpgradeFeatureKey2(params.get("feature"))
|
||||
upgradeFeatureKey: normalizeUpgradeFeatureKey2(params.get("feature")),
|
||||
upgradeTrialRequested: normalizeUpgradeTrialRequested(params.get("trial"))
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
email: "",
|
||||
openBillingPanelID: "",
|
||||
upgradePortalHandoffID: "",
|
||||
upgradeFeatureKey: ""
|
||||
upgradeFeatureKey: "",
|
||||
upgradeTrialRequested: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -3650,12 +3662,14 @@
|
|||
billingState.openBillingPanelID = handoff.openBillingPanelID;
|
||||
billingState.upgradePortalHandoffID = handoff.upgradePortalHandoffID;
|
||||
billingState.upgradeFeatureKey = handoff.upgradeFeatureKey;
|
||||
billingState.upgradeTrialRequested = handoff.upgradeTrialRequested;
|
||||
}, { notify: false });
|
||||
} else if (handoff.upgradeFeatureKey || handoff.upgradePortalHandoffID) {
|
||||
store.setActiveShellSection("billing");
|
||||
store.updateBillingState(function(billingState) {
|
||||
billingState.upgradePortalHandoffID = handoff.upgradePortalHandoffID;
|
||||
billingState.upgradeFeatureKey = handoff.upgradeFeatureKey;
|
||||
billingState.upgradeTrialRequested = handoff.upgradeTrialRequested;
|
||||
}, { notify: false });
|
||||
}
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ describe('portal app', function() {
|
|||
openBillingPanelID: 'retrieve-billing-panel',
|
||||
upgradePortalHandoffID: '',
|
||||
upgradeFeatureKey: '',
|
||||
upgradeTrialRequested: false,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -248,11 +248,15 @@ export function installBillingRuntime(deps: BillingRuntimeDeps): void {
|
|||
beginMutationState(nextBillingState.upgradeCheckout);
|
||||
});
|
||||
try {
|
||||
var trialRequested = billingState.upgradeTrialRequested === true &&
|
||||
tier === 'pro' &&
|
||||
billingCycle === 'monthly';
|
||||
var data = await api.postCommercialJSON<PortalCheckoutSessionCreateResponse>('/v1/checkout/session', {
|
||||
plan_key: planKey,
|
||||
tier: tier,
|
||||
billing_cycle: billingCycle,
|
||||
portal_handoff_id: portalHandoffID,
|
||||
...(trialRequested ? { trial: true } : {}),
|
||||
});
|
||||
if (!data || !data.url) {
|
||||
throw new Error('Checkout URL was not returned.');
|
||||
|
|
|
|||
|
|
@ -122,6 +122,9 @@ function renderUpgradePlansHTML(billingState: PortalBillingState): string {
|
|||
}).join('') + '</ul>' +
|
||||
(plan.note ? '<div class="helper-text">' + escapeText(plan.note) + '</div>' : '') +
|
||||
'<div class="form-actions">' + checkoutButtons.map(function(button) {
|
||||
var isTrialButton = billingState.upgradeTrialRequested === true &&
|
||||
button.tier === 'pro' &&
|
||||
button.billingCycle === 'monthly';
|
||||
return (
|
||||
'<button type="button" class="' + escapeAttribute(button.className || 'btn-primary') + '"' +
|
||||
' data-account-billing-action="upgrade-start-checkout"' +
|
||||
|
|
@ -130,10 +133,15 @@ function renderUpgradePlansHTML(billingState: PortalBillingState): string {
|
|||
' data-upgrade-billing-cycle="' + escapeAttribute(button.billingCycle || '') + '"' +
|
||||
(checkoutDisabled ? ' disabled' : '') +
|
||||
'>' +
|
||||
escapeText(button.label) +
|
||||
escapeText(isTrialButton ? 'Start 14-day free trial' : button.label) +
|
||||
'</button>'
|
||||
);
|
||||
}).join('') + '</div>' +
|
||||
(billingState.upgradeTrialRequested === true && checkoutButtons.some(function(button) {
|
||||
return button.tier === 'pro' && button.billingCycle === 'monthly';
|
||||
})
|
||||
? '<div class="helper-text">Card required. You will not be charged if you cancel during the 14-day trial.</div>'
|
||||
: '') +
|
||||
'</article>'
|
||||
);
|
||||
}).join('') + '</div>';
|
||||
|
|
|
|||
|
|
@ -71,6 +71,20 @@ describe('portal runtime', function() {
|
|||
expect(handoff.openBillingPanelID).toBe('upgrade-billing-panel');
|
||||
expect(handoff.upgradePortalHandoffID).toBe('cph_signed');
|
||||
expect(handoff.upgradeFeatureKey).toBe('');
|
||||
expect(handoff.upgradeTrialRequested).toBe(false);
|
||||
});
|
||||
|
||||
it('reads a trial request from the handoff arrival query', function() {
|
||||
var handoff = readPortalRuntimeHandoff(
|
||||
'https://cloud.pulserelay.pro/portal?portal_handoff_id=cph_signed&feature=self_hosted_plan&trial=1',
|
||||
);
|
||||
|
||||
expect(handoff.upgradeTrialRequested).toBe(true);
|
||||
|
||||
var noTrial = readPortalRuntimeHandoff(
|
||||
'https://cloud.pulserelay.pro/portal?portal_handoff_id=cph_signed&trial=definitely',
|
||||
);
|
||||
expect(noTrial.upgradeTrialRequested).toBe(false);
|
||||
});
|
||||
|
||||
it('blocks legacy checkout intent arrivals from acting as a trusted upgrade bootstrap', function() {
|
||||
|
|
@ -96,6 +110,7 @@ describe('portal runtime', function() {
|
|||
openBillingPanelID: 'refund-billing-panel',
|
||||
upgradePortalHandoffID: '',
|
||||
upgradeFeatureKey: '',
|
||||
upgradeTrialRequested: false,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -118,12 +133,14 @@ describe('portal runtime', function() {
|
|||
openBillingPanelID: 'upgrade-billing-panel',
|
||||
upgradePortalHandoffID: 'cph_signed',
|
||||
upgradeFeatureKey: '',
|
||||
upgradeTrialRequested: true,
|
||||
}
|
||||
);
|
||||
|
||||
expect(runtime.store.getShellState().activeSection).toBe('billing');
|
||||
expect(runtime.store.getBillingState().openBillingPanelID).toBe('upgrade-billing-panel');
|
||||
expect(runtime.store.getBillingState().upgradePortalHandoffID).toBe('cph_signed');
|
||||
expect(runtime.store.getBillingState().upgradeTrialRequested).toBe(true);
|
||||
expect(runtime.store.getBillingState().upgradeFeatureKey).toBe('');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export interface PortalRuntimeHandoff {
|
|||
openBillingPanelID: string;
|
||||
upgradePortalHandoffID: string;
|
||||
upgradeFeatureKey: string;
|
||||
upgradeTrialRequested: boolean;
|
||||
}
|
||||
|
||||
export interface PortalRuntime {
|
||||
|
|
@ -61,6 +62,11 @@ function normalizeUpgradeFeatureKey(value: string | null): string {
|
|||
return String(value || '').trim();
|
||||
}
|
||||
|
||||
function normalizeUpgradeTrialRequested(value: string | null): boolean {
|
||||
var trimmed = String(value || '').trim().toLowerCase();
|
||||
return trimmed === '1' || trimmed === 'true';
|
||||
}
|
||||
|
||||
export function readPortalRuntimeHandoff(
|
||||
locationHref: string | undefined = window.location.href,
|
||||
): PortalRuntimeHandoff {
|
||||
|
|
@ -76,6 +82,7 @@ export function readPortalRuntimeHandoff(
|
|||
openBillingPanelID: openBillingPanelID,
|
||||
upgradePortalHandoffID: upgradePortalHandoffID,
|
||||
upgradeFeatureKey: normalizeUpgradeFeatureKey(params.get('feature')),
|
||||
upgradeTrialRequested: normalizeUpgradeTrialRequested(params.get('trial')),
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
|
|
@ -83,6 +90,7 @@ export function readPortalRuntimeHandoff(
|
|||
openBillingPanelID: '',
|
||||
upgradePortalHandoffID: '',
|
||||
upgradeFeatureKey: '',
|
||||
upgradeTrialRequested: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -132,12 +140,14 @@ export function createPortalRuntime(
|
|||
billingState.openBillingPanelID = handoff.openBillingPanelID;
|
||||
billingState.upgradePortalHandoffID = handoff.upgradePortalHandoffID;
|
||||
billingState.upgradeFeatureKey = handoff.upgradeFeatureKey;
|
||||
billingState.upgradeTrialRequested = handoff.upgradeTrialRequested;
|
||||
}, { notify: false });
|
||||
} else if (handoff.upgradeFeatureKey || handoff.upgradePortalHandoffID) {
|
||||
store.setActiveShellSection('billing');
|
||||
store.updateBillingState(function(billingState) {
|
||||
billingState.upgradePortalHandoffID = handoff.upgradePortalHandoffID;
|
||||
billingState.upgradeFeatureKey = handoff.upgradeFeatureKey;
|
||||
billingState.upgradeTrialRequested = handoff.upgradeTrialRequested;
|
||||
}, { notify: false });
|
||||
}
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ export function createPortalBillingState(): PortalBillingState {
|
|||
return {
|
||||
openBillingPanelID: '',
|
||||
upgradeFeatureKey: '',
|
||||
upgradeTrialRequested: false,
|
||||
upgradePortalHandoffID: '',
|
||||
upgradePortalHandoff: createQueryState(null),
|
||||
upgradePricing: createQueryState(null),
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ export interface RefundState {
|
|||
export interface PortalBillingState {
|
||||
openBillingPanelID: string;
|
||||
upgradeFeatureKey: string;
|
||||
upgradeTrialRequested: boolean;
|
||||
upgradePortalHandoffID: string;
|
||||
upgradePortalHandoff: PortalQueryState<PortalUpgradePortalHandoffModel | null>;
|
||||
upgradePricing: PortalQueryState<PortalUpgradePricingModel | null>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue