Created
October 9, 2019 14:15
-
-
Save mingca/48449d49e372e3a6ea344f984de34fcf to your computer and use it in GitHub Desktop.
Authorize.net Accept.js React Native
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Accept.js Webview implementation HOC | |
* | |
* Inspired by https://gist.github.com/mingca/3bc6e349a80a2957dfdc4b59c7154bec | |
*/ | |
import React, { Component } from 'react'; | |
import PropTypes from 'prop-types'; | |
import { | |
ScrollView, | |
View, | |
StyleSheet, | |
WebView | |
} from 'react-native'; | |
import { bindActionCreators } from 'redux'; | |
import { connect } from 'react-redux'; | |
import { stopSubmit } from 'redux-form' | |
import { Actions } from 'react-native-router-flux'; | |
import { Deferred } from 'es6-deferred-promise' | |
import NewPaymentMethod from './NewPaymentMethodContainer' | |
import { createPaymentThunk } from '../reducer'; | |
import { appMsg } from '@containers/ui/MessageBar/reducer'; | |
import { AppStyles, AppColors, AppFonts } from '@theme/'; | |
import { AppConfig } from '@constants/'; | |
const styles = StyleSheet.create({ | |
webViewContainer: { | |
flex: 1, | |
height: 100, | |
display: 'none', | |
}, | |
}); | |
class AcceptHoc extends Component { | |
constructor(props) { | |
super(props); | |
this.onWebViewMessage = this.onWebViewMessage.bind(this); | |
this.sendToBridge = this.sendToBridge.bind(this); | |
this.state = { webviewLoading: true, webviewError: false }; | |
} | |
/* Receive all events from webview */ | |
onWebViewMessage(event) { | |
console.log("Message received from webview"); | |
/* TODO: get initial LOADED_SIGNAL from accept_hosted to ensure correct page is loaded */ | |
let msgData; | |
try { | |
msgData = JSON.parse(event.nativeEvent.data); | |
} catch (err) { | |
console.warn(err); | |
return; | |
} | |
console.log(`Message from web view ${event.nativeEvent.data}`); | |
this.deferred.resolve(msgData); | |
} | |
sendToBridge(data) { | |
this.deferred = new Deferred(); | |
/* Check Webview is loaded */ | |
if (this.state.webviewLoading) { | |
this.props.actions.appMsg('Page not fully loaded. Plz try again.', 'warning'); | |
this.deferred.reject('WebView not loaded'); | |
} | |
if (this.state.webviewError) { | |
this.props.actions.appMsg('Error while fetching Authorize.net library', 'error'); | |
this.deferred.reject('WebView not loaded'); | |
} | |
const promiseHandled = this.deferred.promise.then(response => { | |
/* Can't reach Authorize.net on webview */ | |
if (response.error) { | |
this.props.actions.appMsg(response.error, 'Can\'t reach Authroize.net'); | |
throw new Error(); | |
} | |
/* Response from Authorize.net */ | |
if (response.messages.resultCode === 'Error') { | |
this.props.actions.stopSubmit('paymentForm', { _error: AcceptHoc.parseErrors(response) }); | |
throw new Error(); | |
} | |
/* Send payment nonce to our API backend */ | |
return this.props.actions.create(AcceptHoc.buildRequestBody(data, response)); | |
}); | |
/* send card/bank form data to webview */ | |
/* TODO: Ensure this occures only once */ | |
this.myWebView.postMessage(JSON.stringify(AcceptHoc.refinedData(data))); | |
return promiseHandled; | |
} | |
render = () => ( | |
<ScrollView style={[AppStyles.container]}> | |
<NewPaymentMethod | |
id="create" | |
actions={{ | |
create: this.sendToBridge, | |
}} | |
/> | |
<View style={styles.webViewContainer}> | |
<WebView | |
ref={webview => { | |
this.myWebView = webview; | |
}} | |
scrollEnabled={false} | |
source={{uri: AppConfig.urls.ACCEPT_SELF_HOSTED}} | |
onMessage={this.onWebViewMessage} | |
style={styles.webViewContainer} | |
onLoad={this._onLoadEnd.bind(this)} | |
onError={this._onLoadError.bind(this)} | |
/> | |
</View> | |
</ScrollView> | |
); | |
_onLoadEnd() { | |
this.setState({ webviewLoading: false }); | |
} | |
_onLoadError() { | |
this.setState({ webviewError: true }); | |
} | |
/* Parse Accept Errors */ | |
static parseErrors(response) { | |
let i = 0 | |
const result = [] | |
while (i < response.messages.message.length) { | |
result.push(`${response.messages.message[i].code}: ${response.messages.message[i].text}`) | |
i += 1; | |
} | |
return result.join('\n'); | |
} | |
/* | |
Refines card/bank data before pass to Accept.js | |
Warning: Accept.js slightly fails when the values are not string(ex: Integer) | |
Ensure that they are String. | |
*/ | |
static refinedData(data) { | |
const authData = { | |
clientKey: AppConfig.authNet.AUTHNET_PUBLIC_CLIENT_KEY, | |
apiLoginID: AppConfig.authNet.AUTHNET_API_LOGIN_ID, | |
} | |
const newData = {} | |
newData.authData = authData | |
if (data.profile_type === 'card') { | |
const { addressAttributes, ...cardDataWithoutAddress } = data.cardData; | |
newData.cardData = cardDataWithoutAddress | |
newData.cardData.cardNumber = newData.cardData.cardNumber.replace(/ /g, '') | |
} else { | |
newData.bankData = data.bankData | |
} | |
return newData | |
} | |
/* Build body to send to Cobalt */ | |
static buildRequestBody(data, acceptResponse) { | |
let body = { profile_type: data.profile_type } | |
const { opaqueData } = acceptResponse | |
console.log(data.cardData); | |
if (data.profile_type === 'card') { | |
body = { | |
...body, | |
opaqueData, | |
card: { | |
month: data.cardData.month, | |
year: data.cardData.year, | |
address_attributes: data.cardData.addressAttributes, | |
}, | |
} | |
} else if (data.profile_type === 'ach') { | |
body = { | |
...body, | |
account: data.bankData.accountNumber, | |
routing: data.bankData.routingNumber, | |
bank_name: data.bankData.bankName, | |
account_holder: data.bankData.nameOnAccount, | |
account_type: data.bankData.accountType, | |
} | |
} | |
return body | |
} | |
} | |
AcceptHoc.propTypes = { | |
} | |
AcceptHoc.defaultProps = { | |
}; | |
const mapDispatchToProps = (dispatch) => ({ | |
actions: bindActionCreators({ | |
create: createPaymentThunk, | |
appMsg: appMsg, | |
stopSubmit: stopSubmit, | |
}, dispatch), | |
}); | |
export default connect(null, mapDispatchToProps)(AcceptHoc); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yeah Same what is that NewPaymentMethodContainer