react-native-kora-checkout
Version:
A React Native SDK for integrating the Kora payment gateway, enabling secure and seamless payment processing in mobile applications. This SDK provides an easy-to-use interface for initializing payments, managing transaction events, and customizing payment
264 lines (257 loc) • 8.76 kB
JavaScript
"use strict";
import { useRef, useCallback, useState } from 'react';
import WebView from 'react-native-webview';
import { StyleSheet, View, Dimensions } from 'react-native';
import { jsx as _jsx } from "react/jsx-runtime";
export const useKoraCheckout = ({
paymentDetails,
onClose,
onSuccess,
onFailed,
onPending,
onTokenized
}) => {
const webViewRef = useRef(null);
const [isCheckoutVisible, setIsCheckoutVisible] = useState(false);
// Helper function to safely stringify values for injection
const safeStringify = value => {
if (value === undefined || value === null) return 'undefined';
if (typeof value === 'string') return `"${value.replace(/"/g, '\\"')}"`;
return JSON.stringify(value);
};
const injectedJavaScript = `
(function() {
const script = document.createElement('script');
script.src = 'https://korablobstorage.blob.core.windows.net/modal-bucket/korapay-collections.min.js';
script.async = true;
script.onload = function () {
if (window.Korapay) {
window.ReactNativeWebView.postMessage('SCRIPT_LOADED');
try {
const koraConfig = {
key: ${safeStringify(paymentDetails.publicKey)},
reference: ${safeStringify(paymentDetails.reference)},
amount: ${paymentDetails.amount},
currency: ${safeStringify(paymentDetails.currency || 'NGN')},
customer: {
name: ${safeStringify(paymentDetails.customer.name)},
email: ${safeStringify(paymentDetails.customer.email)}
},
${paymentDetails.notification_url ? `notification_url: ${safeStringify(paymentDetails.notification_url)},` : ''}
${paymentDetails.narration ? `narration: ${safeStringify(paymentDetails.narration)},` : ''}
${paymentDetails.channels ? `channels: ${JSON.stringify(paymentDetails.channels)},` : ''}
${paymentDetails.default_channel ? `default_channel: ${safeStringify(paymentDetails.default_channel)},` : ''}
${paymentDetails.metadata ? `metadata: ${JSON.stringify(paymentDetails.metadata)},` : ''}
${paymentDetails.containerId ? `containerId: ${safeStringify(paymentDetails.containerId)},` : ''}
${paymentDetails.merchant_bears_cost !== undefined ? `merchant_bears_cost: ${paymentDetails.merchant_bears_cost},` : ''}
onClose: function () {
window.ReactNativeWebView.postMessage('PAYMENT_CLOSED');
},
onSuccess: function (data) {
window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'SUCCESS', data }));
},
onFailed: function (data) {
window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'FAILED', data }));
},
onPending: function (data) {
window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'PENDING', data }));
},
onTokenized: function (data) {
window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'TOKENIZED', data }));
}
};
// Remove undefined properties
Object.keys(koraConfig).forEach(key => {
if (koraConfig[key] === undefined) {
delete koraConfig[key];
}
});
window.Korapay.initialize(koraConfig);
} catch (err) {
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'ERROR',
data: { message: err.message, stack: err.stack }
}));
}
} else {
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'ERROR',
data: { message: 'Korapay not found after script load' }
}));
}
};
script.onerror = function (err) {
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'ERROR',
data: { message: 'Failed to load Kora script', error: err.toString() }
}));
};
document.body.appendChild(script);
})();
true;
`;
const htmlContent = `
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
body {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
#payment-root {
width: 100%;
height: 100%;
position: relative;
}
/* Loading indicator */
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
color: white;
font-size: 16px;
}
.spinner {
border: 2px solid #f3f3f3;
border-top: 2px solid #3498db;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin-right: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div id="payment-root">
<div class="loading">
<div class="spinner"></div>
Loading payment options...
</div>
</div>
</body>
</html>
`;
const handleMessage = useCallback(event => {
const message = event.nativeEvent.data;
try {
const parsedMessage = JSON.parse(message);
switch (parsedMessage.type) {
case 'SUCCESS':
setIsCheckoutVisible(false);
onSuccess?.(parsedMessage.data);
break;
case 'FAILED':
setIsCheckoutVisible(false);
onFailed?.(parsedMessage.data);
break;
case 'PENDING':
onPending?.(parsedMessage.data);
break;
case 'TOKENIZED':
onTokenized?.(parsedMessage.data);
break;
case 'ERROR':
console.error('Kora Payment Error:', parsedMessage.data);
setIsCheckoutVisible(false);
onFailed?.(parsedMessage.data);
break;
default:
console.log('Unknown message type:', parsedMessage.type);
}
} catch (e) {
if (message === 'PAYMENT_CLOSED') {
setIsCheckoutVisible(false);
onClose?.();
} else if (message === 'SCRIPT_LOADED') {
console.log('Kora script loaded successfully');
} else {
console.log('Unhandled message:', message);
}
}
}, [onClose, onSuccess, onFailed, onPending, onTokenized]);
const initiatePayment = useCallback(() => {
setIsCheckoutVisible(true);
}, []);
const closePayment = useCallback(() => {
setIsCheckoutVisible(false);
onClose?.();
}, [onClose]);
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.8)',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1000
},
webview: {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
backgroundColor: 'transparent'
}
});
const CheckoutComponent = useCallback(() => isCheckoutVisible ? /*#__PURE__*/_jsx(View, {
style: styles.container,
children: /*#__PURE__*/_jsx(WebView, {
ref: webViewRef,
source: {
html: htmlContent
},
injectedJavaScript: injectedJavaScript,
onMessage: handleMessage,
style: styles.webview,
scrollEnabled: false,
javaScriptEnabled: true,
domStorageEnabled: true,
startInLoadingState: true,
scalesPageToFit: false,
bounces: false,
onError: syntheticEvent => {
const {
nativeEvent
} = syntheticEvent;
console.error('WebView error: ', nativeEvent);
onFailed?.({
message: 'WebView failed to load',
description: nativeEvent.description
});
},
onHttpError: syntheticEvent => {
const {
nativeEvent
} = syntheticEvent;
console.error('WebView HTTP error: ', nativeEvent);
onFailed?.({
message: 'HTTP error occurred',
statusCode: nativeEvent.statusCode
});
}
})
}) : null, [htmlContent, injectedJavaScript, handleMessage, isCheckoutVisible, styles, onFailed]);
return {
CheckoutComponent,
initiatePayment,
closePayment,
isCheckoutVisible
};
};
//# sourceMappingURL=koraCheckout.js.map