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 = ({
- - - + + + + + + + + + + + + + + {(cancelSubscriptionStatus === 'ok' || + cancelSubscriptionStatus === 'error') && ( + + {cancelSubscriptionStatus === 'ok' && ( + + + + )} + {cancelSubscriptionStatus === 'error' && ( + + + + )} + + )} +
]; }; -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: