Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
thecybernanny-webapp
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
asranov0003
thecybernanny-webapp
Commits
ec458499
Commit
ec458499
authored
Jul 31, 2025
by
asranov0003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add billing pay
parent
85392706
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
76 additions
and
3 deletions
+76
-3
Subscription.tsx
src/pages/Subscription/Subscription.tsx
+33
-3
billingSlice.ts
src/stores/slices/billingSlice.ts
+42
-0
billing.types.ts
src/types/billing.types.ts
+1
-0
No files found.
src/pages/Subscription/Subscription.tsx
View file @
ec458499
...
...
@@ -10,6 +10,7 @@ import {
import
{
fetchPaymentMethods
,
fetchTariffs
,
pay
,
}
from
"../../stores/slices/billingSlice"
;
import
type
{
IPaymentMethod
,
ITariff
}
from
"../../types/billing.types"
;
import
CLoading
from
"../../components/CLoading"
;
...
...
@@ -18,8 +19,14 @@ const Subscription: React.FC = () => {
const
[
selectedTariff
,
setSelectedTariff
]
=
useState
<
ITariff
|
null
>
(
null
);
const
[
selectedPaymentMethod
,
setSelectedPaymentMethod
]
=
useState
<
IPaymentMethod
|
null
>
(
null
);
const
{
tariffs
,
paymentMethods
,
isTariffsLoading
,
isPaymentMethodsLoading
}
=
useAppSelector
((
state
:
RootState
)
=>
state
.
billing
);
const
{
tariffs
,
paymentMethods
,
isTariffsLoading
,
isPaymentMethodsLoading
,
payUrl
,
isPayLoading
,
}
=
useAppSelector
((
state
:
RootState
)
=>
state
.
billing
);
const
dispatch
=
useAppDispatch
();
const
{
t
}
=
useTranslation
();
...
...
@@ -40,6 +47,25 @@ const Subscription: React.FC = () => {
}
},
[
paymentMethods
]);
useEffect
(()
=>
{
if
(
payUrl
)
{
window
.
location
.
href
=
payUrl
;
}
},
[
payUrl
]);
const
handlePay
=
()
=>
{
if
(
!
selectedTariff
||
!
selectedPaymentMethod
)
{
return
;
}
dispatch
(
pay
({
paymentMethodId
:
selectedPaymentMethod
.
id
,
payMonths
:
selectedTariff
.
payMonths
,
})
);
};
return
(
<
div
className=
"subscription wrapper"
>
{
isTariffsLoading
&&
...
...
@@ -101,7 +127,11 @@ const Subscription: React.FC = () => {
</
div
>
</
div
>
<
CButton
title=
{
t
(
"subscription.pay"
)
}
/>
<
CButton
title=
{
t
(
"subscription.pay"
)
}
onClick=
{
handlePay
}
isLoading=
{
isPayLoading
}
/>
</
div
>
);
};
...
...
src/stores/slices/billingSlice.ts
View file @
ec458499
...
...
@@ -9,6 +9,8 @@ interface IBillingState {
paymentMethods
:
IPaymentMethod
[];
isPaymentMethodsLoading
:
boolean
;
paymentMethodsError
:
string
|
null
;
payUrl
:
string
;
isPayLoading
:
boolean
;
}
const
initialState
:
IBillingState
=
{
...
...
@@ -18,6 +20,8 @@ const initialState: IBillingState = {
paymentMethods
:
[],
isPaymentMethodsLoading
:
false
,
paymentMethodsError
:
null
,
payUrl
:
""
,
isPayLoading
:
false
,
};
export
const
fetchTariffs
=
createAsyncThunk
(
...
...
@@ -58,6 +62,31 @@ export const fetchPaymentMethods = createAsyncThunk(
}
);
export
const
pay
=
createAsyncThunk
(
"billing/pay"
,
async
(
{
payMonths
,
paymentMethodId
,
}:
{
payMonths
:
string
;
paymentMethodId
:
string
},
{
rejectWithValue
}
)
=>
{
try
{
const
response
=
await
sendRpcRequest
<
{
url
:
string
}
>
(
"billing.pay"
,
{
methodId
:
paymentMethodId
,
months
:
payMonths
,
});
return
response
.
url
;
}
catch
(
error
:
unknown
)
{
if
(
typeof
error
===
"object"
&&
error
!==
null
&&
"message"
in
error
)
{
return
rejectWithValue
(
error
.
message
);
}
return
rejectWithValue
(
"Unknown error occurred"
);
}
}
);
const
billingSlice
=
createSlice
({
name
:
"billing"
,
initialState
,
...
...
@@ -88,6 +117,19 @@ const billingSlice = createSlice({
.
addCase
(
fetchPaymentMethods
.
rejected
,
(
state
,
action
)
=>
{
state
.
isPaymentMethodsLoading
=
false
;
state
.
paymentMethodsError
=
action
.
payload
as
string
;
})
.
addCase
(
pay
.
pending
,
(
state
)
=>
{
state
.
isPayLoading
=
true
;
state
.
payUrl
=
""
;
})
.
addCase
(
pay
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
isPayLoading
=
false
;
state
.
payUrl
=
action
.
payload
;
})
.
addCase
(
pay
.
rejected
,
(
state
,
action
)
=>
{
state
.
isPayLoading
=
false
;
state
.
payUrl
=
action
.
payload
as
string
;
});
},
});
...
...
src/types/billing.types.ts
View file @
ec458499
...
...
@@ -2,6 +2,7 @@ export interface ITariff {
id
:
string
;
title
:
string
;
amount
:
string
;
payMonths
:
string
;
}
export
interface
IPaymentMethod
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment