On Android, I had been using expo-stripe-checkout for payments on an Expo managed app. Everything was working fine until I updated to the newest version of expo and react-native. Now, none of the callbacks, like onClose and onPaymentSuccess are working. It seems WebView was removed from react-native, so I'm importing directly from react-native-webview
.
Instead of using expo-stripe-checkout
, I created my own component, to try and work through what's happening, and it seems as if WebView's injectedJavaScript
isn't running at all, and therefore window.postMessage
or window.ReactNativeWebView.postMessage
isn't working.
package.json:
{
"name": "example",
"version": "0.0.6",
"private": true,
"devDependencies": {
"jest-expo": "^37.0.0",
"react-test-renderer": "16.0.0",
"schedule": "^0.4.0"
},
"main": "./node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"eject": "expo eject",
"android": "expo start --android",
"ios": "expo start --ios",
"test": "node node_modules/jest/bin/jest.js --watch",
"prettier": "./node_modules/prettier/bin-prettier.js --single-quote --trailing-comma es5 --print-width 100 --write 'app/**/*.js' -l"
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"@ptomasroos/react-native-multi-slider": "^1.0.0",
"@react-native-community/masked-view": "^0.1.10",
"@react-navigation/drawer": "^5.6.4",
"@react-navigation/native": "^5.2.3",
"@react-navigation/stack": "^5.2.18",
"axios": "^0.17.1",
"babel-plugin-transform-remove-console": "^6.9.4",
"emoji-utils": "^1.0.1",
"expo": "^37.0.0",
"expo-analytics": "^1.0.9",
"expo-apple-authentication": "^2.1.1",
"expo-av": "~8.1.0",
"expo-blur": "~8.1.0",
"expo-branch": "~2.1.0",
"expo-constants": "~9.0.0",
"expo-facebook": "~8.1.0",
"expo-font": "~8.1.0",
"expo-image-manipulator": "~8.1.0",
"expo-image-picker": "~8.1.0",
"expo-linear-gradient": "~8.1.0",
"expo-permissions": "~8.1.0",
"expo-stripe-checkout": "^1.0.1",
"immutability-helper": "^2.8.1",
"lottie-react-native": "~2.6.1",
"moment": "^2.22.1",
"prettier": "^1.12.1",
"react": "16.9.0",
"react-native": "0.61.4",
"react-native-animate-number": "^0.1.2",
"react-native-aws3": "0.0.9",
"react-native-branch": "4.2.1",
"react-native-calendars": "^1.20.0",
"react-native-circular-action-menu": "^0.5.0",
"react-native-circular-progress": "^1.0.1",
"react-native-datepicker": "^1.7.2",
"react-native-gesture-handler": "^1.6.1",
"react-native-gifted-chat": "0.13.0",
"react-native-google-places-autocomplete": "^1.4.0",
"react-native-invertible-scroll-view": "^1.1.1",
"react-native-keyboard-aware-scroll-view": "0.9.1",
"react-native-maps": "0.26.1",
"react-native-modal": "^11.5.3",
"react-native-modal-dropdown": "^0.6.2",
"react-native-reanimated": "^1.8.0",
"react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.7.0",
"react-native-scroll-into-view": "^0.1.4",
"react-native-snap-carousel": "^3.7.2",
"react-native-status-bar-height": "^3.0.0-alpha.1",
"react-native-svg": "11.0.1",
"react-native-svg-icon": "^0.8.1",
"react-native-swipe-gestures": "^1.0.5",
"react-native-unimodules": "~0.8.1",
"react-native-webview": "^10.3.1",
"react-redux": "^5.0.7",
"react-timer-mixin": "^0.13.4",
"react-visibility-sensor": "^4.1.0",
"redux": "^4.0.0",
"redux-thunk": "^2.3.0",
"sentry-expo": "~2.0.0",
"socket.io-client": "^2.0.4"
}
}
I've tried several different suggestions from around the web, but nothing has worked:
StripeCheckout.js
import React, { Component } from 'react';
import { Platform, View, ViewPropTypes } from 'react-native';
import { PropTypes } from 'prop-types';
import { WebView } from 'react-native-webview'
class StripeCheckout extends Component {
render() {
const {
publicKey,
amount,
allowRememberMe,
currency,
description,
imageUrl,
storeName,
prepopulatedEmail,
style,
onPaymentSuccess,
onClose
} = this.props;
const jsCode = `(function() {
console.log('hello')
var originalPostMessage = window.ReactNativeWebView.postMessage;
var patchedPostMessage = function(message, targetOrigin, transfer) {
originalPostMessage(message, targetOrigin, transfer);
};
patchedPostMessage.toString = function() {
return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
};
window.ReactNativeWebView.postMessage = patchedPostMessage;
})();`;
const runFirst = `
console.log('firstRun')
window.isNativeApp = true;
true; // note: this is required, or you'll sometimes get silent failures
`;
return (
<WebView
ref={(ref) => { this.webview = ref; }}
javaScriptEnabled={true}
injectedJavaScriptForMainFrameOnly={false}
scrollEnabled={false}
bounces={false}
messagingEnabled={true}
onMessage={(event) => console.log(event)}
onPaymentSuccess(event.nativeEvent.data)}
injectedJavaScriptBeforeContentLoaded={runFirst}
injectedJavaScript={jsCode}
source={{ html: `<script src="https://checkout.stripe.com/checkout.js"></script>
<script>
var handler = StripeCheckout.configure({
key: '${publicKey}',
image: '${imageUrl}',
locale: 'auto',
token: function(token) {
window.ReactNativeWebView.postMessage(token.id, token.id);
},
});
window.onload = function() {
console.log('onload')
handler.open({
image: '${imageUrl}',
name: '${storeName}',
description: '${description}',
amount: ${amount},
currency: '${currency}',
allowRememberMe: ${allowRememberMe},
email: '${prepopulatedEmail}',
closed: function() {
window.ReactNativeWebView.postMessage("WINDOW_CLOSED", "*");
}
});
};
</script>`}}
style={[{ flex: 1 }, style]}
scalesPageToFit={Platform.OS === 'android'}
/>
);
}
}
StripeCheckout.propTypes = {
publicKey: PropTypes.string.isRequired,
amount: PropTypes.number.isRequired,
imageUrl: PropTypes.string.isRequired,
storeName: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
allowRememberMe: PropTypes.bool.isRequired,
onPaymentSuccess: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
currency: PropTypes.string,
prepopulatedEmail: PropTypes.string,
style: ViewPropTypes.object
};
StripeCheckout.defaultProps = {
prepopulatedEmail: '',
currency: 'USD',
};
export default StripeCheckout;
None of the console's are logging. The stripe integration pops up and returns a token successfully, but then nothing happens, and the component isn't receiving the token (i can see it's returning a token from the stripe logs).
Let me know if there's anything else I should post. Thanks!
A few things off the top of my head:
"react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
(Expo SDK 37 is already based on 0.61.4, but with Expo-specific mods). Getting expo-stripe-checkout working as before in previous versions may be as simple as this. The SDK 37 announcement blog post gives more info on the SDK upgrade process, namely:
- Run
expo upgrade
in your project directory (requires the latest version of expo-cli, you can update withnpm i -g expo-cli
).- Make sure to check the changelog for other breaking changes!
- ...
expo-checkout-stripe
working again (fix your React Native version anyway, you're bound to have more issues with Expo if you don't), try one or both of the following with your webview:
injectJavaScript
as per (Guide > Communicating between JS and Native > The injectJavaScript method)[https://github.com/react-native-community/react-native-webview/blob/master/docs/Guide.md#the-injectjavascript-method"]. In your case, that would be something like
this.webview.injectJavaScript(jsCode)
I hope one of these suggestions at least gets you on the right path.