Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subscription fixes #1447

Merged
merged 16 commits into from
Apr 18, 2023
7 changes: 6 additions & 1 deletion src/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ class API {
return data;
}

async getSubscriber(userId = {}) {
async getSubscriber(userId = getUserData().id) {
const authToken = getAuthToken();
if (!(authToken && authToken.length)) {
throw new Error('Need to be authenticated to perform this request');
Expand Down Expand Up @@ -570,6 +570,11 @@ class API {
);
return data;
}

async listSubscriptions() {
const { data } = await this.axiosInstance.get(`/subscription/list`);
return data;
}
}

const API_INSTANCE = new API({});
Expand Down
2 changes: 1 addition & 1 deletion src/components/Board/Board.actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import {
updateDefaultBoardsIncluded,
addDefaultBoardIncluded
} from '../Communicator/Communicator.actions';
import { isAndroid, isCordova, writeCvaFile } from '../../cordova-util';
import { isAndroid, writeCvaFile } from '../../cordova-util';
import { DEFAULT_BOARDS } from '../../helpers';
import history from './../../history';

Expand Down
2 changes: 1 addition & 1 deletion src/components/PremiumFeature/PremiumFeature.messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default defineMessages({
featureBlockedText: {
id: 'cboard.components.PremiumFeature.featureBlockedText',
defaultMessage:
'Cboard disable this feature. To continue using it and all the features like public boards, online voices, advanced edit functions and many more, please upgrade'
'Cboard disabled this feature. To continue using it and all the features like public boards, online voices, advanced edit functions and many more, please upgrade'
},
upgradeNow: {
id: 'cboard.components.PremiumFeature.upgradeNow',
Expand Down
15 changes: 0 additions & 15 deletions src/components/Settings/Subscribe/Subscribe.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,21 @@ const propTypes = {
* flag for user
*/
isLogged: PropTypes.bool.isRequired,
/**
* Name of user
*/
name: PropTypes.string.isRequired,
/**
* User email
*/
email: PropTypes.string.isRequired,
/**
* Handle refresh subscription
*/
onRefreshSubscription: PropTypes.func
};

const defaultProps = {
name: '',
email: '',
location: { country: null, countryCode: null }
};

const Subscribe = ({
onClose,
isLogged,
onSubscribe,
name,
email,
location: { country, countryCode },
onSubmitPeople,
products,
subscription,
onRefreshSubscription
}) => {
Expand All @@ -66,7 +52,6 @@ const Subscribe = ({
{!subscription.isSubscribed ? (
<SubscriptionPlans
subscription={subscription}
products={products}
onRefreshSubscription={onRefreshSubscription}
isLogged={isLogged}
onSubscribe={onSubscribe}
Expand Down
8 changes: 0 additions & 8 deletions src/components/Settings/Subscribe/Subscribe.constants.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
export const AVAIABLE_PRODUCTS_ID = [
{
subscriptionId: 'premium_full',
planId: ['premium-full-features-yearl', 'premium-full-feature-monthly']
}
];

export const INCLUDED_FEATURES = [
'onlineNeuralVoices',
'copyPublicBoards',
'publishBoards',
'copyTiles',
'pasteTiles',
'powerfulUsageAnalytics',
'shareBoards',
'adsFree',
'exportToOpenBoardFormat',
Expand Down
181 changes: 98 additions & 83 deletions src/components/Settings/Subscribe/Subscribe.container.js
Original file line number Diff line number Diff line change
@@ -1,91 +1,55 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from 'react-intl';

import Subscribe from './Subscribe.component';
import { getUser, isLogged } from '../../App/App.selectors';
import API from '../../../api';
import messages from './Subscribe.messages';

import { isAndroid } from '../../../cordova-util';
import { AVAIABLE_PRODUCTS_ID } from './Subscribe.constants';
import {
comprobeSubscription,
updateSubscriberId,
updateSubscription,
updateAndroidSubscriptionState,
updateSubscriptionError,
updateProduct
updateIsSubscribed,
updatePlans
} from '../../../providers/SubscriptionProvider/SubscriptionProvider.actions';
import {
NOT_SUBSCRIBED,
PROCCESING
PROCCESING,
EXPIRED,
ACTIVE
} from '../../../providers/SubscriptionProvider/SubscriptionProvider.constants';

import { formatTitle } from './Subscribe.helpers';

export class SubscribeContainer extends PureComponent {
static propTypes = {
intl: intlShape.isRequired,
history: PropTypes.object.isRequired,
subscription: PropTypes.object.isRequired
};

state = {
name: this.props.user.name,
email: this.props.user.email,
products: []
};

componentDidMount() {
if (isAndroid()) {
window.CdvPurchase.store.when('subscription').updated(this.setProducts);
this.props.comprobeSubscription();
}
this.setProducts();
const { updateIsSubscribed, updatePlans } = this.props;
updateIsSubscribed();
updatePlans();
}

setProducts = () => {
if (isAndroid()) {
const validProducts = window.CdvPurchase.store.products.filter(
product =>
product.offers.length > 0 &&
AVAIABLE_PRODUCTS_ID.some(p => p.subscriptionId === product.id)
);
return this.setState({ products: validProducts });
}

const products = [
{
id: '1',
title: 'Preimum full',
offers: [
{
id: 'premiumfull@month',
pricingPhases: [{ price: 'USD 6,00', billingPeriod: 'P1M' }]
},
{
id: 'premiumfull@year',
pricingPhases: [{ price: 'USD 50,00', billingPeriod: 'P1Y' }]
}
]
}
];
this.setState({ products: products });
};

handleChange = name => event => {
this.setState({
...this.state,
[name]: event.target.value
});
};

handleSubmit = async () => {};

handleRefreshSubscription = () => {
const { comprobeSubscription } = this.props;

window.CdvPurchase.store.restorePurchases();
comprobeSubscription();
const { updateIsSubscribed, updatePlans } = this.props;
//window.CdvPurchase.store.restorePurchases();
updateIsSubscribed();
updatePlans();
};

handleError = e => {
Expand All @@ -112,69 +76,125 @@ export class SubscribeContainer extends PureComponent {
}, 3000);
};

handleSubscribe = (product, offer) => async event => {
handleSubscribe = product => async event => {
const {
intl,
user,
isLogged,
location,
updateSubscriberId,
updateSubscription,
subscription,
updateProduct
subscription
} = this.props;
if (isAndroid()) {
if (
isLogged &&
subscription.androidSubscriptionState === NOT_SUBSCRIBED
(isLogged &&
product &&
subscription.androidSubscriptionState === NOT_SUBSCRIBED) ||
subscription.androidSubscriptionState === EXPIRED
) {
const newProduct = {
title: formatTitle(product.title),
billingPeriod: offer.pricingPhases[0].billingPeriod,
price: offer.pricingPhases[0].price
billingPeriod: product.billingPeriod,
price: product.price,
tag: product.tag,
subscriptionId: product.subscriptionId
};
const apiProduct = {
product: {
...newProduct,
subscriptionId: product.id
...newProduct
}
};

updateSubscription({
isSubscribed: false,
expiryDate: null,
androidSubscriptionState: PROCCESING,
ownedProduct: ''
});

const prod = await window.CdvPurchase.store.products[0];
const localReceipts = window.CdvPurchase.store.findInLocalReceipts(
prod
);

// get offer from the plugin
let offers, offer;
try {
updateSubscription({
isSubscribed: false,
expiryDate: null,
androidSubscriptionState: PROCCESING
});
await window.CdvPurchase.store.update();
offers = prod.offers;
offer = offers.find(offer => offer.tags[0] === product.tag);
} catch (err) {
console.error('Cannot subscribe product. Error: ', err.message);
this.handleError(err);
return;
}

try {
// update the api
const subscriber = await API.getSubscriber(user.id);
updateSubscriberId(subscriber._id);

// check if current subscriber already bought in this device
if (
localReceipts &&
localReceipts.nativePurchase?.orderId !==
subscriber.transaction?.transactionId
) {
this.handleError({
code: '0001',
message: intl.formatMessage(messages.googleAccountAlreadyOwns)
});
return;
}
await API.updateSubscriber(apiProduct);
updateProduct(newProduct);

// proceed with the purchase
const order = await window.CdvPurchase.store.order(offer);
if (order && order.isError) throw order;
} catch (e) {
if (e.response?.data.error === 'subscriber not found') {
updateSubscription({
ownedProduct: product,
androidSubscriptionState: ACTIVE,
isInFreeCountry: false,
isOnTrialPeriod: false,
isSubscribed: true
});
} catch (err) {
if (err.response?.data.error === 'subscriber not found') {
// check if current subscriber already bought in this device
if (localReceipts) {
this.handleError({
code: '0001',
message: intl.formatMessage(messages.googleAccountAlreadyOwns)
});
return;
}
try {
const newSubscriber = {
userId: user.id,
country: location.countryCode || 'Not localized',
status: NOT_SUBSCRIBED,
apiProduct
...apiProduct
};
const res = await API.createSubscriber(newSubscriber);
updateSubscriberId(res._id);
updateProduct(newProduct);
const order = await window.CdvPurchase.store.order(offer);
if (order && order.isError) throw order;
} catch (e) {
console.error('Cannot subscribe product', e.message);
this.handleError(e);
updateSubscription({
ownedProduct: product,
androidSubscriptionState: ACTIVE,
isInFreeCountry: false,
isOnTrialPeriod: false,
isSubscribed: true
});
} catch (err) {
console.error('Cannot subscribe product. Error: ', err.message);
this.handleError(err);
}
return;
}
console.error('Cannot subscribe product', e.message);
this.handleError(e);
console.error('Cannot subscribe product. Error: ', err.message);
this.handleError(err);
}
}
}
Expand All @@ -188,11 +208,7 @@ export class SubscribeContainer extends PureComponent {
onClose={history.goBack}
isLogged={this.props.isLogged}
onSubscribe={this.handleSubscribe}
name={this.state.name}
email={this.state.email}
location={location}
onSubmitPeople={this.handleSubmit}
products={this.state.products}
subscription={this.props.subscription}
updateSubscriberId={this.props.updateSubscriberId}
onRefreshSubscription={this.handleRefreshSubscription}
Expand Down Expand Up @@ -224,13 +240,12 @@ const mapStateToProps = state => {
const mapDispatchToProps = {
updateSubscriberId,
updateSubscription,
comprobeSubscription,
updateAndroidSubscriptionState,
updateSubscriptionError,
updateProduct
updateIsSubscribed,
updatePlans
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(SubscribeContainer);
)(injectIntl(SubscribeContainer));
5 changes: 5 additions & 0 deletions src/components/Settings/Subscribe/Subscribe.messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,10 @@ export default defineMessages({
fixPaymentIssue: {
id: 'cboard.components.Settings.Subscribe.fixPaymentIssue',
defaultMessage: 'Fix your payment issues before the:'
},
googleAccountAlreadyOwns: {
id: 'cboard.components.Settings.Subscribe.googleAccountAlreadyOwns',
defaultMessage:
'It looks that your Google account already owns this product.'
}
});
Loading