486 lines
19 KiB
JavaScript
486 lines
19 KiB
JavaScript
import * as React from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
TouchableOpacity,
|
|
Alert,
|
|
Platform,
|
|
Image
|
|
} from 'react-native';
|
|
import {
|
|
Image as NBImage,
|
|
Button,
|
|
ScrollView
|
|
} from 'native-base';
|
|
import {
|
|
TotalAmount,
|
|
Buyer,
|
|
Item,
|
|
Ref,
|
|
CheckOut
|
|
} from '../../components/paymaya/checkout';
|
|
import { saveUserInfo } from "../../redux/actions/AppUserInfoActions";
|
|
import { Input } from 'react-native-elements';
|
|
import { connect } from "react-redux";
|
|
import Icon from '../../components/icons.js';
|
|
import CustomSafeArea from '../../components/safeArea.component.js';
|
|
import NetInfo from '../../components/netstatus';
|
|
import CustomHeader from '../../components/header.js';
|
|
import Theme from '../../components/theme.style.js';
|
|
import Assets from '../../components/assets.manager.js';
|
|
import Elements from '../../components/elements.js';
|
|
import REQUEST from '../../components/api';
|
|
import DB from '../../components/storage/';
|
|
import TokenizationForm from './form';
|
|
import Tokenization from '../../components/paymaya/tokenization'
|
|
import moment from 'moment';
|
|
|
|
// const PAYMENT_ENV = "SANDBOX"
|
|
// const PAYMENT_ENV = "PRODUCTION"
|
|
|
|
class TopUp extends React.Component {
|
|
|
|
constructor(props) {
|
|
super(props)
|
|
this.current = ""
|
|
}
|
|
|
|
state = {
|
|
connected: false,
|
|
isCardFormVisible: false,
|
|
loading: false,
|
|
popup: false,
|
|
Task: "",
|
|
DisplayAmount: "",
|
|
amount: 0,
|
|
focused: false,
|
|
checkoutResult: "",
|
|
userProfile: null,
|
|
session: null,
|
|
topupCount: 0,
|
|
merchant: {},
|
|
creditcards: [],
|
|
activeIndex: 0,
|
|
isKeyboardActive: false,
|
|
tokenizationFormCount: 0,
|
|
transactionType: null
|
|
}
|
|
|
|
componentDidMount() {
|
|
NetInfo.netstatus(isConnected => {
|
|
if(isConnected) {
|
|
this.init()
|
|
} else {
|
|
Elements.nointernet2(this.props)
|
|
}
|
|
})
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
|
|
}
|
|
|
|
init = async () => {
|
|
let user = await DB.profile()
|
|
const session = await DB.session()
|
|
this.setState({ connected: true, loading: true, userProfile: user, session: session })
|
|
if(user.data.civilstatus_code == "0" || user.data.gender_code == "0") {
|
|
Alert.alert("Information", "Update your Profile first to use this feature.", [{text: "OK", onPress: () => this.props.navigation.goBack()}])
|
|
return
|
|
}
|
|
|
|
this.countTransactions()
|
|
this.getMerchant()
|
|
}
|
|
|
|
countTransactions = async () => {
|
|
const SESSION = await DB.session()
|
|
await REQUEST("transactions", "get", {
|
|
Authorization: SESSION.token,
|
|
card_number: SESSION.user.card_number
|
|
}, {}, {}, (res) => {
|
|
console.log(res)
|
|
let count = 0
|
|
if(res.data.length > 0){
|
|
for(var x=0;x<res.data.length;x++){
|
|
let today = moment()
|
|
let dt = moment(res.data[x].date.split(",")[0])
|
|
let diff = dt.diff(today, 'days')
|
|
if(diff == 0) count++
|
|
}
|
|
}
|
|
this.setState({ topupCount: count, loading: false })
|
|
if(count >= 5){
|
|
this.setState({ loading: false })
|
|
Platform.OS == 'ios' ? setTimeout(() => {
|
|
Alert.alert("Top Up", "\nYou have reached your maximum top up for this day.")
|
|
}, 700)
|
|
:
|
|
Alert.alert("Top Up", "\nYou have reached your maximum top up for this day.")
|
|
}
|
|
}, (error) => {
|
|
this.setState({ loading: false })
|
|
console.log(error)
|
|
})
|
|
}
|
|
|
|
handleDelete = (id, token, uuid) => {
|
|
Alert.alert(
|
|
"Delete Payment Card",
|
|
"Are you sure you want to delete this card?",
|
|
[
|
|
{
|
|
text: "NO",
|
|
style: "cancel"
|
|
},
|
|
{
|
|
text: "YES",
|
|
onPress: () => {
|
|
this.setState({ loading: true })
|
|
Tokenization.initRemove(id, token, uuid).then((res) => {
|
|
this.setState({ loading: false })
|
|
if(res.result == 'OK'){
|
|
setTimeout(() => {
|
|
Alert.alert("Deleted Successfully", "\nYour credit card has successfully deleted.", [{text: "OK", onPress: async () => {
|
|
this.init()
|
|
let creditcards = this.state.creditcards.filter(function(item){
|
|
return item.uuid != uuid
|
|
}).map(function(item){
|
|
return item
|
|
})
|
|
this.setState({ creditcards: creditcards })
|
|
}}])
|
|
}, 300)
|
|
}
|
|
})
|
|
}
|
|
},
|
|
]
|
|
)
|
|
}
|
|
|
|
getMerchant = async () => {
|
|
const USER_PROFILE = await DB.profile()
|
|
const SESSION = await DB.session()
|
|
await REQUEST("paymaya_tokens", "get", {'Authorization': SESSION.token}, {noID: true, value: USER_PROFILE.data.card_number}, {}, async (res) => {
|
|
if(res.data && res.data.cards){
|
|
this.setState({ merchant: res.data, activeIndex: 0 })
|
|
for(var x=0;x<res.data.cards.length;x++){
|
|
if(res.data.cards[x].default) this.setState({ activeIndex: x })
|
|
}
|
|
console.log(res.data.cards, res)
|
|
this.setState({ creditcards: res.data.cards, loading: false })
|
|
} else {
|
|
this.setState({ merchant: {}, creditcards: [], loading: false })
|
|
}
|
|
}, (error) => {
|
|
this.setState({ loading: false })
|
|
console.log(error)
|
|
})
|
|
}
|
|
|
|
moneyFormat = (price, sign = 'PHP ') => {
|
|
const pieces = parseFloat(price).toFixed(2).split('')
|
|
let ii = pieces.length - 3
|
|
while ((ii-=3) > 0) {
|
|
pieces.splice(ii, 0, ',')
|
|
}
|
|
return sign + pieces.join('')
|
|
}
|
|
|
|
setamount = (value) => {
|
|
this.setState({ DisplayAmount: this.moneyFormat(value).toString(), amount: value })
|
|
}
|
|
|
|
onAmountChange = (value) => {
|
|
if(value != this.current){
|
|
let parsed = parseFloat(this.clean(value))
|
|
if(parsed > 1000000) return false
|
|
let formatted = this.formatCurrency(parsed)
|
|
this.current = formatted
|
|
this.setState({ DisplayAmount: formatted, amount: parseFloat(parsed/100) })
|
|
}
|
|
}
|
|
|
|
clean = (str) => {
|
|
return str.replace("PHP ", "").replace(".", "").replace(",", "").toString()
|
|
}
|
|
|
|
formatCurrency = (val) => {
|
|
let fv = isNaN(val) ? (0).toFixed(2) : parseFloat(val / 100).toFixed(2)
|
|
return val.length == 6 ? "PHP " + fv.substr(0, 1) + "," + fv.substr(2, fv.length) : "PHP " + fv
|
|
}
|
|
|
|
validate = () => {
|
|
if(this.state.amount < 100 || this.state.amount > 10000){
|
|
Alert.alert("Invalid top up amount", "\nPlease enter value between 100 - 10,000 pesos only.\n")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
onReload = (res, msg) => {
|
|
console.log("onBack", res, msg)
|
|
}
|
|
|
|
updateTopup = (transactionId, amount, onSuccess, onError) => {
|
|
REQUEST('topup_transaction_entry', 'post', {
|
|
Authorization: this.state.session.token
|
|
}, {}, {
|
|
amount: amount,
|
|
paymaya_tranx_id: transactionId
|
|
}, (res) => onSuccess(res), (error) => onError(error))
|
|
}
|
|
|
|
initCheckout = async () => {
|
|
if(!this.validate()) return false
|
|
if(this.state.creditcards.length > 0){
|
|
let mct = this.state.creditcards[this.state.activeIndex]
|
|
Alert.alert("Confirm Top Up", "\nYour card number will be used to pay for "+parseFloat(this.state.amount).toFixed(2)+" points.", [
|
|
{text: "NO",style: "cancel"},
|
|
{text: "YES",
|
|
onPress: async () => {
|
|
let payout = await Tokenization.initNewPayout(mct.cardTokenId, this.state.amount);
|
|
if(payout.status == "success") {
|
|
this.setState({ loading: true })
|
|
this.updateTopup(payout.id, this.state.amount, onSuccess => {
|
|
this.setState({ loading: false })
|
|
let data = {
|
|
amount: this.state.amount,
|
|
transactionId: payout.id,
|
|
data: {redirectUrl: payout.verificationUrl, card_number: mct.maskedPan, type: 'create' },
|
|
onBack: (res, msg) => alert(msg)
|
|
}
|
|
this.props.navigation.navigate('CheckOut', data)
|
|
}, onError => {
|
|
setTimeout(() => {
|
|
Alert.alert("Top Up", `\n${onError.error}`)
|
|
}, 700)
|
|
})
|
|
} else if(payout.status == "failed"){
|
|
this.setState({ loading: false })
|
|
if(payout.parameters.length > 0) {
|
|
setTimeout(() => {
|
|
Alert.alert("Top Up", `\n${payout.parameters[0]?.description}`)
|
|
}, 700)
|
|
} else {
|
|
Platform.OS == 'ios' ? setTimeout(() => {
|
|
Alert.alert("Top Up", "\nFailed to add new card.")
|
|
}, 300) : Alert.alert("Top Up", "\nFailed to add new card.")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
])
|
|
} else {
|
|
this.setState({ isCardFormVisible: true })
|
|
}
|
|
}
|
|
|
|
initAddCard = async () => {
|
|
this.setState({ isCardFormVisible: true, tokenizationFormCount: this.state.tokenizationFormCount + 1, transactionType: "add" })
|
|
}
|
|
|
|
getDisplayCard = (type) => {
|
|
if(!type) return
|
|
else if(type == "visa") return Assets.icons.stpvisa
|
|
else if(type == "master-card") return Assets.icons.stpmastercard
|
|
else if(type == "jcb") return Assets.icons.stpjcb
|
|
else return Assets.icons.stpunknown
|
|
}
|
|
|
|
renderCardform = () => {
|
|
return (
|
|
<TokenizationForm
|
|
key={this.state.tokenizationFormCount}
|
|
type={this.state.transactionType == "add" ? "add" : "create"}
|
|
cardlist={this.state.creditcards}
|
|
customerId={this.state.merchant && this.state.creditcards.length > 0 ? this.state.merchant.customer_id : ''}
|
|
amount={this.state.amount}
|
|
onDone={() => {
|
|
this.init()
|
|
console.log("onDone")
|
|
}}
|
|
onBack={(res, msg) => {
|
|
this.onReload(res, msg)
|
|
}}
|
|
onGoBack={() => {
|
|
this.setState({ isCardFormVisible: false })
|
|
console.log("onGoBack")
|
|
}}
|
|
onSuccess={data => {
|
|
console.log("onSucces", data)
|
|
this.props.navigation.navigate('CheckOut', data)
|
|
}}
|
|
/>
|
|
)
|
|
}
|
|
|
|
render() {
|
|
console.log("The Credit Cards: " + JSON.stringify(this.state.creditcards));
|
|
if(!this.state.connected){
|
|
return (
|
|
<CustomSafeArea style={{flex: 1}}>
|
|
<View style={{flex: 1}}>
|
|
<CustomHeader title="Top Up Points" menu={false} navigation={this.props.navigation} />
|
|
<Elements.nointernet
|
|
message="No internet found. Please check your internet connection."
|
|
buttonText="Try Again"
|
|
onPress={() => this.init()}
|
|
/>
|
|
</View>
|
|
</CustomSafeArea>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<CustomSafeArea>
|
|
{this.state.isCardFormVisible && this.renderCardform()}
|
|
<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} />
|
|
<CustomHeader title="Top Up Points" menu={false} navigation={this.props.navigation} />
|
|
<ScrollView>
|
|
<View style={{ flex: 1, backgroundColor: Theme.colors.white }}>
|
|
<View style={{flexDirection: 'row', padding: 15, backgroundColor: Theme.colors.textPrimary}}>
|
|
<View style={{flex: 1}}>
|
|
<Text style={{fontFamily: 'Arial', fontSize: 16, color: '#fff'}}>Select Top Up Value Points</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={{flexDirection: 'column', padding: 20, paddingTop: 10, paddingBottom: 5, alignItems: 'center'}}>
|
|
<View style={{flexDirection: 'row'}}>
|
|
<TouchableOpacity onPress={() => this.setamount(100)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}>
|
|
<Text style={{textAlign: 'center', fontFamily: 'Arial', fontSize: 16, color: Theme.colors.primary}}>100</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity onPress={() => this.setamount(500)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}>
|
|
<Text style={{textAlign: 'center', fontFamily: 'Arial', fontSize: 16, color: Theme.colors.primary}}>500</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
<View style={{flexDirection: 'row'}}>
|
|
<TouchableOpacity onPress={() => this.setamount(1000)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}>
|
|
<Text style={{textAlign: 'center', fontFamily: 'Arial', fontSize: 16, color: Theme.colors.primary}}>1000</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity onPress={() => this.setamount(5000)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}>
|
|
<Text style={{textAlign: 'center', fontFamily: 'Arial', fontSize: 16, color: Theme.colors.primary}}>5000</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={{padding: 15, paddingTop: 0}}>
|
|
<Text style={{padding: 5, paddingLeft:12, fontSize: 14, color: this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.text : Theme.colors.darkGray}}>
|
|
Or Enter Desired Value (maximum of 10,000 points)
|
|
</Text>
|
|
<Input
|
|
keyboardType="numeric"
|
|
placeholder="PHP"
|
|
value={this.state.DisplayAmount}
|
|
onFocus={() => this.setState({ focused: true })}
|
|
onChangeText={(value) => this.onAmountChange(value)}
|
|
containerStyle={{padding: 0}}
|
|
inputContainerStyle={{padding: 0, borderBottomWidth: this.state.focused ? 1.75 : 1, borderColor: this.state.focused ? Theme.colors.accent : "gray" }}
|
|
inputStyle={{padding: 0, fontFamily: 'Arial', fontSize: 16, color: this.props.app_theme?.theme.colors.text }}
|
|
/>
|
|
<Text style={{fontStyle: 'italic', fontSize: 15, paddingLeft: 12, marginTop: 20, fontFamily: 'Arial', color: this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.text : Theme.colors.darkGray}}>Note: Top Up to a maximum of five(5) times per day.</Text>
|
|
</View>
|
|
|
|
<View style={{flexDirection: 'row', padding: 15, backgroundColor: Theme.colors.textPrimary}}>
|
|
<View style={{flex: 1}}>
|
|
<Text style={{fontFamily: 'Arial', fontSize: 16, color: '#fff'}}>Payment Method</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{
|
|
this.state.creditcards.length > 0 &&
|
|
<View style={{flexDirection: 'row', alignItems: 'center', paddingHorizontal: 15, borderBottomWidth: 0.5, borderColor: 'lightgray'}}>
|
|
<NBImage source={this.getDisplayCard(this.state.creditcards[this.state.activeIndex]?.cardType)} style={{width: 60, resizeMode: 'contain'}} />
|
|
<View style={{marginLeft: 10}}>
|
|
<Text style={{ color: this.props.app_theme?.theme.colors.text }}>{this.state.creditcards[this.state.activeIndex]?.cardType.toString().toUpperCase()} {this.state.creditcards[this.state.activeIndex]?.maskedPan}</Text>
|
|
<Text style={{ color: this.props.app_theme?.theme.colors.text }} note numberOfLines={1}>**** **** **** {this.state.creditcards[this.state.activeIndex]?.maskedPan}</Text>
|
|
</View>
|
|
</View>
|
|
}
|
|
|
|
{
|
|
this.state.creditcards && this.state.creditcards.length === 0 &&
|
|
<View style={{flexDirection: 'row', alignItems: 'center', paddingHorizontal: 15, borderBottomWidth: 0.5, borderColor: 'lightgray'}}>
|
|
<NBImage source={Assets.icons.stpunknown} style={{width: 60, resizeMode: 'contain'}} />
|
|
<View style={{marginLeft: 10}}>
|
|
<Text style={{ color: this.props.app_theme?.theme.colors.text }} note numberOfLines={1}>0000 0000 0000 0000</Text>
|
|
</View>
|
|
</View>
|
|
}
|
|
|
|
{
|
|
this.state.creditcards.length <= 5 && (
|
|
<TouchableOpacity onPress={() => this.initAddCard()} style={{flexDirection: 'row', alignItems: 'center', padding: 15, borderBottomWidth: 0.5, borderColor: 'lightgray'}}>
|
|
<Image source={Assets.icons.topup} style={{width: 25, height: 25, tintColor: this.props.app_theme?.theme.colors.text}} />
|
|
<Text style={{fontFamily: 'Arial', fontSize: 16, marginLeft: 10, color: this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.text : Theme.colors.textPrimary}}>
|
|
Add New Card
|
|
</Text>
|
|
</TouchableOpacity>
|
|
)
|
|
}
|
|
|
|
{
|
|
this.state.creditcards.length > 0 && this.state.creditcards.map((card, index) => {
|
|
return (
|
|
<View style={{flexDirection: 'row', alignItems: 'center', paddingHorizontal: 15, marginBottom: 5}}>
|
|
<TouchableOpacity
|
|
activeOpacity={.8}
|
|
onPress={() => {
|
|
Alert.alert("Select Card", "**** **** **** " + card?.maskedPan + " is selected.", [{text: "OK"}])
|
|
this.setState({ activeIndex: index });
|
|
}}
|
|
style={{flexDirection: 'row', flex: 1, alignItems: 'center'}}>
|
|
<NBImage source={this.getDisplayCard(card?.cardType)} style={{width: 80, resizeMode: 'contain'}} />
|
|
<View style={{marginLeft: 10 }}>
|
|
<Text style={{ color: this.props.app_theme?.theme.colors.text }}>{card?.cardType.toString().toUpperCase()}</Text>
|
|
<Text style={{ color: this.props.app_theme?.theme.colors.text }} note numberOfLines={1}>**** **** **** {card?.maskedPan}</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
|
|
<Button
|
|
style={{padding: 0, backgroundColor: 'transparent', marginRight: 10}}
|
|
onPress={() => this.handleDelete(card.customer_id, card.paymentTokenId, card.uuid)}>
|
|
<Icon.Ionicons name='trash' size={25} color={Theme.colors.primary}/>
|
|
</Button>
|
|
</View>
|
|
)
|
|
})
|
|
}
|
|
|
|
<View style={{marginBottom:60}}></View>
|
|
</View>
|
|
</ScrollView>
|
|
{this.state.topupCount >= 5 ? null :
|
|
<TouchableOpacity
|
|
disabled={this.state.creditcards.length > 0 ? false : true}
|
|
style={{justifyContent: 'center', marginHorizontal: 15, paddingVertical: 15, borderRadius: 10, backgroundColor: this.state.creditcards.length > 0 ? Theme.colors.primary : this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.border : Theme.colors.primary + "15", marginBottom: 16}}
|
|
onPress={() => this.initCheckout()}>
|
|
<Text style={{fontSize: 16, fontFamily: 'Arial', textAlign: 'center', color: '#fff'}}>Next</Text>
|
|
</TouchableOpacity>
|
|
}
|
|
<Elements.popup visible={this.state.popup} message={`Transaction ${this.state.Task != "" ? this.state.Task : null}.`} />
|
|
</CustomSafeArea>
|
|
)
|
|
}
|
|
}
|
|
|
|
const mapStateToProps = (state) => {
|
|
return {
|
|
userinfo: state.appUserInfoReducer.userinfo,
|
|
app_theme: state.appThemeReducer.theme
|
|
}
|
|
}
|
|
|
|
const mapDispatchToProps = {
|
|
saveUserInfo
|
|
}
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(TopUp);
|