Commit a3421bba authored by Martin Santangelo's avatar Martin Santangelo

(chore) remove old stripe code from wire

1 merge request!325WIP: [Sprint/ModestMonkey] wire epic 37
......@@ -19,8 +19,6 @@ import keychain from './src/keychain/KeychainStore';
import blockchainTransaction from './src/blockchain/transaction-modal/BlockchainTransactionStore';
import blockchainWallet from './src/blockchain/wallet/BlockchainWalletStore';
import blockchainWalletSelector from './src/blockchain/wallet/BlockchainWalletSelectorStore';
import payments from './src/payments/PaymentsStore';
import checkoutModal from './src/payments/checkout/CheckoutModalStore';
import capture from './src/capture/CaptureStore';
import withdraw from './src/wallet/tokens/WithdrawStore';
import hashtag from './src/common/stores/HashtagStore';
......@@ -55,8 +53,6 @@ const stores = {
blockchainWallet: new blockchainWallet(),
blockchainWalletSelector: new blockchainWalletSelector(),
channelSubscribersStore: new channelSubscribersStore(),
payments: new payments(),
checkoutModal: new checkoutModal(),
capture: new capture(),
withdraw: new withdraw(),
hashtag: new hashtag(),
......
......@@ -11,7 +11,6 @@ let dispose;
const DEFAULT_OPTS = {
signable: false,
offchain: false,
buyable: false,
};
/**
......
......@@ -167,7 +167,7 @@ export class BlockchainWalletService {
async getCurrent(onlyWithPrivateKey = false) {
if (!this.current || (!this.current.privateKey && onlyWithPrivateKey)) {
const payload = await this.selectCurrent(i18n.t('blockchain.selectTheWallet'), { signable: onlyWithPrivateKey, offchain: false, buyable: false });
const payload = await this.selectCurrent(i18n.t('blockchain.selectTheWallet'), { signable: onlyWithPrivateKey, offchain: false});
if (payload && payload.type === 'onchain') {
this.current = payload.wallet;
......
......@@ -22,7 +22,7 @@ class BlockchainWalletStore {
.filter(wallet => !!wallet.privateKey);
}
getList(signableOnly, allowOffchain, allowCreditCard) {
getList(signableOnly, allowOffchain) {
const wallets = (signableOnly ? this.signableWallets : this.wallets).slice();
if (allowOffchain) {
......@@ -33,14 +33,6 @@ class BlockchainWalletStore {
})
}
/*if (allowCreditCard) {
wallets.push({
address: 'creditcard',
alias: 'Credit Card',
creditcard: true
})
}*/
return wallets;
}
......
......@@ -61,7 +61,6 @@ export default class BlockchainWalletList extends Component {
const wallets = this.props.blockchainWallet.getList(
this.props.signableOnly,
this.props.allowOffchain,
this.props.allowCreditCard
);
return (
......
......@@ -22,7 +22,7 @@ import currency from '../../../common/helpers/currency';
import BlockchainApiService from '../../BlockchainApiService';
import i18n from '../../../common/services/i18n.service';
@inject('blockchainWallet', 'blockchainWalletSelector', 'checkoutModal')
@inject('blockchainWallet', 'blockchainWalletSelector')
@observer
export default class BlockchainWalletModalScreen extends Component {
async select(wallet) {
......@@ -39,28 +39,6 @@ export default class BlockchainWalletModalScreen extends Component {
payload = {
type: 'offchain'
};
} else if (opts.buyable && wallet.address == 'creditcard') {
const ccOpts = {};
if (opts.confirmTokenExchange) {
try {
const usd = await BlockchainApiService.getRate('tokens') * opts.confirmTokenExchange;
ccOpts.confirmMessage = i18n.t('blockchain.walletConfirmMessage', {currency: currency(usd, 'usd'), tokens: currency(opts.confirmTokenExchange, 'tokens')});
} catch (e) {
ccOpts.confirmMessage = i18n.t('blockchain.walletNotDeterminedMessage', {tokens: currency(opts.confirmTokenExchange, 'tokens')});
}
}
const ccPayload = await this.props.checkoutModal.show(ccOpts);
if (ccPayload === null) {
return;
}
payload = {
type: 'creditcard',
token: ccPayload
}
} else {
if (opts.signable && !wallet.privateKey) {
return;
......@@ -139,7 +117,6 @@ export default class BlockchainWalletModalScreen extends Component {
onSelect={this.selectAction}
signableOnly={opts.signable}
allowOffchain={opts.offchain}
allowCreditCard={opts.buyable}
/>
</View>
......
......@@ -47,7 +47,7 @@ class VisibleError extends Error {
/**
* Boost Screen
*/
@inject('user', 'checkoutModal')
@inject('user')
export default class BoostScreen extends Component {
textInput = void 0;
......@@ -462,16 +462,6 @@ export default class BoostScreen extends Component {
}
break;
case 'usd':
const token = await this.props.checkoutModal.show();
if (!token) {
return;
}
nonce = token;
break;
default:
throw new Error('Not supported');
}
......
......@@ -36,7 +36,6 @@ import BlockchainWalletImportScreen from '../blockchain/wallet/import/Blockchain
import BlockchainWalletDetailsScreen from '../blockchain/wallet/details/BlockchainWalletDetailsScreen';
import ReportScreen from '../report/ReportScreen';
import MoreScreen from '../tabs/MoreScreen';
import CheckoutModalScreen from '../payments/checkout/CheckoutModalScreen';
import WithdrawScreen from '../wallet/tokens/WithdrawScreen';
import WalletOnboardingScreen from "../wallet/onboarding/WalletOnboardingScreen";
import ComingSoonScreen from '../static-views/ComingSoonScreen';
......@@ -186,12 +185,6 @@ const Stack = createStackNavigator({
More: {
screen: withErrorBoundaryScreen(MoreScreen)
},
CheckoutModal: {
screen: withErrorBoundaryScreen(CheckoutModalScreen),
navigationOptions: {
gesturesEnabled: false
},
},
Withdraw: {
screen: withErrorBoundaryScreen(WithdrawScreen)
},
......
import {
observable,
action,
observe
} from 'mobx';
import stripeFactory from 'stripe-client';
import api from '../common/services/api.service';
import mindsService from '../common/services/minds.service';
class PaymentsStore {
@observable inProgress = false;
@observable cards = [];
@action async load() {
try {
this.inProgress = true;
this.cards = [];
const response = await api.get(`api/v1/payments/stripe/cards`);
if (response.cards) {
this.cards = response.cards.map(card => ({
token: card.id,
brand: (card.brand || 'generic').toLowerCase(),
label: `${card.brand} **** ${card.last4} ${card.exp_month}/${card.exp_year}`
}));
}
} catch (e) {
console.error('PaymentsStore.load', e);
} finally {
this.inProgress = false;
}
}
async createCardToken(payload) {
const stripe = stripeFactory((await mindsService.getSettings()).stripe_key);
const card = await stripe.createToken({
card: payload
});
return card;
}
reset() {
this.inProgress = false;
this.cards = [];
}
}
export default PaymentsStore;
import React, { Component } from 'react';
import {
View,
Text,
TextInput,
Picker,
ActivityIndicator,
StyleSheet,
} from 'react-native';
import {
inject,
observer
} from 'mobx-react/native';
import TransparentButton from '../../common/components/TransparentButton';
import Colors from '../../styles/Colors';
import CardType from './CardTypeComponent';
import creditCardType from '../../common/helpers/credit-card-type';
import i18n from '../../common/services/i18n.service';
@inject('payments')
@observer
export default class CardInput extends Component {
state = {
number: '',
expMonth: '',
expYear: '',
cvc: '',
name: '',
inProgress: false,
}
componentWillMount() {
this.reset();
}
reset() {
const now = new Date(),
currentMonth = now.getMonth() + 1,
currentYear = now.getFullYear();
this.setState({
number: '',
expMonth: currentMonth,
expYear: currentYear,
cvc: '',
name: ''
});
}
validate() {
card = this.getCard();
if (!this.state.number) {
throw new Error('E_INVALID_CARD_NUMBER');
}
if (!this.state.expMonth || !this.state.expYear) {
throw new Error('E_INVALID_CARD_EXP_DATE');
}
if (!this.state.cvc) {
throw new Error('E_INVALID_CARD_CVC');
}
if (!this.state.name) {
throw new Error('E_INVALID_CARDHOLDER_NAME');
}
const now = new Date(),
exp = new Date(`${this.state.expYear}-${this.state.expMonth}-1 00:00`);
if (now.getTime() > exp.getTime()) {
throw new Error('E_EXPIRED_CARD');
}
return true;
}
canSubmit() {
try {
this.validate();
return true;
} catch (e) {
return false;
}
}
async submit() {
if (this.state.inProgress) {
return;
}
this.setState({ inProgress: true });
try {
const card = await this.props.payments.createCardToken({
number: this.state.number,
exp_month: this.state.expMonth,
exp_year: this.state.expYear,
cvc: this.state.cvc,
name: this.state.name,
});
const label = `${card.card.brand} **** ${card.card.last4} ${card.card.exp_month}/${card.card.exp_year}`;
this.props.onConfirm({ token: card.id, label });
this.reset();
} catch (e) { } finally {
this.setState({ inProgress: false });
}
}
getCard() {
return creditCardType(this.state.number);
}
//
setNumber = number => this.setState({ number: `${number}`.replace(/[^0-9]/g, '') });
getNumber() {
const card = this.getCard();
if (!card || !card.gaps) {
return this.state.number;
}
const characters = this.state.number.split('');
for (gap of card.gaps.reverse()) {
if (characters.length < gap) {
continue;
}
characters.splice(gap, 0, ' ');
}
return characters.join('').trim();
}
getMaxNumberLength() {
const card = this.getCard();
if (!card || !card.lengths) {
return 16;
}
let maxLength = Math.max(...card.lengths);
if (card.gaps) {
maxLength += card.gaps.length;
}
return maxLength;
}
setExpMonth = expMonth => this.setState({ expMonth });
setExpYear = expYear => this.setState({ expYear });
setCvc = cvc => this.setState({ cvc });
getCvcLabel() {
const card = this.getCard();
if (!card || !card.code) {
return 'CVC / CVV';
}
return card.code.name.toUpperCase();
}
getCvcLength() {
const card = this.getCard();
return (card && card.code && card.code.size) || 4;
}
setName = name => this.setState({ name });
// Partials
getNumberPartial() {
return (<View style={style.cell}>
<TextInput
style={style.textInput}
placeholder="1111 2222 3333 4444"
onChangeText={this.setNumber}
value={this.getNumber()}
disabled={this.props.inProgress}
keyboardType="numeric"
maxLength={this.getMaxNumberLength()}
/>
</View>);
}
getExpDateSelectorPartial() {
const now = new Date();
const currentMonth = now.getMonth() + 1,
monthItems = Array(12).fill()
.map((value, i) => <Picker.Item key={i} label={`${i + 1}`} value={`${i + 1}`} />);
const currentYear = now.getFullYear(),
yearItems = Array(20).fill()
.map((value, i) => <Picker.Item key={i} label={`${i + currentYear}`} value={`${i + currentYear}`} />);
return (<View style={[style.cell, style.rowView]}>
<Picker
style={[style.cell, style.picker]}
itemStyle={style.pickerItem}
selectedValue={this.state.expMonth}
onValueChange={this.setExpMonth}
enabled={!this.props.inProgress}
prompt={i18n.t('payments.cardExpirationMonth')}
>{monthItems}</Picker>
<Picker
style={[style.cell, style.picker]}
itemStyle={style.pickerItem}
selectedValue={this.state.expYear}
onValueChange={this.setExpYear}
enabled={!this.props.inProgress}
prompt={i18n.t('payments.cardExpirationYear')}
>{yearItems}</Picker>
</View>);
}
getCvcPartial() {
return (<View style={style.cell}>
<TextInput
style={style.textInput}
secureTextEntry={true}
placeholder={this.getCvcLabel()}
onChangeText={this.setCvc}
value={this.state.cvc}
disabled={this.props.inProgress}
keyboardType="numeric"
maxLength={this.getCvcLength()}
/>
</View>);
}
getCardholderNamePartial() {
return (<View style={style.cell}>
<TextInput
style={style.textInput}
placeholder={i18n.t('payments.cardHoldersName')}
onChangeText={this.setName}
value={this.state.name}
disabled={this.props.inProgress}
autoCapitalize="words"
/>
</View>);
}
submitAction = () => this.submit();
//
render() {
return (
<View style={[style.wrapper, this.props.style]}>
<View style={style.rowView}>
{this.getNumberPartial()}
</View>
<View style={style.rowView}>
{this.getExpDateSelectorPartial()}
{this.getCvcPartial()}
</View>
<View style={style.rowView}>
{this.getCardholderNamePartial()}
</View>
<View style={style.rowView}>
<View style={style.cell}>
{!!this.state.number && <CardType
number={this.state.number}
color={colors.primary}
style={style.cardType}
labelStyle={style.cardTypeLabel}
iconSize={20}
/>}
</View>
<View style={style.cell}>
<TransparentButton
color={this.canSubmit() ? Colors.primary : Colors.greyed}
disabled={!this.canSubmit()}
title={
!this.state.inProgress ?
i18n.t('confirm') :
(<ActivityIndicator size="small" color={Colors.primary} />)
}
onPress={this.submitAction}
/>
</View>
</View>
</View>
);
}
}
const style = StyleSheet.create({
wrapper: {
justifyContent: 'flex-start',
},
rowView: {
flexDirection: 'row',
alignItems: 'center'
},
cell: {
flexGrow: 1,
flexBasis: 0,
padding: 5,
},
textInput: {
borderWidth: 1,
borderColor: Colors.greyed,
borderRadius: 4,
padding: 8,
fontSize: 18,
textAlign: 'center'
},
picker: {
padding: 0,
margin: 0,
height: 44
},
pickerItem: {
padding: 0,
margin: 0,
height: 44,
fontSize: 16,
},
cardType: {
justifyContent: 'center'
},
cardTypeLabel: {
fontSize: 16
}
});
import React, { Component } from 'react';
import {
View,
Text,
FlatList,
StyleSheet,
} from 'react-native';
import {
inject,
observer
} from 'mobx-react/native';
import CardType from './CardTypeComponent';
import Touchable from '../../common/components/Touchable';
import Colors from '../../styles/Colors';
@inject('payments')
@observer
export default class CardList extends Component {
componentWillMount() {
this.props.payments.load();
}
ItemPartial = ({ item, index }) => {
if (!item) {
return null;
}
return (
<Touchable
style={[style.itemWrapper, index === 0 && style.firstItemWrapper]}
onPress={() => this.props.onSelect(item)}
>
<View style={this.props.itemStyle}>
<Text style={style.itemText}>{item.label.toUpperCase()}</Text>
</View>
</Touchable>
);
};
keyExtractor = item => item.token;
render() {
return (
<FlatList
style={this.props.style}
data={this.props.payments.cards}
renderItem={this.ItemPartial}
keyExtractor={this.keyExtractor}
/>
);
}
}
const style = StyleSheet.create({
itemWrapper: {
paddingTop: 5,
paddingBottom: 5,
borderColor: Colors.greyed,
borderBottomWidth: 1,
},
firstItemWrapper: {
borderTopWidth: 1,
},
itemText: {
fontSize: 14,
letterSpacing: 1,
}
});
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import FaIcon from 'react-native-vector-icons/FontAwesome';
import { types } from 'credit-card-type';
import creditCardType from '../../common/helpers/credit-card-type';
export default class CardType extends Component {
getIcon(type) {
let icon;
switch (type) {
case types.VISA:
icon = 'cc-visa';
break;
case types.MASTERCARD:
case types.MAESTRO:
icon = 'cc-mastercard';
break;
case types.DISCOVER:
icon = 'cc-discover';
break;
case types.AMERICAN_EXPRESS:
icon = 'cc-amex';
break;
default:
icon = 'credit-card';
break;
}
return (<FaIcon style={style.icon} name={icon} color={this.props.color} size={this.props.iconSize || 16} />);
}
render() {
const card = creditCardType(this.props.number, true) ;
if (!card) {
return <View style={this.props.style} />
}
return (<View style={[style.view, this.props.style]}>
{this.getIcon(card.type)}
{card && <Text style={[this.props.color && { color: this.props.color }, this.props.labelStyle ]}>{ card.niceType }</Text>}
</View>);
}
}
const style = StyleSheet.create({
view: {
flexDirection: 'row',
alignItems: 'center',
},
icon: {
marginRight: 5,
}
});
import React, { Component } from 'react';
import {
View,
Text,
ScrollView,
Alert,
ActivityIndicator,
StyleSheet,
BackHandler,
} from 'react-native';
import {
inject,
observer
} from 'mobx-react/native';
import Colors from '../../styles/Colors';
import mindsService from '../../common/services/minds.service';
import CardList from './CardListComponent';
import CardInput from './CardInputComponent';
import { CommonStyle } from '../../styles/Common';
import i18n from '../../common/services/i18n.service';
@inject('checkoutModal', 'payments')
@observer
export default class CheckoutModalScreen extends Component {
disposeLeave;
componentWillMount() {
this.props.payments.load();
this.disposeLeave = this.props.navigation.addListener('didBlur', () => this.props.checkoutModal.cancel(false));
BackHandler.addEventListener("hardwareBackPress", this.onBackPress);
}
componentWillUnmount() {
if (this.disposeLeave) {
this.disposeLeave();
this.disposeLeave = void 0;
}
BackHandler.removeEventListener("hardwareBackPress", this.onBackPress);
}
onBackPress = e => {
this.props.checkoutModal.cancel();
return true;
};
onConfirmAction = async ({ label, token }) => {
Alert.alert(
label,
i18n.t('payments.cardConfirmMessage', {confirmMessage: this.props.checkoutModal.opts.confirmMessage}).trim(),
[
{ text: i18n.t('no'), style: 'cancel' },
{ text: i18n.t('yes'), onPress: () => this.props.checkoutModal.submit(token) }
]
);
};
getCardList() {
return (<View>
{!!this.props.payments.cards.length && <Text style={style.title}>{i18n.t('payments.yourSavedCard')}</Text>}
<CardList
style={style.cardList}
itemStyle={style.cardListItem}
onSelect={this.onConfirmAction}
/>
</View>);
}
getCardInput() {
if (this.props.payments.inProgress) {
return;
}
return (<View>
<Text style={[
style.title,
!!this.props.payments.cards.length && CommonStyle.marginTop3x
]}>{
!!this.props.payments.cards.length ?
i18n.t('payments.orUseAntoherCard') :
i18n.t('payments.enterCardDetails')
}</Text>
<CardInput
style={style.cardInput}
onConfirm={this.onConfirmAction}
/>
</View>);
}
render() {
return (<ScrollView style={style.view}>
{this.props.checkoutModal.isActive && <View>
{this.props.payments.inProgress && <ActivityIndicator size="large" />}
{this.getCardList()}
{this.getCardInput()}
</View>}
</ScrollView>)
}
}
const style = StyleSheet.create({
view: {
paddingTop: 10,
backgroundColor: '#fff'
},
title: {
fontSize: 12,
letterSpacing: 1,
color: Colors.darkGreyed,
marginLeft: 10,
marginRight: 10,
},
cardList: {
margin: 10,
marginTop: 5,
},
cardListItem: {
padding: 5
},
cardInput: {
margin: 10,
marginTop: 5,
padding: 5,
borderRadius: 8,
borderColor: Colors.greyed,
borderWidth: 1
}
});
\ No newline at end of file
import ModalControllerStore from '../../common/stores/ModalControllerStore';
class CheckoutModalStore extends ModalControllerStore {
route = 'CheckoutModal';
defaultOpts = {
confirmMessage: ''
};
}
export default CheckoutModalStore;
......@@ -101,7 +101,6 @@ class WireService {
{
signable: true,
offchain: opts.currency === 'tokens',
buyable: opts.currency === 'tokens',
confirmTokenExchange: opts.amount,
currency: opts.currency
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment