Skip to content

Commit

Permalink
Merge pull request #1584 from tomivm/feature/ios-premium
Browse files Browse the repository at this point in the history
Feature/ios premium
  • Loading branch information
martinbedouret authored Dec 18, 2023
2 parents 3ef1026 + e19a773 commit e4ce4c0
Show file tree
Hide file tree
Showing 13 changed files with 303 additions and 55 deletions.
4 changes: 2 additions & 2 deletions src/components/Settings/Settings.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import FullScreenDialog from '../UI/FullScreenDialog';
import Paper from '@material-ui/core/Paper';
import UserIcon from '../UI/UserIcon';
import SettingsTour from './SettingsTour.component';
import { isCordova, isAndroid, isIOS } from '../../cordova-util';
import { isCordova, isAndroid } from '../../cordova-util';

import './Settings.css';
import { CircularProgress } from '@material-ui/core';
Expand Down Expand Up @@ -96,7 +96,7 @@ export class Settings extends PureComponent {
}
];

if (!isIOS() && !isInFreeCountry) {
if (!isInFreeCountry) {
const subscribeSection = {
icon: <MonetizationOnIcon />,
text: messages.subscribe,
Expand Down
13 changes: 10 additions & 3 deletions src/components/Settings/Subscribe/Subscribe.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import './Subscribe.css';

import SubscriptionInfo from './SubscriptionInfo';
import SubscriptionPlans from './SubscriptionPlans';
import { CircularProgress } from '@material-ui/core';

const propTypes = {
/**
Expand All @@ -29,7 +30,8 @@ const propTypes = {
onRefreshSubscription: PropTypes.func,
onSubscribeCancel: PropTypes.func.isRequired,
onCancelSubscription: PropTypes.func.isRequired,
cancelSubscriptionStatus: PropTypes.string.isRequired
cancelSubscriptionStatus: PropTypes.string.isRequired,
updatingStatus: PropTypes.bool.isRequired
};

const defaultProps = {
Expand All @@ -46,7 +48,8 @@ const Subscribe = ({
onSubscribeCancel,
onPaypalApprove,
onCancelSubscription,
cancelSubscriptionStatus
cancelSubscriptionStatus,
updatingStatus
}) => {
return (
<div className="Subscribe">
Expand All @@ -56,7 +59,11 @@ const Subscribe = ({
onClose={onClose}
// fullWidth
>
{!subscription.isSubscribed ? (
{updatingStatus ? (
<div className="Subscribe__Loading__Container">
<CircularProgress />
</div>
) : !subscription.isSubscribed ? (
<SubscriptionPlans
subscription={subscription}
onRefreshSubscription={onRefreshSubscription}
Expand Down
1 change: 1 addition & 0 deletions src/components/Settings/Subscribe/Subscribe.constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export const EMPTY_PRODUCT = 'empty_product';
export const ON_TRIAL_PERIOD = 'on_trial_period';
export const GOOGLE_PLAY_STORE_URL =
'https://play.google.com/store/account/subscriptions';
export const APP_STORE_URL = 'https://www.apple.com/app-store/';
101 changes: 75 additions & 26 deletions src/components/Settings/Subscribe/Subscribe.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getUser, isLogged } from '../../App/App.selectors';
import API from '../../../api';
import messages from './Subscribe.messages';

import { isAndroid } from '../../../cordova-util';
import { isAndroid, isIOS } from '../../../cordova-util';
import {
updateSubscriberId,
updateSubscription,
Expand All @@ -34,14 +34,17 @@ export class SubscribeContainer extends PureComponent {
};

state = {
cancelSubscriptionStatus: ''
cancelSubscriptionStatus: '',
updatingStatus: true
};

componentDidMount() {
async componentDidMount() {
const { updateIsSubscribed, updatePlans } = this.props;
const requestOrigin =
'Function: componentDidMount - Component: Subscribe Container';
updateIsSubscribed(requestOrigin);
this.setState({ updatingStatus: true });
await updateIsSubscribed(requestOrigin);
this.setState({ updatingStatus: false });
updatePlans();
}

Expand Down Expand Up @@ -195,7 +198,7 @@ export class SubscribeContainer extends PureComponent {

let localReceipts = '';
let offers, offer;
if (isAndroid()) {
if (isAndroid() || isIOS()) {
const storeProducts = await window.CdvPurchase.store.products;
const prod = storeProducts.find(p => {
return p.id === product.subscriptionId;
Expand All @@ -207,14 +210,22 @@ export class SubscribeContainer extends PureComponent {
try {
await window.CdvPurchase.store.update();
offers = prod.offers;
offer = offers.find(offer => offer.tags[0] === product.tag);
const findCallback = isAndroid()
? offer => offer.tags[0] === product.tag
: offer => offer.productId === product.subscriptionId;
offer = offers.find(findCallback);
} catch (err) {
console.error('Cannot subscribe product. Error: ', err.message);
this.handleError(err);
return;
}
}

const filterInAppPurchaseIOSTransactions = uniqueReceipt =>
uniqueReceipt.transactions.filter(
transaction => transaction.transactionId !== 'appstore.application'
);

try {
// update the api
const requestOrigin =
Expand All @@ -225,40 +236,77 @@ export class SubscribeContainer extends PureComponent {
// check if current subscriber already bought in this device
if (localReceipts.length) {
const lastReceipt = localReceipts.slice(-1)[0];
if (
lastReceipt &&
lastReceipt?.transactions[0]?.nativePurchase?.orderId !==
subscriber.transaction?.transactionId
) {
this.handleError({
code: '0001',
message: intl.formatMessage(messages.googleAccountAlreadyOwns)
});
return;
if (isAndroid()) {
if (
lastReceipt &&
lastReceipt?.transactions[0]?.nativePurchase?.orderId !==
subscriber.transaction?.transactionId
) {
this.handleError({
code: '0001',
message: intl.formatMessage(messages.googleAccountAlreadyOwns)
});
return;
}
}
if (isIOS()) {
//IOS have a unique receipt here => 'lastReceipt'
const inAppPurchaseTransactions = filterInAppPurchaseIOSTransactions(
localReceipts[0]
);

const lastTransaction = inAppPurchaseTransactions.slice(-1)[0];
if (
inAppPurchaseTransactions.length > 0 &&
lastTransaction?.transactionId !==
subscriber.transaction?.transactionId
) {
this.handleError({
code: '0001',
message: intl.formatMessage(messages.appleAccountAlreadyOwns)
});
return;
}
}
}

await API.updateSubscriber(apiProduct);

// proceed with the purchase
if (isAndroid()) {
if (isAndroid() || isIOS()) {
const order = await window.CdvPurchase.store.order(offer);
if (order && order.isError) throw order;
updateSubscription({
ownedProduct: {
...product,
platform: 'android-playstore'
platform: isAndroid() ? 'android-playstore' : 'app-store'
}
});
}
} catch (err) {
if (err.response?.data.error === 'subscriber not found') {
// check if current subscriber already bought in this device
if (localReceipts.length) {
this.handleError({
code: '0001',
message: intl.formatMessage(messages.googleAccountAlreadyOwns)
});
return;
if (isAndroid()) {
if (localReceipts.length) {
this.handleError({
code: '0001',
message: intl.formatMessage(messages.googleAccountAlreadyOwns)
});
return;
}
}
if (isIOS()) {
const localInAppPurchaseTransactions = filterInAppPurchaseIOSTransactions(
localReceipts[0]
);

if (localInAppPurchaseTransactions.length) {
this.handleError({
code: '0001',
message: intl.formatMessage(messages.appleAccountAlreadyOwns)
});
return;
}
}
try {
const newSubscriber = {
Expand All @@ -269,13 +317,13 @@ export class SubscribeContainer extends PureComponent {
};
const res = await API.createSubscriber(newSubscriber);
updateSubscriberId(res._id);
if (isAndroid()) {
if (isAndroid() || isIOS()) {
const order = await window.CdvPurchase.store.order(offer);
if (order && order.isError) throw order;
updateSubscription({
ownedProduct: {
...product,
platform: 'android-playstore'
platform: isAndroid() ? 'android-playstore' : 'app-store'
}
});
}
Expand Down Expand Up @@ -306,6 +354,7 @@ export class SubscribeContainer extends PureComponent {
onPaypalApprove={this.handlePaypalApprove}
onCancelSubscription={this.handleCancelSubscription}
cancelSubscriptionStatus={this.state.cancelSubscriptionStatus}
updatingStatus={this.state.updatingStatus}
/>
);
}
Expand Down
7 changes: 7 additions & 0 deletions src/components/Settings/Subscribe/Subscribe.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
.Subscribe__Loading__Container {
padding: 1em;
height: 92vh;
display: flex;
justify-content: center;
align-items: center;
}
.Subscribe__Alert {
margin: 8px 0;
}
Expand Down
10 changes: 10 additions & 0 deletions src/components/Settings/Subscribe/Subscribe.messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ export default defineMessages({
id: 'cboard.components.Settings.Subscribe.not_subscribed',
defaultMessage: 'You are not subscribed. '
},
unverified: {
id: 'cboard.components.Settings.Subscribe.unverified',
defaultMessage:
'An error occurred during validation of your purchase. Please refresh'
},
refresh: {
id: 'cboard.components.Settings.Subscribe.refresh',
defaultMessage: 'Refresh'
Expand Down Expand Up @@ -148,6 +153,11 @@ export default defineMessages({
defaultMessage:
'It looks like your Google account has already purchased a product. Try restarting the app.'
},
appleAccountAlreadyOwns: {
id: 'cboard.components.Settings.Subscribe.appleAccountAlreadyOwns',
defaultMessage:
'It looks like your Apple account has already purchased a product. Try restarting the app.'
},
fallback: {
id: 'cboard.components.Settings.Subscribe.fallback',
defaultMessage: 'Wait please...'
Expand Down
15 changes: 12 additions & 3 deletions src/components/Settings/Subscribe/SubscriptionInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import {

import RefreshIcon from '@material-ui/icons/Refresh';
import IconButton from '../../UI/IconButton';
import { isAndroid, isElectron } from '../../../cordova-util';
import { GOOGLE_PLAY_STORE_URL } from './Subscribe.constants';
import { isAndroid, isElectron, isIOS } from '../../../cordova-util';
import { APP_STORE_URL, GOOGLE_PLAY_STORE_URL } from './Subscribe.constants';

const propTypes = {
ownedProduct: PropTypes.object.isRequired,
Expand Down Expand Up @@ -131,13 +131,15 @@ const SubscriptionInfo = ({
</div>
<div className="Subscribe__Info__Button__Container">
<Button
variant={isAndroid() ? 'contained' : 'text'}
variant={isAndroid() || isIOS() ? 'contained' : 'text'}
fullWidth={false}
color="primary"
disabled={ownedProduct.platform === 'paypal' && status !== ACTIVE}
onClick={() => {
if (isAndroid() && ownedProduct.platform === 'android-playstore')
window.CdvPurchase.store.manageSubscriptions();
if (isIOS() && ownedProduct.platform === 'ios-appstore')
window.CdvPurchase.store.manageSubscriptions();
if (ownedProduct.platform === 'paypal') setCancelDialog(true);
if (!isAndroid() && ownedProduct.platform === 'android-playstore') {
if (isElectron()) {
Expand All @@ -148,6 +150,13 @@ const SubscriptionInfo = ({
window.open(GOOGLE_PLAY_STORE_URL, '_blank');
}
}
if (!isIOS() && ownedProduct.platform === 'ios-appstore') {
if (isElectron()) {
window.cordova.plugins.DefaultBrowser.open(APP_STORE_URL);
} else {
window.open(APP_STORE_URL, '_blank');
}
}
}}
style={{ marginLeft: '1em' }}
>
Expand Down
14 changes: 9 additions & 5 deletions src/components/Settings/Subscribe/SubscriptionPlans.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
ON_TRIAL_PERIOD
} from './Subscribe.constants';
import { formatDuration, formatTitle } from './Subscribe.helpers';
import { isAndroid, isCordova, isElectron } from '../../../cordova-util';
import { isAndroid, isCordova, isElectron, isIOS } from '../../../cordova-util';
import { CircularProgress } from '@material-ui/core';

import { Link } from 'react-router-dom';
Expand All @@ -26,7 +26,8 @@ import {
EXPIRED,
NOT_SUBSCRIBED,
PROCCESING,
ON_HOLD
ON_HOLD,
UNVERIFIED
} from '../../../providers/SubscriptionProvider/SubscriptionProvider.constants';
import Button from '@material-ui/core/Button';
import List from '@material-ui/core/List';
Expand Down Expand Up @@ -80,7 +81,7 @@ const SubscriptionPlans = ({
} = subscription;

let plans = [];
if (!isAndroid() && products) {
if (!(isAndroid() || isIOS()) && products) {
products.forEach(product => {
if (product.paypalId) plans.push(product);
});
Expand All @@ -95,6 +96,7 @@ const SubscriptionPlans = ({

const subscriptionStatus = (function() {
if (error.showError) return ERROR;
if (status === UNVERIFIED) return UNVERIFIED;
if (isOnTrialPeriod && !isSubscribed && status !== PROCCESING)
return ON_TRIAL_PERIOD;
if (products.length || status !== NOT_SUBSCRIBED)
Expand All @@ -111,6 +113,7 @@ const SubscriptionPlans = ({
error: 'error',
empty_product: 'warning',
on_trial_period: 'info',
unverified: 'warning',

on_hold: 'warning', //TODO
paused: 'info', //TODO
Expand Down Expand Up @@ -144,7 +147,8 @@ const SubscriptionPlans = ({
const getMessage = () => {
function errorMessage() {
if (error && error.code === '0001') {
return messages.googleAccountAlreadyOwns;
if (isAndroid()) return messages.googleAccountAlreadyOwns;
if (isIOS()) return messages.appleAccountAlreadyOwns;
}
return messages.error;
}
Expand Down Expand Up @@ -228,7 +232,7 @@ const SubscriptionPlans = ({
/{formatDuration(product.billingPeriod)}
</Typography>
</Box>
{isAndroid() && (
{(isAndroid() || isIOS()) && (
<Button
variant="contained"
fullWidth={true}
Expand Down
Loading

0 comments on commit e4ce4c0

Please sign in to comment.