From e3d08a4275ee1c0c5561db86123098cda7ec1920 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 19 Oct 2023 08:30:34 +0000 Subject: [PATCH] chore: send to customer portal for manage (#3763) --- ee/query-service/app/api/api.go | 1 + ee/query-service/app/api/license.go | 34 ++++++++- frontend/src/api/billing/manage.ts | 30 ++++++++ .../BillingContainer/BillingContainer.tsx | 72 ++++++++++++++----- 4 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 frontend/src/api/billing/manage.ts diff --git a/ee/query-service/app/api/api.go b/ee/query-service/app/api/api.go index 1c0171efac..a17eb7b79a 100644 --- a/ee/query-service/app/api/api.go +++ b/ee/query-service/app/api/api.go @@ -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/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", am.ViewAccess(ah.listLicensesV2)). diff --git a/ee/query-service/app/api/license.go b/ee/query-service/app/api/license.go index c125fd10d1..66d108d468 100644 --- a/ee/query-service/app/api/license.go +++ b/ee/query-service/app/api/license.go @@ -14,7 +14,7 @@ import ( type tierBreakdown struct { UnitPrice float64 `json:"unitPrice"` - Quantity int64 `json:"quantity"` + Quantity float64 `json:"quantity"` TierStart int64 `json:"tierStart"` TierEnd int64 `json:"tierEnd"` TierCost float64 `json:"tierCost"` @@ -224,3 +224,35 @@ func (ah *APIHandler) listLicensesV2(w http.ResponseWriter, r *http.Request) { 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) +} diff --git a/frontend/src/api/billing/manage.ts b/frontend/src/api/billing/manage.ts new file mode 100644 index 0000000000..dca561bdba --- /dev/null +++ b/frontend/src/api/billing/manage.ts @@ -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 | 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; diff --git a/frontend/src/container/BillingContainer/BillingContainer.tsx b/frontend/src/container/BillingContainer/BillingContainer.tsx index f5a35bbeae..3d41317c7c 100644 --- a/frontend/src/container/BillingContainer/BillingContainer.tsx +++ b/frontend/src/container/BillingContainer/BillingContainer.tsx @@ -6,6 +6,7 @@ import { Button, Col, Row, Skeleton, Table, Tag, Typography } from 'antd'; import { ColumnsType } from 'antd/es/table'; import updateCreditCardApi from 'api/billing/checkout'; import getUsage from 'api/billing/getUsage'; +import manageCreditCardApi from 'api/billing/manage'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import useAxiosError from 'hooks/useAxiosError'; @@ -15,6 +16,8 @@ import { useCallback, useEffect, useState } from 'react'; import { useMutation, useQuery } from 'react-query'; import { useSelector } from 'react-redux'; 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 AppReducer from 'types/reducer/app'; import { getFormattedDate } from 'utils/timeUtils'; @@ -271,32 +274,65 @@ export default function BillingContainer(): JSX.Element { /> ); + const handleBillingOnSuccess = ( + data: ErrorResponse | SuccessResponse, + ): 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( updateCreditCardApi, { onSuccess: (data) => { - if (data.payload?.redirectURL) { - const newTab = document.createElement('a'); - newTab.href = data.payload.redirectURL; - newTab.target = '_blank'; - newTab.rel = 'noopener noreferrer'; - newTab.click(); - } + handleBillingOnSuccess(data); }, - onError: () => - notifications.error({ - message: SOMETHING_WENT_WRONG, - }), + onError: handleBillingOnError, }, ); + const { + mutate: manageCreditCard, + isLoading: isLoadingManageBilling, + } = useMutation(manageCreditCardApi, { + onSuccess: (data) => { + handleBillingOnSuccess(data); + }, + onError: handleBillingOnError, + }); + const handleBilling = useCallback(async () => { - updateCreditCard({ - licenseKey: activeLicense?.key || '', - successURL: window.location.href, - cancelURL: window.location.href, - }); - }, [activeLicense?.key, updateCreditCard]); + if (isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription) { + updateCreditCard({ + licenseKey: activeLicense?.key || '', + successURL: window.location.href, + cancelURL: window.location.href, + }); + } else { + manageCreditCard({ + licenseKey: activeLicense?.key || '', + successURL: window.location.href, + cancelURL: window.location.href, + }); + } + }, [ + activeLicense?.key, + isFreeTrial, + licensesData?.payload?.trialConvertedToSubscription, + manageCreditCard, + updateCreditCard, + ]); return (
@@ -330,7 +366,7 @@ export default function BillingContainer(): JSX.Element {