feat: support Pricings flow (#2250)

* feat: fix price display

* feat: support subscription

* feat: fix select-plan-> signup -> buy-plan -> login flow

* feat: support paid-user to login and jump to the pricing page

* feat: support more subscription state

* feat: add payment providers for plan

* feat: format code

* feat: gofumpt

* feat: redirect to buy-plan-result page when user have pending subscription

* feat: response err when pricing don't exit

* Update PricingListPage.js

* Update ProductBuyPage.js

* Update LoginPage.js

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
This commit is contained in:
haiwu
2023-08-24 23:20:50 +08:00
committed by GitHub
parent 8073dfa88c
commit 05b2f00057
31 changed files with 759 additions and 295 deletions

View File

@ -24,11 +24,13 @@ import i18next from "i18next";
class PricingPage extends React.Component {
constructor(props) {
super(props);
const params = new URLSearchParams(window.location.search);
this.state = {
classes: props,
applications: null,
owner: props.owner ?? (props.match?.params?.owner ?? null),
pricingName: (props.pricingName ?? props.match?.params?.pricingName) ?? null,
userName: params.get("user"),
pricing: props.pricing,
plans: null,
loading: false,
@ -39,7 +41,9 @@ class PricingPage extends React.Component {
this.setState({
applications: [],
});
if (this.state.userName) {
Setting.showMessage("info", `${i18next.t("pricing:paid-user do not have active subscription or pending subscription, please select a plan to buy")}`);
}
if (this.state.pricing) {
this.loadPlans();
} else {
@ -60,7 +64,7 @@ class PricingPage extends React.Component {
loadPlans() {
const plans = this.state.pricing.plans.map((plan) =>
PlanBackend.getPlanById(plan, true));
PlanBackend.getPlan(this.state.owner, plan, true));
Promise.all(plans)
.then(results => {
@ -70,7 +74,7 @@ class PricingPage extends React.Component {
return;
}
this.setState({
plans: results,
plans: results.map(result => result.data),
loading: false,
});
})
@ -90,7 +94,6 @@ class PricingPage extends React.Component {
Setting.showMessage("error", res.msg);
return;
}
this.setState({
loading: false,
pricing: res.data,
@ -105,9 +108,12 @@ class PricingPage extends React.Component {
renderCards() {
const getUrlByPlan = (plan) => {
const getUrlByPlan = (planName) => {
const pricing = this.state.pricing;
const signUpUrl = `/signup/${pricing.application}?plan=${plan}&pricing=${pricing.name}`;
let signUpUrl = `/signup/${pricing.application}?plan=${planName}&pricing=${pricing.name}`;
if (this.state.userName) {
signUpUrl = `/buy-plan/${pricing.owner}/${pricing.name}?plan=${planName}&user=${this.state.userName}`;
}
return `${window.location.origin}${signUpUrl}`;
};

View File

@ -29,21 +29,16 @@ class SingleCard extends React.Component {
}
renderCard(plan, isSingle, link) {
return (
<Col style={{minWidth: "320px", paddingLeft: "20px", paddingRight: "20px", paddingBottom: "20px", marginBottom: "20px", paddingTop: "0px"}} span={6}>
<Card
hoverable
onClick={() => Setting.isMobile() ? window.location.href = link : null}
style={isSingle ? {width: "320px", height: "100%"} : {width: "100%", height: "100%", paddingTop: "0px"}}
title={<h2>{plan.displayName}</h2>}
>
<div style={{textAlign: "right"}}>
<h2
style={{marginTop: "0px"}}>{plan.displayName}</h2>
</div>
<div style={{textAlign: "left"}} className="px-10 mt-5">
<span style={{fontWeight: 700, fontSize: "48px"}}>$ {plan.pricePerMonth}</span>
<span style={{fontSize: "40px", fontWeight: 700}}>{Setting.getCurrencySymbol(plan.currency)} {plan.pricePerMonth}</span>
<span style={{fontSize: "18px", fontWeight: 600, color: "gray"}}> {i18next.t("plan:per month")}</span>
</div>