mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 12:18:58 +08:00
chore: send to customer portal for manage (#3763)
This commit is contained in:
parent
814431e3a8
commit
e3d08a4275
@ -158,6 +158,7 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddlew
|
|||||||
|
|
||||||
router.HandleFunc("/api/v1/checkout", am.AdminAccess(ah.checkout)).Methods(http.MethodPost)
|
router.HandleFunc("/api/v1/checkout", am.AdminAccess(ah.checkout)).Methods(http.MethodPost)
|
||||||
router.HandleFunc("/api/v1/billing", am.AdminAccess(ah.getBilling)).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/billing", am.AdminAccess(ah.getBilling)).Methods(http.MethodGet)
|
||||||
|
router.HandleFunc("/api/v1/portal", am.AdminAccess(ah.portalSession)).Methods(http.MethodPost)
|
||||||
|
|
||||||
router.HandleFunc("/api/v2/licenses",
|
router.HandleFunc("/api/v2/licenses",
|
||||||
am.ViewAccess(ah.listLicensesV2)).
|
am.ViewAccess(ah.listLicensesV2)).
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
type tierBreakdown struct {
|
type tierBreakdown struct {
|
||||||
UnitPrice float64 `json:"unitPrice"`
|
UnitPrice float64 `json:"unitPrice"`
|
||||||
Quantity int64 `json:"quantity"`
|
Quantity float64 `json:"quantity"`
|
||||||
TierStart int64 `json:"tierStart"`
|
TierStart int64 `json:"tierStart"`
|
||||||
TierEnd int64 `json:"tierEnd"`
|
TierEnd int64 `json:"tierEnd"`
|
||||||
TierCost float64 `json:"tierCost"`
|
TierCost float64 `json:"tierCost"`
|
||||||
@ -224,3 +224,35 @@ func (ah *APIHandler) listLicensesV2(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
ah.Respond(w, resp)
|
ah.Respond(w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ah *APIHandler) portalSession(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
type checkoutResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Data struct {
|
||||||
|
RedirectURL string `json:"redirectURL"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
hClient := &http.Client{}
|
||||||
|
req, err := http.NewRequest("POST", constants.LicenseSignozIo+"/portal", r.Body)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, model.InternalError(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header.Add("X-SigNoz-SecretKey", constants.LicenseAPIKey)
|
||||||
|
licenseResp, err := hClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, model.InternalError(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode response body
|
||||||
|
var resp checkoutResponse
|
||||||
|
if err := json.NewDecoder(licenseResp.Body).Decode(&resp); err != nil {
|
||||||
|
RespondError(w, model.InternalError(err), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ah.Respond(w, resp.Data)
|
||||||
|
}
|
||||||
|
30
frontend/src/api/billing/manage.ts
Normal file
30
frontend/src/api/billing/manage.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import {
|
||||||
|
CheckoutRequestPayloadProps,
|
||||||
|
CheckoutSuccessPayloadProps,
|
||||||
|
} from 'types/api/billing/checkout';
|
||||||
|
|
||||||
|
const manageCreditCardApi = async (
|
||||||
|
props: CheckoutRequestPayloadProps,
|
||||||
|
): Promise<SuccessResponse<CheckoutSuccessPayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/portal', {
|
||||||
|
licenseKey: props.licenseKey,
|
||||||
|
returnURL: props.successURL,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default manageCreditCardApi;
|
@ -6,6 +6,7 @@ import { Button, Col, Row, Skeleton, Table, Tag, Typography } from 'antd';
|
|||||||
import { ColumnsType } from 'antd/es/table';
|
import { ColumnsType } from 'antd/es/table';
|
||||||
import updateCreditCardApi from 'api/billing/checkout';
|
import updateCreditCardApi from 'api/billing/checkout';
|
||||||
import getUsage from 'api/billing/getUsage';
|
import getUsage from 'api/billing/getUsage';
|
||||||
|
import manageCreditCardApi from 'api/billing/manage';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
import useAxiosError from 'hooks/useAxiosError';
|
import useAxiosError from 'hooks/useAxiosError';
|
||||||
@ -15,6 +16,8 @@ import { useCallback, useEffect, useState } from 'react';
|
|||||||
import { useMutation, useQuery } from 'react-query';
|
import { useMutation, useQuery } from 'react-query';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { CheckoutSuccessPayloadProps } from 'types/api/billing/checkout';
|
||||||
import { License } from 'types/api/licenses/def';
|
import { License } from 'types/api/licenses/def';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
import { getFormattedDate } from 'utils/timeUtils';
|
import { getFormattedDate } from 'utils/timeUtils';
|
||||||
@ -271,32 +274,65 @@ export default function BillingContainer(): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleBillingOnSuccess = (
|
||||||
|
data: ErrorResponse | SuccessResponse<CheckoutSuccessPayloadProps, unknown>,
|
||||||
|
): void => {
|
||||||
|
if (data?.payload?.redirectURL) {
|
||||||
|
const newTab = document.createElement('a');
|
||||||
|
newTab.href = data.payload.redirectURL;
|
||||||
|
newTab.target = '_blank';
|
||||||
|
newTab.rel = 'noopener noreferrer';
|
||||||
|
newTab.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBillingOnError = (): void => {
|
||||||
|
notifications.error({
|
||||||
|
message: SOMETHING_WENT_WRONG,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
|
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
|
||||||
updateCreditCardApi,
|
updateCreditCardApi,
|
||||||
{
|
{
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (data.payload?.redirectURL) {
|
handleBillingOnSuccess(data);
|
||||||
const newTab = document.createElement('a');
|
|
||||||
newTab.href = data.payload.redirectURL;
|
|
||||||
newTab.target = '_blank';
|
|
||||||
newTab.rel = 'noopener noreferrer';
|
|
||||||
newTab.click();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onError: () =>
|
onError: handleBillingOnError,
|
||||||
notifications.error({
|
|
||||||
message: SOMETHING_WENT_WRONG,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
mutate: manageCreditCard,
|
||||||
|
isLoading: isLoadingManageBilling,
|
||||||
|
} = useMutation(manageCreditCardApi, {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
handleBillingOnSuccess(data);
|
||||||
|
},
|
||||||
|
onError: handleBillingOnError,
|
||||||
|
});
|
||||||
|
|
||||||
const handleBilling = useCallback(async () => {
|
const handleBilling = useCallback(async () => {
|
||||||
updateCreditCard({
|
if (isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription) {
|
||||||
licenseKey: activeLicense?.key || '',
|
updateCreditCard({
|
||||||
successURL: window.location.href,
|
licenseKey: activeLicense?.key || '',
|
||||||
cancelURL: window.location.href,
|
successURL: window.location.href,
|
||||||
});
|
cancelURL: window.location.href,
|
||||||
}, [activeLicense?.key, updateCreditCard]);
|
});
|
||||||
|
} else {
|
||||||
|
manageCreditCard({
|
||||||
|
licenseKey: activeLicense?.key || '',
|
||||||
|
successURL: window.location.href,
|
||||||
|
cancelURL: window.location.href,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
activeLicense?.key,
|
||||||
|
isFreeTrial,
|
||||||
|
licensesData?.payload?.trialConvertedToSubscription,
|
||||||
|
manageCreditCard,
|
||||||
|
updateCreditCard,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="billing-container">
|
<div className="billing-container">
|
||||||
@ -330,7 +366,7 @@ export default function BillingContainer(): JSX.Element {
|
|||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="middle"
|
size="middle"
|
||||||
loading={isLoadingBilling}
|
loading={isLoadingBilling || isLoadingManageBilling}
|
||||||
onClick={handleBilling}
|
onClick={handleBilling}
|
||||||
>
|
>
|
||||||
{isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription
|
{isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription
|
||||||
|
Loading…
x
Reference in New Issue
Block a user