UNPKG

zips-react-native-sdk-test

Version:

Lightweight ZIPS Payment Gateway SDK for React Native - Complete payment solution with card payments, wallet payments (AfrMoney & ZApp), netbanking, and native UI design

165 lines (164 loc) 8.18 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { useState, useCallback, useEffect } from 'react'; import Toast from 'react-native-toast-message'; import { Text, TouchableOpacity, ActivityIndicator, } from 'react-native'; import PaymentModal from './PaymentModal'; import { ShimmerButton } from './common/Shimmer'; import { QueryClientProvider } from '@tanstack/react-query'; import { queryClient } from '../tanstack-query/queryClient'; import { useZipsContext, ZipsProvider } from '../context/ZipsProvider'; import { NetBankingProvider, useNetBankingContext, } from '../context/NetBankingProvider'; import { WalletProvider } from '../context/WalletProvider'; import { CardProvider } from '../context/CardProvider'; import Zips from 'zips-typescript-sdk'; export default function PaymentButton(props) { return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ZipsProvider, { children: _jsx(NetBankingProvider, { children: _jsx(WalletProvider, { children: _jsx(CardProvider, { children: _jsx(PaymentButtonInner, { ...props }) }) }) }) }) })); } function PaymentButtonInner({ apiKey, paymentDetails, onSuccess, onError, buttonText = 'Pay Now', environment = 'sandbox', style, textStyle, disabledStyle, disabledTextStyle, loadingStyle, showLoadingIndicator = true, loadingIndicatorColor = '#FFFFFF', loadingIndicatorSize = 'small', disabled = false, loading: externalLoading = false, onPress, showShimmer = false, shimmerStyle, }) { const { isLoading, order, setTransactionDetails, setZips, setIsLoading, setPaymentMethod, error, setError, resetZips, } = useZipsContext(); const { resetNetBanking } = useNetBankingContext(); const [showModal, setShowModal] = useState(false); // Use external loading if provided, otherwise use internal loading state const loading = externalLoading || isLoading; const handleButtonPress = useCallback(() => { if (disabled || loading) return; // Clear error state when starting a new payment attempt if (error) { setError(null); } // Only call onPress if provided, but don't prevent modal opening if (onPress) { onPress(); } setShowModal(true); }, [disabled, loading, onPress, error, setError]); const handleModalClose = useCallback(() => { // Only reset states, don't clear transaction details that might be needed setShowModal(false); }, []); // Show shimmer if explicitly requested if (showShimmer) { return _jsx(ShimmerButton, { style: shimmerStyle }); } // Default button styles const defaultButtonStyle = { backgroundColor: '#007AFF', borderRadius: 12, paddingVertical: 12, paddingHorizontal: 20, minHeight: 44, alignItems: 'center', justifyContent: 'center', flexDirection: 'row', }; // Default text styles const defaultTextStyle = { color: '#FFFFFF', fontSize: 16, fontWeight: '600', textAlign: 'center', }; // Compute final button styles const buttonStyle = { ...defaultButtonStyle, ...(disabled && disabledStyle), ...(loading && loadingStyle), ...style, // User style overrides everything }; const textStyleFinal = { ...defaultTextStyle, ...(disabled && disabledTextStyle), ...textStyle, // User text style overrides everything }; const verifyPaymentDetails = async () => { try { // Perform verification logic here const zips = new Zips(apiKey); const payment = await zips.payments.create({ amount: paymentDetails.amount, currency: paymentDetails.currency || 'GMD', firstName: paymentDetails.firstName, lastName: paymentDetails.lastName, phoneNumber: paymentDetails.phoneNumber, merchantAccountId: paymentDetails.merchantAccountId, name: paymentDetails.name, projectId: paymentDetails.projectId, quantity: paymentDetails.quantity, description: paymentDetails.description, }); if (!payment) { throw new Error('Payment creation failed'); } const transaction = await zips.transactions.single(payment === null || payment === void 0 ? void 0 : payment.referenceNumber); if (!transaction) { throw new Error('Transaction not found'); } console.log(transaction === null || transaction === void 0 ? void 0 : transaction.data); setTransactionDetails(transaction === null || transaction === void 0 ? void 0 : transaction.data); } catch (error) { // Suppress console errors in development to prevent error overlay if (__DEV__) { console.warn('Payment initialization error:', (error === null || error === void 0 ? void 0 : error.message) || error); } // Handle error gracefully setError(error); } finally { setIsLoading(false); } }; useEffect(() => { if (apiKey) { verifyPaymentDetails(); setZips({ apiKey, paymentDetails, environment, }); } }, [apiKey, environment, paymentDetails]); useEffect(() => { if (order) { Toast.show({ type: 'success', text1: 'Payment Successful', text2: `Your payment of ${order.amount} was successful.`, swipeable: true, autoHide: true, avoidKeyboard: true, visibilityTime: 2000, }); setTimeout(() => { onSuccess(order); setShowModal(false); // Reset states after successful payment resetZips(); resetNetBanking(); }, 2000); } }, [order, onSuccess, resetZips, resetNetBanking]); useEffect(() => { var _a, _b, _c, _d, _e, _f, _g; if (error) { // Only show toast for backend errors (not network/axios errors) const isBackendError = ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) && ((_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data); if (isBackendError) { Toast.show({ type: 'error', text1: 'Payment Error', text2: ((_e = (_d = (_c = error === null || error === void 0 ? void 0 : error.response) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.message) === null || _e === void 0 ? void 0 : _e.toString()) || ((_g = (_f = error === null || error === void 0 ? void 0 : error.response) === null || _f === void 0 ? void 0 : _f.data) === null || _g === void 0 ? void 0 : _g.toString()) || 'An error occurred during payment processing', swipeable: true, autoHide: true, avoidKeyboard: true, }); } // Always call onError for developer handling onError(error); } }, [error, onError]); return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, { style: buttonStyle, onPress: handleButtonPress, disabled: disabled || loading || isLoading, activeOpacity: 0.8, accessibilityRole: "button", accessibilityState: { disabled: disabled || loading }, accessibilityLabel: buttonText, children: [loading && showLoadingIndicator && (_jsx(ActivityIndicator, { size: loadingIndicatorSize, color: loadingIndicatorColor, style: { marginRight: 8 } })), _jsx(Text, { style: textStyleFinal, children: buttonText })] }), _jsx(PaymentModal, { visible: showModal, onClose: handleModalClose })] })); }