509 lines
21 KiB
JavaScript
509 lines
21 KiB
JavaScript
import * as React from 'react';
|
|
import { connect } from "react-redux";
|
|
import {
|
|
TouchableOpacity,
|
|
View,
|
|
Text,
|
|
Image,
|
|
Switch,
|
|
StyleSheet,
|
|
Modal,
|
|
Alert,
|
|
TextInput
|
|
} from 'react-native';
|
|
import { WebView } from 'react-native-webview';
|
|
import { returnIcon } from '../../utils/card';
|
|
import moment from 'moment';
|
|
import cardValidator from 'card-validator';
|
|
import REQUEST_POST_PAY from '../../components/api/postpayapi';
|
|
import CustomHeader from '../../components/header.js';
|
|
import Theme from '../../components/theme.style.js';
|
|
import Elements from '../../components/elements.js';
|
|
import Assets from '../../components/assets.manager.js';
|
|
import Cipher from '../../components/cardencrypt/encryptcard';
|
|
import DB from '../../components/storage/';
|
|
import CustomSafeArea from '../../components/safeArea.component';
|
|
|
|
class PayatpumpPaymentMethodList extends React.Component {
|
|
|
|
constructor(props) {
|
|
super(props)
|
|
}
|
|
|
|
state = {
|
|
cards: [],
|
|
cardNumber: null,
|
|
cardExpiry: null,
|
|
cardCVV: null,
|
|
cardZipcode: null,
|
|
customerName: `${this.props.userinfo.data.firstname} ${this.props.userinfo.data.lastname}`,
|
|
focus_holder: null,
|
|
focus_expiry: false,
|
|
focus_cvv: false,
|
|
focus_zipcode: false,
|
|
enableDefaultCard: false,
|
|
showModal: false,
|
|
pubkey: null,
|
|
verificationUrl: null,
|
|
cardDetails: null,
|
|
loading: false
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.getWalletKey()
|
|
}
|
|
|
|
getWalletKey = async () => {
|
|
this.setState({ loading: true })
|
|
REQUEST_POST_PAY('getWalletPublicKey', 'get', {}, {}, {}, async (res) => {
|
|
this.setState({ loading: false })
|
|
|
|
if(res.status) {
|
|
this.setState({ pubkey: res.data.publicKey })
|
|
} else {
|
|
setTimeout(() => {
|
|
Alert.alert("Information", '\n' + 'Failed to get fund. Try again.')
|
|
}, 300)
|
|
}
|
|
}, (error) => {
|
|
this.setState({ loading: false })
|
|
setTimeout(() => {
|
|
Alert.alert("Information", '\n' + 'Failed to get fund. Try again.')
|
|
}, 300)
|
|
})
|
|
}
|
|
|
|
addCard = async (cardDetails, publicKey, cardHolder, onSuccess, onError) => {
|
|
REQUEST_POST_PAY('getFunding', 'post', {}, {}, {}, async (res) => {
|
|
const cardNumber = this.state.cardNumber.replace(/ /g, "").toString();
|
|
const wallets = res.data.wallets.find(wallets => wallets.fundingProviderName === "p97token").wallets;
|
|
// const filteredWallets = wallets.find(wallet => cardNumber.includes(wallet.firstSix) && cardNumber.includes(wallet.lastFour));
|
|
|
|
// if(filteredWallets) {
|
|
// return onError("Card number already exist.")
|
|
// } else {
|
|
this.setState({ loading: false })
|
|
|
|
const firstSix = cardDetails.pan.slice(0, 6)
|
|
const lastSix = cardDetails.pan.slice(-4)
|
|
const expMonth = cardDetails.expDate.slice(0,2);
|
|
const expYear = cardDetails.expDate.slice(-2);
|
|
|
|
let params = {
|
|
first_six: firstSix,
|
|
last_four: lastSix,
|
|
cvv: cardDetails.cvv,
|
|
expire: `${expMonth}${expYear}`,
|
|
ecd: publicKey,
|
|
zip_code: cardDetails.zipcode
|
|
}
|
|
|
|
REQUEST_POST_PAY('addCreditCard', 'post', {}, {}, params, (res) => {
|
|
if(res.status) {
|
|
if(res.data.result) {
|
|
this.addCardProviderData(res.data.referenceNumber, cardHolder, onSuccessResponse => onSuccess(onSuccessResponse), onErrorResponse => onError(onErrorResponse))
|
|
}
|
|
} else {
|
|
onError('Failed to add new card. Try again.')
|
|
}
|
|
}, (error) => {
|
|
onError(error)
|
|
})
|
|
// }
|
|
}, () => {
|
|
onError("Something went wrong, please try again.")
|
|
})
|
|
}
|
|
|
|
addCardProviderData = async (referenceNumber, cardHolder, onSuccess, onError) => {
|
|
let params = {
|
|
referenceNumber: referenceNumber,
|
|
providerData: {
|
|
nickName: "Paymaya Visa",
|
|
firstName: cardHolder
|
|
}
|
|
}
|
|
|
|
const USER_PROFILE = await DB.profile()
|
|
REQUEST_POST_PAY('addCreditCard', 'post', {
|
|
token: USER_PROFILE.data.auth_p97
|
|
}, {}, params, (res) => {
|
|
if(res.success == undefined) {
|
|
onError(res.Message)
|
|
} else {
|
|
if(res.success) {
|
|
if(res.response.result) {
|
|
onSuccess({ referenceNumber: res.response.referenceNumber, verificationUrl: res.response.stepWiseResponsePayload.verificationUrl })
|
|
}
|
|
} else {
|
|
onError('Failed to add new card. Try again.')
|
|
}
|
|
}
|
|
}, (error) => {
|
|
onError(error)
|
|
})
|
|
}
|
|
|
|
_handlingCustomerName = (name) => {
|
|
this.setState({ customerName: name })
|
|
}
|
|
|
|
_handlingCardNumber = (number) => {
|
|
var value = number.replace(/\s+/g, '').replace(/[^0-9]/gi, '')
|
|
var matches = value.match(/\d{4,16}/g);
|
|
var match = matches && matches[0] || ''
|
|
var parts = []
|
|
for (let i = 0, len=match.length; i<len; i+=4) {
|
|
parts.push(match.substring(i, i+4))
|
|
}
|
|
var cardnumber = parts.length ? parts.join(' ') : number
|
|
|
|
this.setState({ cardNumber: cardnumber, cardDetails: cardValidator.number(cardnumber) });
|
|
}
|
|
|
|
_handlingCardExpiry = (number) => {
|
|
if (number.indexOf('.') >= 0 || number.length > 5) return
|
|
if (number.length === 2 && this.state.cardExpiry.length === 1) number += '/'
|
|
this.setState({ cardExpiry: number });
|
|
}
|
|
|
|
_handlingCVV = (number) => {
|
|
if (number.indexOf('.') >= 0 || number.length > 3) return
|
|
this.setState({ cardCVV: number });
|
|
}
|
|
|
|
_handlingZipCode = (number) => {
|
|
if (number.indexOf('.') >= 0 || number.length > 4) return
|
|
this.setState({ cardZipcode: number });
|
|
}
|
|
|
|
_onAuthenticationSuccess = async (referenceNumber, callback) => {
|
|
this.props.route?.params.onSetLoading();
|
|
|
|
let params = {
|
|
referenceNumber: referenceNumber,
|
|
}
|
|
|
|
this.setState({ loading: true })
|
|
const USER_PROFILE = await DB.profile()
|
|
REQUEST_POST_PAY('addCreditCard', 'post', {
|
|
token: USER_PROFILE.data.auth_p97
|
|
}, {}, params, (res) => {
|
|
console.log(res)
|
|
if(res.success == undefined) {
|
|
this.setState({ loading: false })
|
|
setTimeout(() => {
|
|
Alert.alert("Information", '\n' + res.Message)
|
|
}, 300)
|
|
} else {
|
|
if(res.success) {
|
|
this.setState({ loading: false })
|
|
this.props.route?.params.onAddNewCard({ userPaymentSourceId: res.response.userPaymentSourceId, referenceNumber: res.response.referenceNumber }, this.state.enableDefaultCard);
|
|
} else {
|
|
setTimeout(() => {
|
|
this.setState({ loading: false })
|
|
Alert.alert("Information", '\n' + res.Message);
|
|
}, 300)
|
|
}
|
|
}
|
|
}, (error) => {
|
|
this.setState({ loading: false })
|
|
setTimeout(() => {
|
|
Alert.alert("Information", '\n' + error)
|
|
callback();
|
|
}, 300)
|
|
})
|
|
}
|
|
|
|
submitNewAddedCard = () => {
|
|
if(this.state.cardNumber == null || this.state.cardExpiry == null || this.state.cardCVV == null) {
|
|
this.setState({ showModal: true })
|
|
return
|
|
}
|
|
|
|
let numberYear = moment(new Date()).format('YYYY').slice(0, 2) //get initial 2 digits of year
|
|
let cardnumber = this.state.cardNumber.replace(/\s/g, "")
|
|
let cardExpiry = `${this.state.cardExpiry.split("/")[0]}${numberYear}${this.state.cardExpiry.split("/")[1]}`
|
|
let cardCvv = this.state.cardCVV
|
|
let cardZipcode = this.state.cardZipcode
|
|
|
|
if(!cardValidator.number(cardnumber)?.card?.type || cardnumber.length < 16) {
|
|
Alert.alert("Information", '\n' + `Invalid card number. Please use different card type.`)
|
|
return
|
|
}
|
|
|
|
if(!cardValidator.expirationDate(cardExpiry).isValid) {
|
|
Alert.alert("Information", '\n' + "Invalid expiration date. Please check and try again.")
|
|
return
|
|
}
|
|
|
|
if(!cardValidator.cvv(cardCvv).isValid) {
|
|
Alert.alert("Information", '\n' + "Invalid cvv number. Please check and try again")
|
|
return
|
|
}
|
|
|
|
if(!cardValidator.postalCode(cardZipcode).isValid) {
|
|
Alert.alert("Information", '\n' + "Invalid zipcode number. Please check and try again")
|
|
return
|
|
}
|
|
|
|
|
|
let cardDetails = { pan: cardnumber, expDate: cardExpiry, cvv: cardCvv, zipcode: cardZipcode }
|
|
this.setState({ loading: true })
|
|
this.addCard(cardDetails, this.state.pubkey, cardValidator.number(cardnumber).card.niceType, onSuccess => {
|
|
setTimeout(() => {
|
|
this.props.navigation.navigate('VerificationWebview', {...onSuccess, onSuccessAuthentication:(referenceNumber, callback) => this._onAuthenticationSuccess(referenceNumber, callback)})
|
|
this.setState({ loading: false })
|
|
}, 500);
|
|
},
|
|
error => {
|
|
Alert.alert('Information', '\n' + error)
|
|
this.setState({ loading: false })
|
|
})
|
|
}
|
|
|
|
onBackConfirmation = () => {
|
|
Alert.alert(
|
|
'',
|
|
'Are you sure you want to cancel adding card?',
|
|
[
|
|
{
|
|
text: 'Cancel',
|
|
style: 'cancel',
|
|
},
|
|
{
|
|
text: 'OK',
|
|
onPress: () => this.props.navigation.goBack(),
|
|
},
|
|
],
|
|
{cancelable: true},
|
|
);
|
|
}
|
|
|
|
notEmpty = () => {
|
|
const { cardNumber, cardExpiry, cardCVV, customerName, cardZipcode } = this.state;
|
|
|
|
if(cardNumber && cardExpiry && cardCVV && customerName && cardZipcode) {
|
|
return true;
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
renderWebview = () => {
|
|
return (
|
|
<WebView
|
|
originWhitelist={['*']}
|
|
source={{ uri: this.state.data ? this.state.data?.redirectUrl : '' }}
|
|
style={{
|
|
backgroundColor: 'transparent',
|
|
width: Theme.screen.w,
|
|
height: Theme.screen.h,
|
|
padding: 15
|
|
}}
|
|
onNavigationStateChange={(webViewState) => this.onStateChange(webViewState)}/>
|
|
)
|
|
}
|
|
|
|
renderErrorModal = () => {
|
|
return (
|
|
<Modal
|
|
animationType="none"
|
|
transparent={true}
|
|
visible={this.state.showModal}>
|
|
<TouchableOpacity activeOpacity={1} onPress={() => {}} style={styles.centeredView}>
|
|
<View style={[styles.modalView, { backgroundColor: this.props.app_theme?.theme.colors.border }]}>
|
|
<>
|
|
<Image source={Assets.icons.points_balance} style={{ width: 85, height: 85, resizeMode: 'contain' }} />
|
|
<Text style={{ fontSize: 18, color: this.props.app_theme?.theme.colors.text, marginVertical: 30 }}>{'Please Fill in the required fields'}</Text>
|
|
<TouchableOpacity onPress={() => this.setState({ showModal: false })} style={{ width: 80, height: 30, backgroundColor: Theme.colors.primary, alignItems: 'center', justifyContent: 'center', borderRadius: 5 }}>
|
|
<Text style={{ fontSize: 18, color: Theme.colors.white, textAlign: 'center' }}>Ok</Text>
|
|
</TouchableOpacity>
|
|
</>
|
|
</View>
|
|
</TouchableOpacity>
|
|
</Modal>
|
|
)
|
|
}
|
|
|
|
renderCardForm = () => {
|
|
return (
|
|
<View style={{paddingHorizontal: 20, paddingTop: 40}}>
|
|
<Text style={{fontSize: 12, color: this.state.focus_number ? Theme.colors.primary : Theme.colors.searchGray, marginLeft: 15, marginBottom: 5}}>Card Number</Text>
|
|
<View style={[styles.cardContainer, this.state.focus_number && {borderBottomColor: Theme.colors.primary}]}>
|
|
<TextInput
|
|
keyboardType="numeric"
|
|
returnKeyType={'done'}
|
|
placeholder="Card Number"
|
|
placeholderTextColor={Theme.colors.gray}
|
|
value={this.state.cardNumber}
|
|
onFocus={() => this.setState({ focus_number: true })}
|
|
onBlur={() => this.setState({ focus_number: false })}
|
|
onChangeText={(value) => this._handlingCardNumber(value)}
|
|
style={{ flex: 1, color: this.props.app_theme?.theme.colors.text }}
|
|
/>
|
|
<Image source={returnIcon(this.state.cardDetails?.card?.niceType)} style={{ width: 30, height: 30, resizeMode: 'contain' }} />
|
|
</View>
|
|
|
|
<Text style={{fontSize: 12, color: this.state.focus_holder ? Theme.colors.primary : Theme.colors.searchGray, marginLeft: 15, marginTop: 10, marginBottom: 5}}>Name on Card</Text>
|
|
<View style={[styles.cardContainer, this.state.focus_holder && {borderBottomColor: Theme.colors.primary}]}>
|
|
<TextInput
|
|
returnKeyType={'done'}
|
|
placeholder="Customer Name"
|
|
placeholderTextColor={Theme.colors.gray}
|
|
value={this.state.customerName}
|
|
onFocus={() => this.setState({ focus_holder: true })}
|
|
onBlur={() => this.setState({ focus_holder: false })}
|
|
onChangeText={(value) => this._handlingCustomerName(value)}
|
|
style={{ flex: 1, color: this.props.app_theme?.theme.colors.text }}
|
|
/>
|
|
</View>
|
|
<View style={{flexDirection: 'row' }}>
|
|
<View style={[styles.expirationCvvContainer, this.state.focus_expiry && {borderBottomColor: Theme.colors.primary}, { flex: 1, marginLeft: 16, marginRight: 5 }]}>
|
|
<TextInput
|
|
keyboardType="numeric"
|
|
returnKeyType={'done'}
|
|
placeholder="Expiration(MM/YY)"
|
|
placeholderTextColor={Theme.colors.gray}
|
|
value={this.state.cardExpiry}
|
|
onFocus={() => this.setState({ focus_expiry: true })}
|
|
onBlur={() => this.setState({ focus_expiry: false })}
|
|
onChangeText={(value) => this._handlingCardExpiry(value)}
|
|
style={{ flex: 1, color: this.props.app_theme?.theme.colors.text, marginRight: 3 }}
|
|
/>
|
|
</View>
|
|
<View style={[styles.expirationCvvContainer, this.state.focus_cvv && {borderBottomColor: Theme.colors.primary}, { flex: 0.55, marginRight: 16, marginLeft: 5 }]}>
|
|
<TextInput
|
|
keyboardType="numeric"
|
|
returnKeyType={'done'}
|
|
placeholder="CVV"
|
|
placeholderTextColor={Theme.colors.gray}
|
|
value={this.state.cardCVV}
|
|
onFocus={() => this.setState({ focus_cvv: true })}
|
|
onBlur={() => this.setState({ focus_cvv: false })}
|
|
onChangeText={(value) => this._handlingCVV(value)}
|
|
style={{ flex: 1, color: this.props.app_theme?.theme.colors.text, marginLeft: 3 }}
|
|
/>
|
|
</View>
|
|
<View style={[styles.expirationCvvContainer, this.state.focus_zipcode && {borderBottomColor: Theme.colors.primary}, { flex: 0.55, marginRight: 16, marginLeft: 5 }]}>
|
|
<TextInput
|
|
keyboardType="numeric"
|
|
returnKeyType={'done'}
|
|
placeholder="Zipcode"
|
|
placeholderTextColor={Theme.colors.gray}
|
|
value={this.state.cardZipcode1}
|
|
onFocus={() => this.setState({ focus_zipcode: true })}
|
|
onBlur={() => this.setState({ focus_zipcode: false })}
|
|
onChangeText={(value) => this._handlingZipCode(value)}
|
|
style={{ flex: 1, color: this.props.app_theme?.theme.colors.text, marginLeft: 3 }}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
renderSwitch = () => {
|
|
return (
|
|
<View style={{flexDirection: 'row', paddingHorizontal: 35, marginTop: 20, alignItems: 'center', justifyContent: 'space-between'}}>
|
|
<Text style={{color: Theme.colors.gray}}>Set as primary card</Text>
|
|
<Switch
|
|
style={{ transform: [{scale: .8}]}}
|
|
value={this.state.enableDefaultCard}
|
|
trackColor={{true: Theme.colors.primary + 35, false: "gray"}}
|
|
thumbColor={this.state.enableDefaultCard ? Theme.colors.primary : "gray"}
|
|
onChange={() => this.setState({ enableDefaultCard: !this.state.enableDefaultCard })}
|
|
/>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<CustomSafeArea>
|
|
<CustomHeader
|
|
title={"Add Card"}
|
|
onBackPress={() => this.onBackConfirmation()}
|
|
back={true}
|
|
menu={false}
|
|
navigation={this.props.navigation}
|
|
/>
|
|
<Elements.loaderView
|
|
title="Validating"
|
|
message="Please wait..."
|
|
isDarkMode={this.props.app_theme?.theme.dark}
|
|
backgroundColor={this.props.app_theme?.theme.colors.border}
|
|
color={this.props.app_theme?.theme.colors.text}
|
|
visible={this.state.loading} />
|
|
{this.renderErrorModal()}
|
|
{this.renderCardForm()}
|
|
{this.renderSwitch()}
|
|
{this.state.verificationUrl != null && this.renderWebview()}
|
|
<View style={{ flex: 1 }} />
|
|
<View>
|
|
<Text style={{ fontSize: 12, fontWeight: 'bold', textAlign: 'center', fontStyle: 'italic', color: this.props.app_theme?.theme.colors.text }}>Your card will be charged to ensure that it's valid.</Text>
|
|
<Text style={{ fontSize: 12, fontWeight: 'bold', textAlign: 'center', fontStyle: 'italic', color: this.props.app_theme?.theme.colors.text, marginBottom: 10 }}>Charged amount will be automatically refunded.</Text>
|
|
<View style={{ marginBottom: 5 }} />
|
|
<TouchableOpacity disabled={!this.notEmpty()} onPress={() => this.submitNewAddedCard()}
|
|
style={{ height: 50, marginBottom: 16, backgroundColor: this.notEmpty() ? Theme.colors.primary : this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.border : Theme.colors.primary + "15", justifyContent: 'center', alignItems: 'center', borderRadius: 5, marginHorizontal: 35 }}>
|
|
<Text style={{ fontSize: 18, color: Theme.colors.white, textAlign: 'center', fontWeight: 'bold' }}>Save</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</CustomSafeArea>
|
|
)
|
|
}
|
|
}
|
|
|
|
const mapStateToProps = (state) => {
|
|
return {
|
|
userinfo: state.appUserInfoReducer.userinfo,
|
|
app_theme: state.appThemeReducer.theme
|
|
}
|
|
}
|
|
|
|
export default connect(mapStateToProps, null)(PayatpumpPaymentMethodList)
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
centeredView: {
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
backgroundColor: '#00000090',
|
|
},
|
|
modalView: {
|
|
margin: 25,
|
|
backgroundColor: "white",
|
|
borderRadius: 15,
|
|
padding: 20,
|
|
alignItems: "center",
|
|
shadowColor: "#000",
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 2
|
|
},
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 4,
|
|
elevation: 5
|
|
},
|
|
cardContainer: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: Theme.colors.gray,
|
|
height: 40,
|
|
borderRadius: 5,
|
|
marginVertical: 10,
|
|
marginHorizontal: 16
|
|
},
|
|
expirationCvvContainer: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: Theme.colors.gray,
|
|
height: 40,
|
|
borderRadius: 5,
|
|
marginVertical: 10
|
|
}
|
|
}) |