@lmapp/react-native-cloudpayments
Version:
π ΠΠΎΡΠ½ΡΠΉ SDK Π΄Π»Ρ ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠΈ ΠΏΠ»Π°ΡΠ΅ΠΆΠ΅ΠΉ CloudPayments Π² React Native. ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° Apple Pay, Google Pay, Π‘ΠΠ, Π±Π°Π½ΠΊΠΎΠ²ΡΠΊΠΈΡ ΠΊΠ°ΡΡ. ΠΠΎΠ»Π½Π°Ρ ΡΠΈΠΏΠΈΠ·Π°ΡΠΈΡ TypeScript. iOS 12+ ΠΈ Android 21+
303 lines (267 loc) β’ 9.74 kB
text/typescript
/**
* @fileoverview Π₯ΡΠΊ Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΎΠ±ΡΡΠΈΡΠΌΠΈ CloudPayments SDK
* @description ΠΠ°Π·ΠΎΠ²ΡΠΉ Ρ
ΡΠΊ Π΄Π»Ρ ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠΈ Π½Π° ΡΠΎΠ±ΡΡΠΈΡ ΠΈ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ΠΌ SDK
* @author Leonid Molchanov
* @since 1.0.0
*/
import { useCallback, useEffect, useRef, useState } from 'react';
import type { EmitterSubscription } from 'react-native';
import { eventEmitter } from '../../events';
import { EPaymentFormEventName, EPaymentFormErrorCode } from '../../types';
import type { IPaymentFormEvent } from '../../types';
import type {
TCloudPaymentsStatus,
ICloudPaymentsBaseState,
ICloudPaymentsError,
ICloudPaymentsProgress,
IUseCloudPaymentsEventsOptions,
IUseCloudPaymentsEventsReturn,
} from './types';
// ============================================================================
// CONSTANTS
// ============================================================================
/**
* Π‘ΠΎΠ±ΡΡΠΈΡ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ Π΄Π»Ρ ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠΈ
* @description ΠΠ°Π·ΠΎΠ²ΡΠΉ Π½Π°Π±ΠΎΡ ΡΠΎΠ±ΡΡΠΈΠΉ, Π½Π° ΠΊΠΎΡΠΎΡΡΠ΅ ΠΏΠΎΠ΄ΠΏΠΈΡΡΠ²Π°Π΅ΡΡΡ Ρ
ΡΠΊ Π΅ΡΠ»ΠΈ Π½Π΅ ΡΠΊΠ°Π·Π°Π½Ρ enabledEvents
*/
const DEFAULT_ENABLED_EVENTS: EPaymentFormEventName[] = [
EPaymentFormEventName.PAYMENT_FORM,
];
/**
* ΠΠ°ΡΠ°Π»ΡΠ½ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ SDK
*/
const INITIAL_STATE: ICloudPaymentsBaseState = {
isLoading: false,
isError: false,
isSuccess: false,
status: 'idle',
error: null,
transactionId: null,
progress: null,
};
// ============================================================================
// HOOK IMPLEMENTATION
// ============================================================================
/**
* Π₯ΡΠΊ Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΎΠ±ΡΡΠΈΡΠΌΠΈ CloudPayments SDK
*
* @description ΠΠ°Π·ΠΎΠ²ΡΠΉ Ρ
ΡΠΊ Π΄Π»Ρ ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠΈ Π½Π° ΡΠΎΠ±ΡΡΠΈΡ SDK ΠΈ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ΠΌ.
* ΠΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΠΌΠ΅ΡΠΎΠ΄Ρ Π΄Π»Ρ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ ΠΈ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΏΠΎΠ΄ΠΏΠΈΡΡΠ²Π°Π΅ΡΡΡ
* Π½Π° ΡΠΎΠ±ΡΡΠΈΡ ΠΏΠ»Π°ΡΠ΅ΠΆΠ½ΠΎΠΉ ΡΠΎΡΠΌΡ.
*
* @param options - ΠΠΏΡΠΈΠΈ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ Ρ
ΡΠΊΠ°
* @returns ΠΠ±ΡΠ΅ΠΊΡ Ρ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ΠΌ ΠΈ ΠΌΠ΅ΡΠΎΠ΄Π°ΠΌΠΈ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ
*
* @example ΠΠ°Π·ΠΎΠ²ΠΎΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
* ```typescript
* const { state, actions } = useCloudPaymentsEvents({
* onSuccess: (data) => console.log('Π£ΡΠΏΠ΅Ρ
:', data.transactionId),
* onError: (data) => console.log('ΠΡΠΈΠ±ΠΊΠ°:', data.message)
* });
*
* // ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ΠΌ
* const handleStart = () => {
* actions.setStatus('processing');
* };
*
* const handleError = (message: string) => {
* actions.setError(message, EPaymentFormErrorCode.PAYMENT_FAILED);
* };
* ```
*
* @example ΠΠ°ΡΡΠΎΠΌΠ½ΡΠ΅ ΡΠΎΠ±ΡΡΠΈΡ
* ```typescript
* const { state, actions } = useCloudPaymentsEvents({
* enabledEvents: [EPaymentFormEventName.PAYMENT_FORM],
* onSuccess: (data) => {
* actions.setStatus('success', { transactionId: data.transactionId });
* }
* });
* ```
*
* @since 1.0.0
*/
export const useCloudPaymentsEvents = (
options: IUseCloudPaymentsEventsOptions = {}
): IUseCloudPaymentsEventsReturn => {
// ============================================================================
// STATE & REFS
// ============================================================================
const [state, setState] = useState<ICloudPaymentsBaseState>(INITIAL_STATE);
const subscriptionsRef = useRef<EmitterSubscription[]>([]);
const transactionProcessedRef = useRef(false);
const {
enabledEvents = DEFAULT_ENABLED_EVENTS,
onSuccess,
onError,
onCancel,
} = options;
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
/**
* ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ Ρ
ΡΠΊΠ°
*/
const updateState = useCallback(
(updates: Partial<ICloudPaymentsBaseState>) => {
setState((prevState) => ({ ...prevState, ...updates }));
},
[]
);
/**
* Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΡΡΠ°ΡΡΡΠ° Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΌ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ΠΌ ΡΠ»Π°Π³ΠΎΠ²
*/
const setStatus = useCallback(
(
status: TCloudPaymentsStatus,
additionalUpdates: Partial<ICloudPaymentsBaseState> = {}
) => {
updateState({
status,
isLoading: status === 'initializing' || status === 'processing',
isError: status === 'error',
isSuccess: status === 'success',
...additionalUpdates,
});
},
[updateState]
);
/**
* Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΠΎΡΠΈΠ±ΠΊΠΈ
*/
const setError = useCallback(
(
message: string,
code?: EPaymentFormErrorCode,
details?: Record<string, any>
) => {
const error: ICloudPaymentsError = { message, code, details };
setStatus('error', { error });
},
[setStatus]
);
/**
* Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΠΏΡΠΎΠ³ΡΠ΅ΡΡΠ°
*/
const setProgress = useCallback(
(stage: string, percentage?: number) => {
const progress: ICloudPaymentsProgress = { stage, percentage };
updateState({ progress });
},
[updateState]
);
/**
* ΠΡΠΈΡΡΠΊΠ° ΡΠΎΡΡΠΎΡΠ½ΠΈΡ
*/
const resetState = useCallback(() => {
setState(INITIAL_STATE);
}, []);
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* ΠΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊ ΡΠΎΠ±ΡΡΠΈΠΉ ΠΏΠ»Π°ΡΠ΅ΠΆΠ½ΠΎΠΉ ΡΠΎΡΠΌΡ
*/
const handlePaymentFormEvent = useCallback(
(event: IPaymentFormEvent) => {
switch (event.action) {
case 'willDisplay':
setStatus('initializing');
break;
case 'didDisplay':
setStatus('processing');
break;
case 'willHide':
// Π€ΠΎΡΠΌΠ° Π³ΠΎΡΠΎΠ²ΠΈΡΡΡ ΠΊ ΡΠΊΡΡΡΠΈΡ
break;
case 'didHide':
// Π€ΠΎΡΠΌΠ° ΡΠΊΡΡΡΠ° - ΡΠ±ΡΠ°ΡΡΠ²Π°Π΅ΠΌ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ Π΅ΡΠ»ΠΈ Π½Π΅ Π±ΡΠ»ΠΎ ΡΡΠ°Π½Π·Π°ΠΊΡΠΈΠΈ
if (!transactionProcessedRef.current) {
setStatus('cancelled');
onCancel?.();
}
// Π‘Π±ΡΠ°ΡΡΠ²Π°Π΅ΠΌ ΡΠ»Π°Π³ Π΄Π»Ρ ΡΠ»Π΅Π΄ΡΡΡΠ΅Π³ΠΎ ΠΏΠ»Π°ΡΠ΅ΠΆΠ°
transactionProcessedRef.current = false;
break;
case 'cancelled':
// ΠΠΠΠΠ: ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠΎΠ±ΡΡΠΈΡ ΠΎΡΠΌΠ΅Π½Ρ ΠΏΠ»Π°ΡΠ΅ΠΆΠ°
transactionProcessedRef.current = true;
setStatus('cancelled');
onCancel?.();
break;
case 'transaction':
// ΠΡΠΌΠ΅ΡΠ°Π΅ΠΌ ΡΡΠΎ ΡΡΠ°Π½Π·Π°ΠΊΡΠΈΡ Π±ΡΠ»Π° ΠΎΠ±ΡΠ°Π±ΠΎΡΠ°Π½Π°
transactionProcessedRef.current = true;
if (event.statusCode) {
// Π£ΡΠΏΠ΅ΡΠ½Π°Ρ ΡΡΠ°Π½Π·Π°ΠΊΡΠΈΡ
const transactionId = event.transactionId || null;
setStatus('success', { transactionId });
if (onSuccess && transactionId) {
onSuccess({ transactionId, message: event.message });
}
} else {
// ΠΡΠΈΠ±ΠΊΠ° ΡΡΠ°Π½Π·Π°ΠΊΡΠΈΠΈ
const message =
event.message || 'ΠΡΠΎΠΈΠ·ΠΎΡΠ»Π° ΠΎΡΠΈΠ±ΠΊΠ° ΠΏΡΠΈ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΏΠ»Π°ΡΠ΅ΠΆΠ°';
const code = event.errorCode;
setError(message, code);
if (onError) {
onError({ message, code });
}
}
break;
default:
console.warn('ΠΠ΅ΠΈΠ·Π²Π΅ΡΡΠ½ΠΎΠ΅ Π΄Π΅ΠΉΡΡΠ²ΠΈΠ΅ ΠΏΠ»Π°ΡΠ΅ΠΆΠ½ΠΎΠΉ ΡΠΎΡΠΌΡ:', event.action);
}
},
[setStatus, setError, onSuccess, onError, onCancel]
);
// ============================================================================
// EFFECTS
// ============================================================================
/**
* ΠΠΎΠ΄ΠΏΠΈΡΠΊΠ° Π½Π° ΡΠΎΠ±ΡΡΠΈΡ ΠΏΡΠΈ ΠΌΠΎΠ½ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°
*/
useEffect(() => {
const subscriptions: EmitterSubscription[] = [];
// ΠΠΎΠ΄ΠΏΠΈΡΡΠ²Π°Π΅ΠΌΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π½Π° ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΠΌΡΠ΅ ΡΠΎΠ±ΡΡΠΈΡ
enabledEvents.forEach((eventName) => {
switch (eventName) {
case EPaymentFormEventName.PAYMENT_FORM:
subscriptions.push(
eventEmitter.addListener(eventName, handlePaymentFormEvent)
);
break;
case EPaymentFormEventName.PAYMENT:
case EPaymentFormEventName.CARD:
case EPaymentFormEventName.THREE_DS:
// ΠΠΎΠΊΠ° Π½Π΅ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Ρ, Π½ΠΎ Π³ΠΎΡΠΎΠ²Ρ ΠΊ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΡ
console.log(
`Π‘ΠΎΠ±ΡΡΠΈΠ΅ ${eventName} Π±ΡΠ΄Π΅Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠ°Π½ΠΎ Π² Π±ΡΠ΄ΡΡΠΈΡ
Π²Π΅ΡΡΠΈΡΡ
`
);
break;
default:
console.warn(`ΠΠ΅ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΠΌΠΎΠ΅ ΡΠΎΠ±ΡΡΠΈΠ΅: ${eventName}`);
}
});
subscriptionsRef.current = subscriptions;
// ΠΡΠΈΡΡΠΊΠ° ΠΏΠΎΠ΄ΠΏΠΈΡΠΎΠΊ ΠΏΡΠΈ ΡΠ°Π·ΠΌΠΎΠ½ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ
return () => {
subscriptions.forEach((subscription) => subscription.remove());
subscriptionsRef.current = [];
};
}, [enabledEvents, handlePaymentFormEvent]);
// ============================================================================
// RETURN
// ============================================================================
return {
state,
actions: {
setStatus,
setError,
setProgress,
updateState,
resetState,
},
};
};