Skip to content

Instantly share code, notes, and snippets.

@DesunTech
Created March 16, 2024 09:12
Show Gist options
  • Select an option

  • Save DesunTech/b177fd12578788a0905251f3e7c8f003 to your computer and use it in GitHub Desktop.

Select an option

Save DesunTech/b177fd12578788a0905251f3e7c8f003 to your computer and use it in GitHub Desktop.
Stripe Terminal Tap to pay
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
*/
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {AppProvider, AppConsumer} from './src/Provider/context/AppProvider';
import Stacknav from './src/Provider/Routenavigation';
import {LogBox, Text} from 'react-native';
import { StripeTerminalProvider } from '@stripe/stripe-terminal-react-native';
import { config } from './src/Provider/configProvider';
// LogBox.ignoreLogs(['Warning: ...']); // Ignore log notification by message
// LogBox.ignoreAllLogs(); //Ignore all log notifications
// LogBox.ignoreLogs([
// 'Animated: `useNativeDriver` was not specified. This is a required option and must be explicitly set to `true` or `false`',
// ]);
global.MapAddress = 'NA';
function App(props: any): JSX.Element {
const fetchTokenProvider = async () => {
const response = await fetch(`${config.stripeApiUrl}connection_token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
const { secret } = await response.json();
return secret;
};
return (
<StripeTerminalProvider
logLevel="verbose"
tokenProvider={fetchTokenProvider}
>
<NavigationContainer>
{/* <NavigationContainer linking={linking}> */}
<AppProvider {...props}>
<AppConsumer>
{funcs => {
global.props = {...funcs};
return <Stacknav {...funcs} />;
}}
</AppConsumer>
</AppProvider>
</NavigationContainer>
</StripeTerminalProvider>
);
}
export default App;
import React, { Component, useRef, useEffect, useState } from 'react';
import {
Text,
SafeAreaView,
Alert,
View,
TouchableOpacity,
Keyboard,
StyleSheet,
ScrollView,
StatusBar,
Image,
Modal,
TextInput,
FlatList,
Touchable,
} from 'react-native';
import {
config,
msgProvider,
notification,
localStorage,
apifuntion,
msgText,
msgTitle,
consolepro,
Lang_chg,
Font,
Colors,
mobileH,
mobileW,
localimag,
} from './Provider/utilslib/Utils';
import LinearGradient from 'react-native-linear-gradient';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import DashedLine from 'react-native-dashed-line';
import HideWithKeyboard from 'react-native-hide-with-keyboard';
import Footer from './Provider/Footer';
import StarRating from 'react-native-star-rating';
import { configure } from '@react-native-community/netinfo';
import { cleanSingle } from 'react-native-image-crop-picker';
import { Nodata_foundimage } from './Provider/Nodata_foundimage';
import {
CardField,
useStripe,
StripeProvider,
presentPaymentSheet,
useConfirmPayment,
CardForm,
} from '@stripe/stripe-react-native';
import { requestNeededAndroidPermissions, useStripeTerminal } from '@stripe/stripe-terminal-react-native';
import { connectLocalMobileReader } from "@stripe/stripe-terminal-react-native/src/functions";
/**
* Tap to pay
*/
export function TapToPay({
onPaymentSuccess,
onPaymentFailure,
amount,
details
}) {
const [selectedReader, setSelectedReader] = useState(null);
useEffect(() => {
async function init() {
try {
const granted = await requestNeededAndroidPermissions({
accessFineLocation: {
title: 'Location Permission',
message: 'Stripe Terminal needs access to your location',
buttonPositive: 'Accept',
},
});
if (granted) {
// Initialize the SDK
} else {
Alert.alert('Location and BT services are required in order to connect to a reader.')
}
} catch (e) {
console.error(e);
}
}
init();
}, []);
const { discoverReaders, collectPaymentMethod, confirmPaymentIntent, retrievePaymentIntent } = useStripeTerminal({
onUpdateDiscoveredReaders: (readers) => {
setSelectedReader(readers[0]);
// console.log("discoveredReaders: ", readers);
},
onDidReportUnexpectedReaderDisconnect: (readers) => {
// Consider displaying a UI to notify the user and start rediscovering readers
Alert.alert("something went wrong");
},
onDidRequestReaderInput: (options) => {
// Placeholder for updating your app's checkout UI
Alert.alert(options.join('/'));
},
onDidRequestReaderDisplayMessage: (message) => {
// Alert.alert(message);
console.log(message);
}
});
// discover readers
useEffect(() => {
async function fetchReaders() {
const { error } = await discoverReaders({
discoveryMethod: 'localMobile',
simulated: true // make sure to set it false before build
});
console.warn("discoverReaders err", error);
if (error) {
Alert.alert(error.message);
}
}
fetchReaders();
}, [discoverReaders]);
useEffect(() => {
async function connectReaderToMobile() {
const { reader, error } = await connectLocalMobileReader({
reader: selectedReader,
locationId: 'tml_FfDMsAm9S9Ea64' // need to adjust according to needs
});
if (error) {
onPaymentFailure(JSON.stringify(error));
// onPaymentFailure('Something went wrong.');
return;
}
console.log('Reader connected successfully', reader);
}
if (selectedReader) {
// if we get any available reader lets connect it
connectReaderToMobile();
}
}, [selectedReader]);
async function handlePaymentIntent() {
async function createPayIntent() {
const finalAmount = Number(amount) * 100;
const formData = new URLSearchParams();
formData.append('amount', finalAmount.toString());
formData.append('currency', 'aud');
formData.append('capture_method', 'automatic');
try {
const response = await fetch(`${config.stripeApiUrl}create_payment_intent`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData.toString(),
});
const data = await response.json();
const { error, paymentIntent } = await retrievePaymentIntent(data?.secret);
if(error) {
onPaymentFailure(JSON.stringify(error));
// onPaymentFailure('Something went wrong.');
return;
}
await handleCollectPayment(paymentIntent);
} catch( e ) {
console.log("payment intent error", e);
}
}
await createPayIntent();
}
async function handleCollectPayment(pIntent) {
const { paymentIntent, error } = await collectPaymentMethod({
paymentIntent: pIntent
});
if (error) {
onPaymentFailure(JSON.stringify(error));
// onPaymentFailure('Something went wrong.');
// Placeholder for handling exception
console.warn("collectPaymentMethod error:", error);
return;
}
const confirmData = await confirmPaymentIntent(paymentIntent);
if (confirmData.error) {
onPaymentFailure(JSON.stringify(error));
// onPaymentFailure('Something went wrong.');
// Placeholder for handling exception
return;
}
console.log("payment intent confirm e-->", confirmData.paymentIntent);
onPaymentSuccess(confirmData.paymentIntent.id)
}
return (
<>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<Text>Tap To Pay</Text>
</View>
<TouchableOpacity
onPress={() => handlePaymentIntent() }>
<LinearGradient
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
colors={[Colors.theamColor1, Colors.theamColor]}
style={{
width: '93%',
paddingVertical: (mobileW * 2.5) / 100,
marginTop: (mobileW * 8) / 100,
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center',
borderRadius: (mobileW * 1.5) / 100,
}}>
<Text
style={{
fontSize: (mobileW * 4) / 100,
fontFamily: Font.Semibold,
color: Colors.white_color,
}}>
Continue
</Text>
</LinearGradient>
</TouchableOpacity>
</>
);
}
export function PaymentScreen({
onPaymentSuccess,
onPaymentFailure,
amount,
details,
}) {
const { confirmPayment, loading } = useConfirmPayment();
const stripe = useStripe();
const cardFormRef = useRef(null);
const handleUpdateCardNumber = () => {
// Clear the existing card number
// if (cardFormRef.current) {
// cardFormRef.current.setCardNumber('424242424242');
// }
console.log('Update card number');
};
generate_payment_intent = async () => {
let user_details = await localStorage.getItemObject('user_arr');
let guest_id = await localStorage.getItemString('guest_id');
if (user_details == null) {
var user_id = guest_id.toString();
} else {
if (config.user_type == 2) {
var user_id = guest_id.toString();
} else {
var user_id = user_details.user_id;
}
}
try {
const token = await stripe.createToken({
type: 'Card',
currency: 'usd',
// card: {
// number: '5596664097834644', // Example card number
// expMonth: '05', // Example expiration month
// expYear: '26', // Example expiration year
// cvc: '083', // Example CVC
// addressZip: '19709',
// },
});
let url = config.baseURL + 'stripe_payment/generate_payment_intent.php';
var data = new FormData();
data.append('user_id', user_id);
data.append('amount', amount);
data.append('token', token.token.id);
data.append('descriptor_suffix', 'Order');
// const data = JSON.stringify({
// user_id: user_id,
// amount: amount,
// token: token.token,
// descriptor_suffix: 'Order',
// });
apifuntion
.postApi(url, data)
.then(async obj => {
if (obj.success == 'true') {
console.log(obj, 'Intent obj');
await handlePayment(obj.clientSecret, obj.transactions_id);
}
})
.catch(err => {
// consolepro.consolelog('errs', err);
console.log('Errorss', err);
onPaymentFailure('Something went wrong.');
});
} catch (error) {
onPaymentFailure('Something went wrong, please try again.');
}
};
const handlePayment = async (key, transactions_id) => {
try {
// const {paymentIntent, error} = await confirmPayment(key, {
// paymentMethodType: 'Card',
// paymentMethodData: {
// billingDetails: {
// email: 'mfonabasiisaac@gmail.com',
// },
// },
// });
// if (error) {
// onPaymentFailure(error.message);
// } else {
onPaymentSuccess(transactions_id);
// }
} catch (error) {
onPaymentFailure('Something went wrong.');
} finally {
}
};
return (
<>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<StripeProvider
publishableKey="pk_live_51KcC0FAMDGnEJRxaE2DS5CGjo7oH5F1Pce9FUlpsSmuhyLDC6ImcurkrbFt5bQmRfYJYSAsWzspWCcq6l1FHt1Sg00UWcaYvOt"
merchantIdentifier="merchant.identifier" // required for Apple Pay
urlScheme="your-url-scheme" // required for 3D Secure and bank redirects
>
<CardField
postalCodeEnabled={true}
placeholder={{
number: 'Card Number',
expiry: 'MM/YY',
cvc: 'CVC',
}}
cardStyle={{
backgroundColor: '#FFFFFF',
textColor: '#000000',
}}
style={{
width: '100%',
height: 40,
marginVertical: 30,
}}
onCardChange={cardDetails => {
console.log('cardDetails', cardDetails);
}}
onFocus={focusedField => {
console.log('focusField', focusedField);
}}
/>
{/* <CardForm
postalCodeEnabled={false}
ref={cardFormRef}
style={{
width: '95%',
height: 250,
marginVertical: 30,
}}
cardStyle={{
backgroundColor: '#000',
textColor: '#ff0000',
}}
defaultValues={{number: '424242424242424'}}
cardNumber="424242424242424"
/> */}
</StripeProvider>
</View>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<TouchableOpacity
style={{
width: (mobileW * 10) / 100,
flexDirection: 'row',
}}
onPress={() => {
// alert(2);
// handleUpdateCardNumber();
}}>
<Image
style={{
height: (mobileW * 5) / 100,
width: (mobileW * 5) / 100,
resizeMode: 'contain',
marginRight: 10,
}}
source={localimag.scan_dark}
/>
<Text>Scan</Text>
</TouchableOpacity>
</View>
<TouchableOpacity
onPress={() => {
generate_payment_intent();
}}>
<LinearGradient
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
colors={[Colors.theamColor1, Colors.theamColor]}
style={{
width: '93%',
paddingVertical: (mobileW * 2.5) / 100,
marginTop: (mobileW * 8) / 100,
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center',
borderRadius: (mobileW * 1.5) / 100,
}}>
<Text
style={{
fontSize: (mobileW * 4) / 100,
fontFamily: Font.Semibold,
color: Colors.white_color,
}}>
Continue
</Text>
</LinearGradient>
</TouchableOpacity>
</>
);
}
export default class FinalPayment extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.route.params.data,
amount: this.props.route.params.amount,
stripe_card_id: '',
customer_id: '',
user_id: 0,
card_id: 'NA',
token: '',
exp_month: '',
exp_year: '',
last4: '',
card_arr: 'NA',
card_id: 'NA',
card_token: '',
};
}
componentDidMount() {
// const {navigation} = ;
this.focusListener = this.props.navigation.addListener('blur', async () => {
await this.generate_payment_intent();
this.getData();
});
this.getData();
}
getData = async () => {
let user_details = await localStorage.getItemObject('user_arr');
let guest_id = await localStorage.getItemString('guest_id');
if (user_details == null) {
var user_id = guest_id;
} else {
if (config.user_type == 2) {
var user_id = guest_id.toString();
} else {
var user_id = user_details.user_id;
}
}
this.setState({ user_id: user_id });
};
handlePaymentSuccess = transactions_id => {
this.setState({
transaction_id: transactions_id,
});
setTimeout(() => {
this.addOrder();
}, 500);
};
handlePaymentFailure = errorMessage => {
Alert.alert('Error', errorMessage);
// Additional actions on payment failure
};
get_card = async page => {
let user_details = await localStorage.getItemObject('user_arr');
let guest_id = await localStorage.getItemString('guest_id');
if (user_details == null) {
var user_id = guest_id.toString();
} else {
if (config.user_type == 2) {
var user_id = guest_id.toString();
} else {
var user_id = user_details.user_id;
}
}
let url = config.baseURL + 'get_stripe_card.php?user_id=' + user_id;
consolepro.consolelog('url', url);
apifuntion
.getApi(url, page)
.then(obj => {
consolepro.consolelog('obj', obj);
if (obj.success == 'true') {
this.setState({ card_arr: obj.all_card_arr });
} else {
// if (obj.active_status == msgTitle.deactivate[config.language] || obj.msg[config.language] == msgTitle.usererr[config.language]) {
// usernotfound.loginFirst(this.props, obj.msg[config.language])
// } else {
msgProvider.alert(
msgTitle.information[config.language],
obj.msg[config.language],
false,
);
// }
return false;
}
})
.catch(error => {
consolepro.consolelog('-------- error ------- ' + error);
});
};
delete_click(stripe_card_id) {
Alert.alert(
Lang_chg.txt_detail_job_Delete[config.language],
Lang_chg.delete_card_detail[config.language],
[
{
text: Lang_chg.no[config.language],
},
{
text: Lang_chg.yes[config.language],
onPress: () => this.delete_click_card(stripe_card_id),
},
],
{ cancelable: false },
);
}
delete_click_card = async stripe_card_id => {
consolepro.consolelog('shubhamcardis', stripe_card_id);
let user_details = await localStorage.getItemObject('user_arr');
let guest_id = await localStorage.getItemString('guest_id');
if (user_details == null) {
var user_id = guest_id.toString();
} else {
if (config.user_type == 2) {
var user_id = guest_id.toString();
} else {
var user_id = user_details.user_id;
}
}
let url = config.baseURL + 'delete_stripe_card.php';
consolepro.consolelog('url', url);
var data = new FormData();
data.append('user_id', user_id);
data.append('stripe_card_id', stripe_card_id);
consolepro.consolelog('data', data);
apifuntion
.postApi(url, data)
.then(obj => {
consolepro.consolelog('obj', obj);
if (obj.success == 'true') {
setTimeout(() => {
msgProvider.toast(obj.msg[config.language], 'center');
this.get_card();
}, 500);
} else {
// if (obj.active_status == 0 || obj.msg == msgTitle.user_not_exist[config.language]) {
// setTimeout(() => {
// msgProvider.alert(msgTitle.information[config.language], obj.msg[config.language], false);
// }, 200)
// config.checkUserDeactivate(this.props.navigation)
// } else {
// setTimeout(() => {
msgProvider.alert(
msgTitle.information[config.language],
obj.msg[config.language],
false,
);
// }, 200)
// }
return false;
}
})
.catch(error => {
consolepro.consolelog('-------- error ------- ' + error);
this.setState({ loading: false });
});
};
add_card_payment = async () => {
if (this.state.card_arr == 'NA') {
msgProvider.toast(Lang_chg.addCard[config.language], 'center');
return false;
}
if (this.state.card_id == 'NA') {
msgProvider.toast(Lang_chg.emptyCard[config.language], 'center');
return false;
}
let user_details = await localStorage.getItemObject('user_arr');
let guest_id = await localStorage.getItemString('guest_id');
if (user_details == null) {
var user_id = guest_id.toString();
} else {
if (config.user_type == 2) {
var user_id = guest_id.toString();
} else {
var user_id = user_details.user_id;
}
}
let url = config.baseURL + 'stripe_payment/payment_using_card_id.php';
var data = new FormData();
data.append('user_id', user_id);
data.append('card_token_id', this.state.card_token);
data.append('customer_id', this.state.customer_id);
data.append('amount', this.state.amount);
data.append('descriptor_suffix', 'Order');
// console.log('dataaa', data);
// // consolepro.consolelog('url', url)
apifuntion
.postApi(url, data)
.then(obj => {
if (obj.success == 'true') {
// consolepro.consolelog('obj', obj);
// consolepro.consolelog('transaction_id', obj.transactions_id);
this.setState({
transaction_id: obj.transactions_id,
user_id: this.state.user_id,
});
setTimeout(() => {
this.addOrder();
}, 500);
//------next navigation
} else {
if (obj.active_status == 0 || obj.account_active_status == 0) {
setTimeout(() => {
config.checkUserDeactivate(this.props.navigation);
}, 300);
return false;
} else {
setTimeout(() => {
msgProvider.alert(
msgTitle.information[config.language],
obj.msg[config.language],
false,
);
}, 300);
}
return false;
}
})
.catch(err => {
// consolepro.consolelog('errs', err);
console.log('Errorss', err);
});
};
addOrder = async () => {
consolepro.consolelog('iaminaddorderfun');
let userdata = await localStorage.getItemObject('user_arr');
// ----------------- Make Url For Particular API ----------------
let url = config.baseURL + 'order.php';
consolepro.consolelog('url', url);
//--------- Create Form Data Object And Set Values into it -------
var data = this.state.data;
data.append('transaction_id', this.state.transaction_id);
if (userdata == null || config.user_type == 2) {
data.append('name', this.props.route.params.guest_details.name);
data.append('email', this.props.route.params.guest_details.email);
data.append(
'phone_number',
this.props.route.params.guest_details.phone_number,
);
data.append(
'server_name',
this.props.route.params.guest_details.server_name,
);
data.append('is_business_checkout', config.user_type == 2 ? 1 : 0);
}
consolepro.consolelog('data', data);
apifuntion
.postApi(url, data)
.then(obj => {
consolepro.consolelog('obj', obj);
if (obj.success == 'true') {
setTimeout(async () => {
if (obj.notification_arr != 'NA') {
notification.notification_arr(obj.notification_arr);
}
console.log(obj);
if (userdata == null || config.user_type == 2) {
await localStorage.setItemString('express_mode', 'off');
this.props.navigation.navigate('Ordersuccessful', {
email: obj?.email,
payment_number_id: obj?.payment_number_id,
});
} else {
this.props.navigation.navigate('Ordersuccessful', {
email: userdata.email,
payment_number_id: obj?.payment_number_id,
});
}
}, 300);
} else {
setTimeout(() => {
msgProvider.alert(
msgTitle.information[config.language],
obj.msg[config.language],
false,
);
}, 300);
return false;
}
})
.catch(error => {
// consolepro.consolelog(error);
console.log(JSON.stringify(error));
});
};
checkCard = (item, index) => {
let data = this.state.card_arr;
consolepro.consolelog('data', data);
for (let i = 0; i < data.length; i++) {
data[i].status = false;
}
data[index].status = true;
this.setState({
card_arr: data,
card_id: data[index].card_id,
card_token: data[index].token,
customer_id: data[index].customer_id,
});
};
componentDidMount() {
this.props.navigation.addListener('focus', () => {
this.get_card();
});
this.get_card(1);
}
render() {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: Colors.theamColor1 }}>
<View style={{ flex: 1, backgroundColor: '#fff' }}>
<TouchableOpacity
activeOpacity={1}
style={{ flex: 1 }}
onPress={() => {
Keyboard.dismiss();
}}>
{/* <SafeAreaView style={{ backgroundColor: Colors.theamColor, flex: 0 }} /> */}
<StatusBar
backgroundColor={Colors.theamColor1}
hidden={false}
barStyle={'light-content'}
translucent={false}
networkActivityIndicatorVisible={true}
/>
<PaymentScreen
onPaymentSuccess={this.handlePaymentSuccess}
onPaymentFailure={this.handlePaymentFailure}
amount={this.state.amount}
details={this.props.route.params.data}
/>
<TapToPay
onPaymentSuccess={this.handlePaymentSuccess}
onPaymentFailure={this.handlePaymentFailure}
amount={this.state.amount}
details={this.props.route.params.data}
/>
<HideWithKeyboard>
<Footer
activepage="Managecart"
usertype={1}
footerpage={[
{
name: 'Home',
fname: 'Home',
countshow: false,
image: localimag.home,
activeimage: localimag.home,
},
{
name: 'MyCart',
fname: 'My Cart',
countshow: false,
image: localimag.cart,
activeimage: localimag.cart_fill,
},
{
name: 'OrderHistory',
fname: 'Order History',
countshow: false,
image: localimag.calender,
activeimage: localimag.calender_fill,
},
{
name: 'MyFavs',
fname: 'Favs',
countshow: false,
image: localimag.unfill,
activeimage: localimag.heart_fill,
},
{
name: 'Profile',
fname: 'Profile',
countshow: false,
image: localimag.profile_unfill,
activeimage: localimag.profile_unfill,
},
]}
navigation={this.props.navigation}
imagestyle1={{
width: 25,
height: 25,
backgroundColor: Colors.white_color,
countcolor: 'red',
countbackground: 'red',
}}
/>
</HideWithKeyboard>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment