diff --git a/package.json b/package.json
index 6c52aed99..ac618252c 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"@material-ui/icons": "^4.11.3",
"@material-ui/lab": "4.0.0-alpha.57",
"@microsoft/applicationinsights-web": "^2.8.11",
+ "@paypal/react-paypal-js": "^7.8.3",
"@redux-beacon/google-analytics-gtag": "^1.1.0",
"@redux-beacon/logger": "^1.0.0",
"@redux-beacon/offline-web": "^1.0.0",
diff --git a/src/api/api.js b/src/api/api.js
index 300d8dcc4..c4906ab24 100644
--- a/src/api/api.js
+++ b/src/api/api.js
@@ -531,6 +531,24 @@ class API {
return data;
}
+ async cancelPlan(subscriptionId = '') {
+ const authToken = getAuthToken();
+ if (!(authToken && authToken.length)) {
+ throw new Error('Need to be authenticated to perform this request');
+ }
+ const data = { reason: 'User cancelled the subscription' };
+
+ const headers = {
+ Authorization: `Bearer ${authToken}`
+ };
+ const res = await this.axiosInstance.post(
+ `/subscriber/cancel/${subscriptionId}`,
+ { data },
+ { headers }
+ );
+ return res;
+ }
+
async postTransaction(transaction = {}) {
const authToken = getAuthToken();
if (!(authToken && authToken.length)) {
diff --git a/src/components/Settings/Settings.component.js b/src/components/Settings/Settings.component.js
index 50dc9c1cd..e40874616 100644
--- a/src/components/Settings/Settings.component.js
+++ b/src/components/Settings/Settings.component.js
@@ -25,7 +25,7 @@ import Paper from '@material-ui/core/Paper';
import UserIcon from '../UI/UserIcon';
import SettingsTour from './SettingsTour.component';
-import { isCordova, isAndroid } from '../../cordova-util';
+import { isCordova, isAndroid, isElectron, isIOS } from '../../cordova-util';
import './Settings.css';
import { CircularProgress } from '@material-ui/core';
@@ -98,7 +98,7 @@ export class Settings extends PureComponent {
}
];
- if (isAndroid() && !isInFreeCountry) {
+ if (!isIOS() && !isElectron() && !isInFreeCountry) {
const subscribeSection = {
icon: ,
text: messages.subscribe,
diff --git a/src/components/Settings/Subscribe/Subscribe.component.js b/src/components/Settings/Subscribe/Subscribe.component.js
index 1d852d835..1188b7721 100644
--- a/src/components/Settings/Subscribe/Subscribe.component.js
+++ b/src/components/Settings/Subscribe/Subscribe.component.js
@@ -26,7 +26,10 @@ const propTypes = {
/**
* Handle refresh subscription
*/
- onRefreshSubscription: PropTypes.func
+ onRefreshSubscription: PropTypes.func,
+ onSubscribeCancel: PropTypes.func.isRequired,
+ onCancelSubscription: PropTypes.func.isRequired,
+ cancelSubscriptionStatus: PropTypes.string.isRequired
};
const defaultProps = {
@@ -39,7 +42,11 @@ const Subscribe = ({
onSubscribe,
location: { country, countryCode },
subscription,
- onRefreshSubscription
+ onRefreshSubscription,
+ onSubscribeCancel,
+ onPaypalApprove,
+ onCancelSubscription,
+ cancelSubscriptionStatus
}) => {
return (
@@ -55,9 +62,15 @@ const Subscribe = ({
onRefreshSubscription={onRefreshSubscription}
isLogged={isLogged}
onSubscribe={onSubscribe}
+ onSubscribeCancel={onSubscribeCancel}
+ onPaypalApprove={onPaypalApprove}
/>
) : (
-
+
)}
diff --git a/src/components/Settings/Subscribe/Subscribe.container.js b/src/components/Settings/Subscribe/Subscribe.container.js
index cc5d4c2df..c86caceec 100644
--- a/src/components/Settings/Subscribe/Subscribe.container.js
+++ b/src/components/Settings/Subscribe/Subscribe.container.js
@@ -32,6 +32,10 @@ export class SubscribeContainer extends PureComponent {
subscription: PropTypes.object.isRequired
};
+ state = {
+ cancelSubscriptionStatus: ''
+ };
+
componentDidMount() {
const { updateIsSubscribed, updatePlans } = this.props;
updateIsSubscribed();
@@ -47,11 +51,25 @@ export class SubscribeContainer extends PureComponent {
handleRefreshSubscription = () => {
const { updateIsSubscribed, updatePlans } = this.props;
- //window.CdvPurchase.store.restorePurchases();
updateIsSubscribed();
updatePlans();
};
+ handleCancelSubscription = async ownedProduct => {
+ console.log(ownedProduct);
+ const { updateIsSubscribed, updatePlans } = this.props;
+ try {
+ this.setState({ cancelSubscriptionStatus: 'cancelling' });
+ await API.cancelPlan(ownedProduct.paypalSubscriptionId);
+ this.setState({ cancelSubscriptionStatus: 'ok' });
+ updateIsSubscribed();
+ updatePlans();
+ } catch (err) {
+ console.error(err.message);
+ this.setState({ cancelSubscriptionStatus: 'error' });
+ }
+ };
+
handleError = e => {
const { updateSubscriptionError, updateSubscription } = this.props;
@@ -64,7 +82,7 @@ export class SubscribeContainer extends PureComponent {
updateSubscription({
isSubscribed: false,
expiryDate: null,
- androidSubscriptionState: NOT_SUBSCRIBED
+ status: NOT_SUBSCRIBED
});
setTimeout(() => {
@@ -76,7 +94,62 @@ export class SubscribeContainer extends PureComponent {
}, 3000);
};
- handleSubscribe = product => async event => {
+ handleSubscribeCancel = () => {
+ const { updateSubscription } = this.props;
+ updateSubscription({
+ isSubscribed: false,
+ expiryDate: null,
+ status: NOT_SUBSCRIBED,
+ ownedProduct: ''
+ });
+ };
+
+ handlePaypalApprove = async (product, data) => {
+ const { updateSubscription } = this.props;
+ const {
+ facilitatorAccessToken,
+ orderID,
+ paymentSource,
+ subscriptionID
+ } = data;
+ const transaction = {
+ className: 'Transaction',
+ subscriptionId: subscriptionID,
+ transactionId: subscriptionID,
+ state: 'approved',
+ products: [product],
+ platform: paymentSource,
+ nativePurchase: '',
+ purchaseId: orderID,
+ purchaseDate: '',
+ isPending: false,
+ subscriptionState: ACTIVE,
+ expiryDate: '',
+ facilitatorAccessToken
+ };
+ try {
+ const res = await API.postTransaction(transaction);
+ if (!res.ok) throw res;
+ const subscriber = await API.getSubscriber();
+ updateSubscription({
+ ownedProduct: {
+ ...product,
+ paypalSubscriptionId: subscriptionID,
+ paypalOrderId: orderID
+ },
+ status: ACTIVE,
+ isInFreeCountry: false,
+ isOnTrialPeriod: false,
+ isSubscribed: true,
+ expiryDate: subscriber.transaction.expiryDate
+ });
+ } catch (err) {
+ console.error('Cannot subscribe product. Error: ', err.message);
+ this.handleError(err);
+ }
+ };
+
+ handleSubscribe = async product => {
const {
intl,
user,
@@ -86,40 +159,37 @@ export class SubscribeContainer extends PureComponent {
updateSubscription,
subscription
} = this.props;
- if (isAndroid()) {
- if (
- (isLogged &&
- product &&
- subscription.androidSubscriptionState === NOT_SUBSCRIBED) ||
- subscription.androidSubscriptionState === EXPIRED
- ) {
- const newProduct = {
- title: formatTitle(product.title),
- billingPeriod: product.billingPeriod,
- price: product.price,
- tag: product.tag,
- subscriptionId: product.subscriptionId
- };
- const apiProduct = {
- product: {
- ...newProduct
- }
- };
+ if (
+ (isLogged && product && subscription.status === NOT_SUBSCRIBED) ||
+ subscription.status === EXPIRED
+ ) {
+ const newProduct = {
+ title: formatTitle(product.title),
+ billingPeriod: product.billingPeriod,
+ price: product.price,
+ tag: product.tag,
+ subscriptionId: product.subscriptionId
+ };
+ const apiProduct = {
+ product: {
+ ...newProduct
+ }
+ };
- updateSubscription({
- isSubscribed: false,
- expiryDate: null,
- androidSubscriptionState: PROCCESING,
- ownedProduct: ''
- });
+ updateSubscription({
+ isSubscribed: false,
+ expiryDate: null,
+ status: PROCCESING,
+ ownedProduct: ''
+ });
+ let localReceipts = '';
+ let offers, offer;
+ if (isAndroid()) {
const prod = await window.CdvPurchase.store.products[0];
- const localReceipts = window.CdvPurchase.store.findInLocalReceipts(
- prod
- );
+ localReceipts = window.CdvPurchase.store.findInLocalReceipts(prod);
// get offer from the plugin
- let offers, offer;
try {
await window.CdvPurchase.store.update();
offers = prod.offers;
@@ -129,73 +199,77 @@ export class SubscribeContainer extends PureComponent {
this.handleError(err);
return;
}
+ }
- try {
- // update the api
- const subscriber = await API.getSubscriber(user.id);
- updateSubscriberId(subscriber._id);
+ 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);
+ // 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);
- // proceed with the purchase
+ // proceed with the purchase
+ if (isAndroid()) {
const order = await window.CdvPurchase.store.order(offer);
if (order && order.isError) throw order;
updateSubscription({
ownedProduct: product,
- androidSubscriptionState: ACTIVE,
+ status: 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
- };
- const res = await API.createSubscriber(newSubscriber);
- updateSubscriberId(res._id);
+ }
+ } 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
+ };
+ const res = await API.createSubscriber(newSubscriber);
+ updateSubscriberId(res._id);
+ if (isAndroid()) {
const order = await window.CdvPurchase.store.order(offer);
if (order && order.isError) throw order;
updateSubscription({
ownedProduct: product,
- androidSubscriptionState: ACTIVE,
+ status: ACTIVE,
isInFreeCountry: false,
isOnTrialPeriod: false,
isSubscribed: true
});
- } catch (err) {
- console.error('Cannot subscribe product. Error: ', err.message);
- this.handleError(err);
}
- return;
+ } catch (err) {
+ console.error('Cannot subscribe product. Error: ', err.message);
+ this.handleError(err);
}
- console.error('Cannot subscribe product. Error: ', err.message);
- this.handleError(err);
+ return;
}
+ console.error('Cannot subscribe product. Error: ', err.message);
+ this.handleError(err);
}
}
};
@@ -212,6 +286,10 @@ export class SubscribeContainer extends PureComponent {
subscription={this.props.subscription}
updateSubscriberId={this.props.updateSubscriberId}
onRefreshSubscription={this.handleRefreshSubscription}
+ onSubscribeCancel={this.handleSubscribeCancel}
+ onPaypalApprove={this.handlePaypalApprove}
+ onCancelSubscription={this.handleCancelSubscription}
+ cancelSubscriptionStatus={this.state.cancelSubscriptionStatus}
/>
);
}
diff --git a/src/components/Settings/Subscribe/Subscribe.messages.js b/src/components/Settings/Subscribe/Subscribe.messages.js
index f7db0a44e..f78e72681 100644
--- a/src/components/Settings/Subscribe/Subscribe.messages.js
+++ b/src/components/Settings/Subscribe/Subscribe.messages.js
@@ -115,6 +115,10 @@ export default defineMessages({
id: 'cboard.components.Settings.Subscribe.manageSubscription',
defaultMessage: 'Manage Subscription'
},
+ cancelSubscription: {
+ id: 'cboard.components.Settings.Subscribe.cancelSubscription',
+ defaultMessage: 'Cancel Subscription'
+ },
planAmount: {
id: 'cboard.components.Settings.Subscribe.planAmount',
defaultMessage: 'Plan amount:'
@@ -143,5 +147,22 @@ export default defineMessages({
id: 'cboard.components.Settings.Subscribe.googleAccountAlreadyOwns',
defaultMessage:
'It looks that your Google account already owns this product.'
+ },
+ close: {
+ id: 'cboard.components.Settings.Subscribe.close',
+ defaultMessage: 'Close'
+ },
+ cancelSubscriptionDescription: {
+ id: 'cboard.components.Settings.Subscribe.cancelSubscriptionDescription',
+ defaultMessage: 'Are you sure you want to cancel your current plan?'
+ },
+ canceledSubscriptionOk: {
+ id: 'cboard.components.Settings.Subscribe.canceledSubscriptionOk',
+ defaultMessage: 'Your subscription was cancelled successfully.'
+ },
+ canceledSubscriptionError: {
+ id: 'cboard.components.Settings.Subscribe.canceledSubscriptionError',
+ defaultMessage:
+ 'There was an error cancelling your subscription, please try again in a moment.'
}
});
diff --git a/src/components/Settings/Subscribe/SubscriptionInfo.js b/src/components/Settings/Subscribe/SubscriptionInfo.js
index b6e40354e..2d4bcdd70 100644
--- a/src/components/Settings/Subscribe/SubscriptionInfo.js
+++ b/src/components/Settings/Subscribe/SubscriptionInfo.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useState } from 'react';
import { connect } from 'react-redux';
import Button from '@material-ui/core/Button';
@@ -12,34 +12,48 @@ import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
+import Dialog from '@material-ui/core/Dialog';
+import DialogTitle from '@material-ui/core/DialogTitle';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogContentText from '@material-ui/core/DialogContentText';
+import DialogActions from '@material-ui/core/DialogActions';
+import Box from '@material-ui/core/Box';
import { formatDuration } from './Subscribe.helpers';
+import LoadingIcon from '../../UI/LoadingIcon';
import {
ACTIVE,
CANCELED,
+ CANCELLED,
IN_GRACE_PERIOD
} from '../../../providers/SubscriptionProvider/SubscriptionProvider.constants';
import RefreshIcon from '@material-ui/icons/Refresh';
import IconButton from '../../UI/IconButton';
+import { isAndroid } from '../../../cordova-util';
const propTypes = {
ownedProduct: PropTypes.object.isRequired,
expiryDate: PropTypes.string.isRequired,
- androidSubscriptionState: PropTypes.string.isRequired,
+ status: PropTypes.string.isRequired,
onRefreshSubscription: PropTypes.func.isRequired,
- intl: PropTypes.object.isRequired
+ intl: PropTypes.object.isRequired,
+ onCancelSubscription: PropTypes.func.isRequired,
+ cancelSubscriptionStatus: PropTypes.string.isRequired
};
const LABEL = 0;
const VALUE = 1;
-const subscriptionInfo = ({
+const SubscriptionInfo = ({
ownedProduct,
expiryDate,
- androidSubscriptionState,
+ status,
onRefreshSubscription,
- intl
+ intl,
+ onCancelSubscription,
+ cancelSubscriptionStatus
}) => {
+ const [cancelDialog, setCancelDialog] = useState(false);
const { title, billingPeriod, price } = ownedProduct;
const planAmount = `${price?.currencyCode} ${price?.units} / ${formatDuration(
@@ -49,19 +63,23 @@ const subscriptionInfo = ({
const formatedDate = new Date(expiryDate).toLocaleString();
const statusColor =
- androidSubscriptionState === ACTIVE
+ status === ACTIVE
? { backgroundColor: 'green' }
: { backgroundColor: 'darkorange' };
const getPaymentLabel = () => {
- if (androidSubscriptionState === ACTIVE) return 'nextPayment';
- if (androidSubscriptionState === IN_GRACE_PERIOD) return 'fixPaymentIssue';
- if (androidSubscriptionState === CANCELED) return 'premiumWillEnd';
+ if (status === ACTIVE) return 'nextPayment';
+ if (status === IN_GRACE_PERIOD) return 'fixPaymentIssue';
+ if (status === CANCELED || status === CANCELLED) return 'premiumWillEnd';
+ };
+
+ const handleDialogClose = () => {
+ setCancelDialog(false);
};
const subscription = {
title,
- status: androidSubscriptionState,
+ status: status,
planAmount,
paymentLabel: formatedDate
};
@@ -86,12 +104,20 @@ const subscriptionInfo = ({
{row[LABEL] === 'status' ? (
- }
- size="small"
- color="primary"
- style={statusColor}
- />
+
+ }
+ size="small"
+ color="primary"
+ style={statusColor}
+ />
+
+
+
+
) : (
row[VALUE]
)}
@@ -103,36 +129,98 @@ const subscriptionInfo = ({
-
-
-
+
];
};
-subscriptionInfo.propTypes = propTypes;
+SubscriptionInfo.propTypes = propTypes;
const mapStateToProps = ({
- subscription: { ownedProduct, expiryDate, androidSubscriptionState }
+ subscription: { ownedProduct, expiryDate, status }
}) => ({
ownedProduct,
expiryDate,
- androidSubscriptionState
+ status
});
const mapDispatchToProps = {};
@@ -140,4 +228,4 @@ const mapDispatchToProps = {};
export default connect(
mapStateToProps,
mapDispatchToProps
-)(injectIntl(subscriptionInfo));
+)(injectIntl(SubscriptionInfo));
diff --git a/src/components/Settings/Subscribe/SubscriptionPlans.js b/src/components/Settings/Subscribe/SubscriptionPlans.js
index e5e2dd256..802ca4d07 100644
--- a/src/components/Settings/Subscribe/SubscriptionPlans.js
+++ b/src/components/Settings/Subscribe/SubscriptionPlans.js
@@ -7,6 +7,7 @@ import Typography from '@material-ui/core/Typography';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/core/styles';
import { FormattedMessage } from 'react-intl';
+import { PayPalButtons } from '@paypal/react-paypal-js';
import {
INCLUDED_FEATURES,
@@ -15,7 +16,7 @@ import {
ON_TRIAL_PERIOD
} from './Subscribe.constants';
import { formatDuration, formatTitle } from './Subscribe.helpers';
-import { isAndroid } from '../../../cordova-util';
+import { isAndroid, isCordova } from '../../../cordova-util';
import { CircularProgress } from '@material-ui/core';
import { Link } from 'react-router-dom';
@@ -42,7 +43,9 @@ const propTypes = {
subscription: PropTypes.object.isRequired,
onRefreshSubscription: PropTypes.func.isRequired,
isLogged: PropTypes.bool.isRequired,
- onSubscribe: PropTypes.func.isRequired
+ onSubscribe: PropTypes.func.isRequired,
+ onSubscribeCancel: PropTypes.func.isRequired,
+ onPaypalApprove: PropTypes.func.isRequired
};
const useStyles = makeStyles({
@@ -63,10 +66,12 @@ const SubscriptionPlans = ({
subscription,
onRefreshSubscription,
isLogged,
- onSubscribe
+ onSubscribe,
+ onSubscribeCancel,
+ onPaypalApprove
}) => {
const {
- androidSubscriptionState,
+ status,
expiryDate,
error,
isOnTrialPeriod,
@@ -74,25 +79,28 @@ const SubscriptionPlans = ({
products
} = subscription;
+ let plans = [];
+ console.log(products);
+ if (!isAndroid() && products) {
+ products.forEach(product => {
+ if (product.paypalId) plans.push(product);
+ });
+ } else {
+ plans = products;
+ }
+
const classes = useStyles();
const canPurchase = [NOT_SUBSCRIBED, EXPIRED, ON_HOLD].includes(
- subscription.androidSubscriptionState
+ subscription.status
);
const subscriptionStatus = (function() {
- if (isAndroid()) {
- if (error.showError) return ERROR;
- if (
- isOnTrialPeriod &&
- !isSubscribed &&
- androidSubscriptionState !== PROCCESING
- )
- return ON_TRIAL_PERIOD;
- if (products.length || androidSubscriptionState !== NOT_SUBSCRIBED)
- return androidSubscriptionState || NOT_SUBSCRIBED;
- return EMPTY_PRODUCT;
- }
- return NOT_SUBSCRIBED;
+ if (error.showError) return ERROR;
+ if (isOnTrialPeriod && !isSubscribed && status !== PROCCESING)
+ return ON_TRIAL_PERIOD;
+ if (products.length || status !== NOT_SUBSCRIBED)
+ return status || NOT_SUBSCRIBED;
+ return EMPTY_PRODUCT;
})();
const alertProps = {
@@ -110,6 +118,31 @@ const SubscriptionPlans = ({
expired: 'warning' //TODO
};
+ const paypalButtonsStyle = {
+ layout: 'horizontal',
+ color: 'blue',
+ shape: 'rect',
+ label: 'subscribe',
+ tagline: false
+ };
+
+ const onPaypalAction = (action, product, data = '') => {
+ if (action === 'onClick') {
+ console.log('onClick');
+ console.log(data);
+ onSubscribe(product, data);
+ }
+ if (action === 'onCancel' || action === 'onError') {
+ console.log('onCancel');
+ console.log(data);
+ onSubscribeCancel(product, data);
+ }
+ if (action === 'onApprove') {
+ console.log('onApprove');
+ onPaypalApprove(product, data);
+ }
+ };
+
const fallbabackMessage = {
id: 'cboard.components.Settings.Subscribe.fallback',
defaultMessage: 'Wait please...'
@@ -157,7 +190,7 @@ const SubscriptionPlans = ({
alignItems="center"
justifyContent="space-around"
>
- {products.map(product => {
+ {plans.map(product => {
return [
-
+ {isAndroid() && (
+
+ )}
+ {!isCordova() && (
+ {
+ return actions.subscription.create({
+ plan_id: product.paypalId
+ });
+ }}
+ onClick={function(data, actions) {
+ onPaypalAction('onClick', product, data);
+ }}
+ onApprove={function(data, actions) {
+ // actions.subscription.get().then(details=>{
+ // console.log(details);
+ // });
+ onPaypalAction('onApprove', product, data);
+ }}
+ onCancel={function(data, actions) {
+ onPaypalAction('onCancel', product, data);
+ }}
+ onError={function(data, actions) {
+ onPaypalAction('onError', product, data);
+ }}
+ />
+ )}
diff --git a/src/index.js b/src/index.js
index e6fc82311..0fec39013 100644
--- a/src/index.js
+++ b/src/index.js
@@ -6,6 +6,7 @@ import { BrowserRouter, HashRouter, Route } from 'react-router-dom';
import { TouchBackend } from 'react-dnd-touch-backend';
import { DndProvider } from 'react-dnd';
import { PersistGate } from 'redux-persist/es/integration/react';
+import { PayPalScriptProvider } from '@paypal/react-paypal-js';
import App from './components/App';
import { isCordova, onCordovaReady, initCordovaPlugins } from './cordova-util';
@@ -56,6 +57,15 @@ const dndOptions = {
// When running in Cordova, must use the HashRouter
const PlatformRouter = isCordova() ? HashRouter : BrowserRouter;
+// PayPal configuration
+const paypalOptions = {
+ 'client-id':
+ 'AZ2vK0luRWMX9zzwLs-Ko_B_TJxeHYvIFCgXWcNBt50wmj7oZcUw8n4cf11GgdClTVnYMuEs5vRnxVEk',
+ currency: 'USD',
+ vault: true,
+ intent: 'subscription'
+};
+
const renderApp = () => {
if (isCordova()) {
initCordovaPlugins();
@@ -63,19 +73,21 @@ const renderApp = () => {
ReactDOM.render(
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
,
document.getElementById('root')
diff --git a/src/providers/SubscriptionProvider/SubscriptionProvider.actions.js b/src/providers/SubscriptionProvider/SubscriptionProvider.actions.js
index 13e7a36ee..f20447242 100644
--- a/src/providers/SubscriptionProvider/SubscriptionProvider.actions.js
+++ b/src/providers/SubscriptionProvider/SubscriptionProvider.actions.js
@@ -8,6 +8,7 @@ import {
REQUIRING_PREMIUM_COUNTRIES,
ACTIVE,
CANCELED,
+ CANCELLED,
IN_GRACE_PERIOD
} from './SubscriptionProvider.constants';
import API from '../../api';
@@ -59,23 +60,26 @@ export function updateIsSubscribed() {
return async (dispatch, getState) => {
let isSubscribed = false;
let ownedProduct = '';
- let androidSubscriptionState = NOT_SUBSCRIBED;
+ let status = NOT_SUBSCRIBED;
try {
const state = getState();
if (!isLogged(state)) {
dispatch(
updateSubscription({
ownedProduct,
- androidSubscriptionState,
+ status,
isSubscribed
})
);
} else {
const userId = state.app.userData.id;
- const { status, product } = await API.getSubscriber(userId);
+ const { status, product, transaction } = await API.getSubscriber(
+ userId
+ );
isSubscribed =
status.toLowerCase() === ACTIVE ||
status.toLowerCase() === CANCELED ||
+ status.toLowerCase() === CANCELLED ||
status.toLowerCase() === IN_GRACE_PERIOD
? true
: false;
@@ -86,25 +90,39 @@ export function updateIsSubscribed() {
price: product.price,
subscriptionId: product.subscriptionId,
tag: product.tag,
- title: product.title
+ title: product.title,
+ paypalSubscriptionId: transaction ? transaction.subscriptionId : ''
};
}
+ if (transaction && transaction.expiryDate) {
+ dispatch(
+ updateSubscription({
+ expiryDate: transaction.expiryDate
+ })
+ );
+ }
dispatch(
updateSubscription({
ownedProduct,
- androidSubscriptionState: status.toLowerCase(),
+ status: status.toLowerCase(),
isSubscribed
})
);
}
} catch (err) {
console.error(err.message);
- isSubscribed = false;
- dispatch(
- updateSubscription({
- isSubscribed
- })
- );
+ if (err.response?.data.error === 'subscriber not found') {
+ let isSubscribed = false;
+ let ownedProduct = '';
+ let status = NOT_SUBSCRIBED;
+ dispatch(
+ updateSubscription({
+ ownedProduct,
+ status,
+ isSubscribed
+ })
+ );
+ }
}
return isSubscribed;
};
@@ -127,7 +145,8 @@ export function updatePlans() {
billingPeriod: plan.period,
price: getPrice(plan.countries, locationCode),
title: plan.subscriptionName,
- tag: plan.tags[0]
+ tag: plan.tags[0],
+ paypalId: plan.paypalId
};
return result;
});
diff --git a/src/providers/SubscriptionProvider/SubscriptionProvider.constants.js b/src/providers/SubscriptionProvider/SubscriptionProvider.constants.js
index 860672022..3138f3f19 100644
--- a/src/providers/SubscriptionProvider/SubscriptionProvider.constants.js
+++ b/src/providers/SubscriptionProvider/SubscriptionProvider.constants.js
@@ -11,6 +11,7 @@ export const NOT_SUBSCRIBED = 'not_subscribed';
export const PROCCESING = 'proccesing';
export const ACTIVE = 'active';
export const CANCELED = 'canceled';
+export const CANCELLED = 'cancelled';
export const IN_GRACE_PERIOD = 'in_grace_period';
export const PAUSED = 'paused';
export const EXPIRED = 'expired';
diff --git a/src/providers/SubscriptionProvider/SubscriptionProvider.container.js b/src/providers/SubscriptionProvider/SubscriptionProvider.container.js
index 09d9f47f8..996be5c6e 100644
--- a/src/providers/SubscriptionProvider/SubscriptionProvider.container.js
+++ b/src/providers/SubscriptionProvider/SubscriptionProvider.container.js
@@ -38,40 +38,36 @@ export class SubscriptionProvider extends Component {
updatePlans
} = this.props;
- if (isAndroid()) {
+ const isSubscribed = await updateIsSubscribed();
+ const isInFreeCountry = updateIsInFreeCountry();
+ const isOnTrialPeriod = updateIsOnTrialPeriod();
+ await updatePlans();
+ if (isAndroid()) this.configInAppPurchasePlugin();
+ onAndroidResume(async () => {
+ await updateIsSubscribed();
+ updateIsInFreeCountry();
+ updateIsOnTrialPeriod();
+ });
+ if (!isInFreeCountry && !isOnTrialPeriod && !isSubscribed && isLogged) {
+ showPremiumRequired({ showTryPeriodFinishedMessages: true });
+ }
+ }
+
+ componentDidUpdate = async prevProps => {
+ const {
+ isLogged,
+ updateIsSubscribed,
+ updateIsInFreeCountry,
+ updateIsOnTrialPeriod
+ } = this.props;
+ if (prevProps.isLogged !== isLogged) {
const isSubscribed = await updateIsSubscribed();
const isInFreeCountry = updateIsInFreeCountry();
const isOnTrialPeriod = updateIsOnTrialPeriod();
- await updatePlans();
- this.configInAppPurchasePlugin();
- onAndroidResume(async () => {
- await updateIsSubscribed();
- updateIsInFreeCountry();
- updateIsOnTrialPeriod();
- });
if (!isInFreeCountry && !isOnTrialPeriod && !isSubscribed && isLogged) {
showPremiumRequired({ showTryPeriodFinishedMessages: true });
}
}
- }
-
- componentDidUpdate = async prevProps => {
- if (isAndroid()) {
- const {
- isLogged,
- updateIsSubscribed,
- updateIsInFreeCountry,
- updateIsOnTrialPeriod
- } = this.props;
- if (prevProps.isLogged !== isLogged) {
- const isSubscribed = await updateIsSubscribed();
- const isInFreeCountry = updateIsInFreeCountry();
- const isOnTrialPeriod = updateIsOnTrialPeriod();
- if (!isInFreeCountry && !isOnTrialPeriod && !isSubscribed && isLogged) {
- showPremiumRequired({ showTryPeriodFinishedMessages: true });
- }
- }
- }
};
configPurchaseValidator = () => {
@@ -116,18 +112,18 @@ export class SubscriptionProvider extends Component {
};
configInAppPurchasePlugin = () => {
- const { updateSubscription, androidSubscriptionState } = this.props;
+ const { updateSubscription, status } = this.props;
this.configPurchaseValidator();
window.CdvPurchase.store
.when()
.productUpdated(product => {
- if (androidSubscriptionState === PROCCESING) {
+ if (status === PROCCESING) {
updateSubscription({
isSubscribed: false,
expiryDate: null,
- androidSubscriptionState: NOT_SUBSCRIBED
+ status: NOT_SUBSCRIBED
});
}
})
@@ -159,7 +155,7 @@ const mapStateToProps = state => ({
isInFreeCountry: state.subscription.isInFreeCountry,
isSubscribed: state.subscription.isSubscribed,
expiryDate: state.subscription.expiryDate,
- androidSubscriptionState: state.subscription.androidSubscriptionState,
+ status: state.subscription.status,
isOnTrialPeriod: state.subscription.isOnTrialPeriod,
isLogged: isLogged(state),
subscriberId: state.subscription.subscriberId
diff --git a/src/providers/SubscriptionProvider/SubscriptionProvider.reducer.js b/src/providers/SubscriptionProvider/SubscriptionProvider.reducer.js
index b6b9f208e..530cf88fa 100644
--- a/src/providers/SubscriptionProvider/SubscriptionProvider.reducer.js
+++ b/src/providers/SubscriptionProvider/SubscriptionProvider.reducer.js
@@ -13,7 +13,7 @@ import {
const initialState = {
subscriberId: '',
- androidSubscriptionState: NOT_SUBSCRIBED,
+ status: NOT_SUBSCRIBED,
isSubscribed: false,
expiryDate: null,
error: {
@@ -73,7 +73,7 @@ function subscriptionProviderReducer(state = initialState, action) {
return {
...state,
subscriberId: id,
- androidSubscriptionState: status,
+ status: status,
expiryDate: expiry
};
case LOGOUT: