ข้ามไปยังเนื้อหาหลัก

React Native - การรับชำระเงิน

เรียนรู้วิธีการรับชำระเงินในแอปพลิเคชัน React Native โดยใช้ Omise คำแนะนำนี้ให้กลยุทธ์การใช้งานที่สมบูรณ์สำหรับการรวมการชำระเงินข้ามแพลตฟอร์มที่สนับสนุนทั้ง iOS และ Android

ภาพรวม

การรวม Omise ในแอปพลิเคชัน React Native ต้องรวมความสามารถของ SDK ในตัวกับรหัส JavaScript/TypeScript วิธีนี้ให้กลยุทธ์การใช้งานที่สมบูรณ์สำหรับการรับชำระเงินข้ามแพลตฟอร์ม

คุณสมบัติหลัก

  • การสนับสนุนข้ามแพลตฟอร์ม - รหัสฐานเดียวสำหรับ iOS/Android
  • ประสิทธิภาพในตัว - ใช้ประโยชน์จาก SDK ในตัวภายใต้ประทุน
  • การสนับสนุน TypeScript - รวมนิยามประเภทแบบเต็ม
  • React Hooks - รูปแบบและ Hooks ของ React สมัยใหม่
  • การจัดการ 3D Secure - การไหลการรับรองอย่างราบรื่น
  • เข้ากันได้ Expo - ทำงานกับเวิร์กโฟลว์ Expo ที่จัดการ

ข้อกำหนดเบื้องต้น

ก่อนที่จะใช้งานการรับชำระเงิน:

  1. ตั้งค่าสภาพแวดล้อม React Native (0.64+)
  2. ตั้งค่าโมดูลเนทีฟสำหรับ iOS และ Android
  3. API ด้านหลังสำหรับการสร้างใบเรียกเก็บเงิน
  4. บัญชี Omise พร้อมกับคีย์ API

การติดตั้ง

ติดตั้งการพึ่งพาเนทีฟ

# ใช้ npm
npm install omise-react-native

# ใช้ yarn
yarn add omise-react-native

# ติดตั้งการพึ่งพา iOS
cd ios && pod install && cd ..

ตั้งค่า Android

เพิ่มสิ่งต่อไปนี้ไปยัง android/app/build.gradle:

dependencies {
implementation 'co.omise:omise-android:3.1.0'
}

ตั้งค่า iOS

การติดตั้ง CocoaPods จัดการการตั้งค่า iOS โดยอัตโนมัติ ให้แน่ใจว่าเวอร์ชัน iOS ขั้นต่ำใน ios/Podfile:

platform :ios, '12.0'

เชื่อมโมดูลเนทีฟ (React Native < 0.60)

react-native link omise-react-native

ขั้นตอนการชำระเงินพื้นฐาน

ตั้งค่า Omise Provider

import React from 'react';
import { OmiseProvider } from 'omise-react-native';

export default function App() {
return (
<OmiseProvider publicKey="pkey_test_123">
<AppNavigator />
</OmiseProvider>
);
}

สร้าง Payment Hook

import { useState, useCallback } from 'react';
import { createToken } from 'omise-react-native';
import { Alert } from 'react-native';

interface CardData {
number: string;
name: string;
expiryMonth: number;
expiryYear: number;
securityCode: string;
}

export function usePayment() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const processPayment = useCallback(
async (cardData: CardData, amount: number, currency: string) => {
setLoading(true);
setError(null);

try {
// ขั้นตอนที่ 1: สร้างโทเค็น
const token = await createToken({
card: {
name: cardData.name,
number: cardData.number,
expiration_month: cardData.expiryMonth,
expiration_year: cardData.expiryYear,
security_code: cardData.securityCode,
},
});

// ขั้นตอนที่ 2: สร้างใบเรียกเก็บเงินบนเซิร์ฟเวอร์ด้านหลัง
const charge = await createCharge({
token: token.id,
amount,
currency,
return_uri: 'myapp://payment/complete',
});

// ขั้นตอนที่ 3: จัดการ 3D Secure หากจำเป็น
if (charge.authorize_uri) {
await handle3DSecure(charge.authorize_uri);
}

return charge;
} catch (err: any) {
setError(err.message);
Alert.alert('Payment Error', err.message);
throw err;
} finally {
setLoading(false);
}
},
[]
);

return { processPayment, loading, error };
}

ส่วนประกอบแบบฟอร์มการชำระเงิน

import React, { useState } from 'react';
import {
View,
TextInput,
TouchableOpacity,
Text,
StyleSheet,
ActivityIndicator,
} from 'react-native';
import { usePayment } from './usePayment';

interface PaymentFormProps {
amount: number;
currency: string;
onSuccess: () => void;
}

export default function PaymentForm({
amount,
currency,
onSuccess,
}: PaymentFormProps) {
const [cardNumber, setCardNumber] = useState('');
const [cardholderName, setCardholderName] = useState('');
const [expiry, setExpiry] = useState('');
const [cvv, setCvv] = useState('');

const { processPayment, loading } = usePayment();

const handleSubmit = async () => {
// วิเคราะห์วันหมดอายุ
const [month, year] = expiry.split('/').map(s => parseInt(s.trim()));

// ตรวจสอบ
if (!validateForm()) {
return;
}

try {
await processPayment(
{
number: cardNumber.replace(/\s/g, ''),
name: cardholderName,
expiryMonth: month,
expiryYear: 2000 + year,
securityCode: cvv,
},
amount,
currency
);

onSuccess();
} catch (error) {
// ข้อผิดพลาดจัดการแล้วในฮุก
}
};

const validateForm = (): boolean => {
if (cardNumber.replace(/\s/g, '').length < 13) {
Alert.alert('Error', 'Invalid card number');
return false;
}
if (!cardholderName.trim()) {
Alert.alert('Error', 'Cardholder name required');
return false;
}
if (!expiry.match(/^\d{2}\/\d{2}$/)) {
Alert.alert('Error', 'Invalid expiry date (MM/YY)');
return false;
}
if (cvv.length < 3) {
Alert.alert('Error', 'Invalid CVV');
return false;
}
return true;
};

const formatCardNumber = (text: string) => {
const cleaned = text.replace(/\s/g, '');
const formatted = cleaned.match(/.{1,4}/g)?.join(' ') || cleaned;
setCardNumber(formatted);
};

const formatExpiry = (text: string) => {
const cleaned = text.replace(/\D/g, '');
if (cleaned.length >= 2) {
setExpiry(`${cleaned.slice(0, 2)}/${cleaned.slice(2, 4)}`);
} else {
setExpiry(cleaned);
}
};

return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Card Number"
value={cardNumber}
onChangeText={formatCardNumber}
keyboardType="number-pad"
maxLength={19}
editable={!loading}
/>

<TextInput
style={styles.input}
placeholder="Cardholder Name"
value={cardholderName}
onChangeText={setCardholderName}
autoCapitalize="words"
editable={!loading}
/>

<View style={styles.row}>
<TextInput
style={[styles.input, styles.halfInput]}
placeholder="MM/YY"
value={expiry}
onChangeText={formatExpiry}
keyboardType="number-pad"
maxLength={5}
editable={!loading}
/>

<TextInput
style={[styles.input, styles.halfInput]}
placeholder="CVV"
value={cvv}
onChangeText={setCvv}
keyboardType="number-pad"
maxLength={4}
secureTextEntry
editable={!loading}
/>
</View>

<TouchableOpacity
style={[styles.button, loading && styles.buttonDisabled]}
onPress={handleSubmit}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>
Pay {currency} {(amount / 100).toFixed(2)}
</Text>
)}
</TouchableOpacity>
</View>
);
}

const styles = StyleSheet.create({
container: {
padding: 16,
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 12,
marginBottom: 12,
fontSize: 16,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
},
halfInput: {
width: '48%',
},
button: {
backgroundColor: '#4CAF50',
padding: 16,
borderRadius: 8,
alignItems: 'center',
marginTop: 8,
},
buttonDisabled: {
backgroundColor: '#ccc',
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
},
});

การจัดการ 3D Secure

ไหลการ 3D Secure พร้อมกับ WebView

import React, { useRef } from 'react';
import { Modal, View, StyleSheet, TouchableOpacity, Text } from 'react-native';
import { WebView } from 'react-native-webview';

interface ThreeDSecureModalProps {
visible: boolean;
authorizeUri: string;
onComplete: () => void;
onCancel: () => void;
}

export default function ThreeDSecureModal({
visible,
authorizeUri,
onComplete,
onCancel,
}: ThreeDSecureModalProps) {
const webViewRef = useRef<WebView>(null);

const handleNavigationStateChange = (navState: any) => {
const { url } = navState;

// ตรวจสอบว่าผู้ใช้กลับมาจาก 3DS
if (url.startsWith('myapp://payment/complete')) {
onComplete();
}
};

return (
<Modal visible={visible} animationType="slide">
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Secure Authentication</Text>
<TouchableOpacity onPress={onCancel} style={styles.closeButton}>
<Text style={styles.closeText}>Cancel</Text>
</TouchableOpacity>
</View>

<WebView
ref={webViewRef}
source={{ uri: authorizeUri }}
onNavigationStateChange={handleNavigationStateChange}
style={styles.webview}
startInLoadingState
/>
</View>
</Modal>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#ddd',
},
title: {
fontSize: 18,
fontWeight: 'bold',
},
closeButton: {
padding: 8,
},
closeText: {
color: '#007AFF',
fontSize: 16,
},
webview: {
flex: 1,
},
});

ใช้โมดัล 3D Secure

import React, { useState } from 'react';
import { View } from 'react-native';
import PaymentForm from './PaymentForm';
import ThreeDSecureModal from './ThreeDSecureModal';

export default function CheckoutScreen() {
const [show3DS, setShow3DS] = useState(false);
const [authorizeUri, setAuthorizeUri] = useState('');
const [chargeId, setChargeId] = useState('');

const handlePaymentInitiated = async (charge: any) => {
if (charge.authorize_uri) {
setAuthorizeUri(charge.authorize_uri);
setChargeId(charge.id);
setShow3DS(true);
} else {
handlePaymentSuccess();
}
};

const handle3DSComplete = async () => {
setShow3DS(false);

// ตรวจสอบสถานะใบเรียกเก็บเงิน
try {
const charge = await verifyCharge(chargeId);

if (charge.status === 'successful') {
handlePaymentSuccess();
} else {
handlePaymentFailure('Payment verification failed');
}
} catch (error) {
handlePaymentFailure(error.message);
}
};

const handle3DSCancel = () => {
setShow3DS(false);
handlePaymentFailure('Authentication cancelled');
};

return (
<View style={{ flex: 1 }}>
<PaymentForm
amount={100000}
currency="THB"
onSuccess={handlePaymentInitiated}
/>

<ThreeDSecureModal
visible={show3DS}
authorizeUri={authorizeUri}
onComplete={handle3DSComplete}
onCancel={handle3DSCancel}
/>
</View>
);
}

ตั้งค่าการเชื่อมโยงลึก

ตั้งค่า iOS

เพิ่มต่อไปนี้ใน ios/YourApp/Info.plist:

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>

ตั้งค่า Android

เพิ่มต่อไปนี้ใน android/app/src/main/AndroidManifest.xml:

<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
</activity>

จัดการลิงก์ลึก

import { useEffect } from 'react';
import { Linking } from 'react-native';

export function useDeepLink(onLink: (url: string) => void) {
useEffect(() => {
// จัดการ URL เริ่มต้น
Linking.getInitialURL().then(url => {
if (url) {
onLink(url);
}
});

// จัดการการอัปเดต URL
const subscription = Linking.addEventListener('url', event => {
onLink(event.url);
});

return () => {
subscription.remove();
};
}, [onLink]);
}

// การใช้งาน
export default function App() {
useDeepLink(url => {
if (url.startsWith('myapp://payment/complete')) {
// จัดการการกลับมาของการชำระเงิน
verifyPaymentStatus();
}
});

return <AppNavigator />;
}

การชำระเงินการ์ดที่บันทึก

ส่วนประกอบรายการการ์ดที่บันทึก

import React, { useEffect, useState } from 'react';
import {
View,
Text,
FlatList,
TouchableOpacity,
StyleSheet,
Image,
} from 'react-native';

interface Card {
id: string;
brand: string;
last_digits: string;
expiration_month: number;
expiration_year: number;
}

interface SavedCardsListProps {
customerId: string;
onCardSelect: (card: Card) => void;
}

export default function SavedCardsList({
customerId,
onCardSelect,
}: SavedCardsListProps) {
const [cards, setCards] = useState<Card[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
loadCards();
}, [customerId]);

const loadCards = async () => {
try {
const response = await fetch(
`https://api.yourapp.com/customers/${customerId}/cards`
);
const data = await response.json();
setCards(data.data);
} catch (error) {
console.error('Failed to load cards:', error);
} finally {
setLoading(false);
}
};

const getCardIcon = (brand: string) => {
const icons = {
Visa: require('./assets/visa.png'),
MasterCard: require('./assets/mastercard.png'),
'American Express': require('./assets/amex.png'),
};
return icons[brand] || require('./assets/card-default.png');
};

const renderCard = ({ item }: { item: Card }) => (
<TouchableOpacity
style={styles.cardItem}
onPress={() => onCardSelect(item)}
>
<Image source={getCardIcon(item.brand)} style={styles.cardIcon} />
<View style={styles.cardInfo}>
<Text style={styles.cardBrand}>{item.brand}</Text>
<Text style={styles.cardNumber}>•••• {item.last_digits}</Text>
</View>
<Text style={styles.cardExpiry}>
{item.expiration_month}/{item.expiration_year % 100}
</Text>
</TouchableOpacity>
);

if (loading) {
return <ActivityIndicator />;
}

return (
<View style={styles.container}>
<Text style={styles.title}>Saved Cards</Text>
<FlatList
data={cards}
renderItem={renderCard}
keyExtractor={item => item.id}
ItemSeparatorComponent={() => <View style={styles.separator} />}
/>
</View>
);
}

const styles = StyleSheet.create({
container: {
padding: 16,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 16,
},
cardItem: {
flexDirection: 'row',
alignItems: 'center',
padding: 16,
backgroundColor: '#f5f5f5',
borderRadius: 8,
},
cardIcon: {
width: 40,
height: 25,
marginRight: 12,
},
cardInfo: {
flex: 1,
},
cardBrand: {
fontSize: 16,
fontWeight: '600',
},
cardNumber: {
fontSize: 14,
color: '#666',
marginTop: 2,
},
cardExpiry: {
fontSize: 14,
color: '#666',
},
separator: {
height: 12,
},
});

การเรียกเก็บเงินการ์ดที่บันทึก

async function chargeWithSavedCard(
customerId: string,
cardId: string,
amount: number,
currency: string
) {
try {
const response = await fetch('https://api.yourapp.com/charges', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
customer: customerId,
card: cardId,
amount,
currency,
return_uri: 'myapp://payment/complete',
}),
});

const charge = await response.json();

if (charge.authorize_uri) {
// จัดการ 3D Secure
return { charge, requires3DS: true };
}

return { charge, requires3DS: false };
} catch (error) {
throw new Error(`Failed to charge card: ${error.message}`);
}
}

การรวม API

สร้างบริการด้านหลัง

class OmiseService {
private baseUrl = 'https://api.yourapp.com';

async createCharge(params: {
token: string;
amount: number;
currency: string;
return_uri: string;
metadata?: Record<string, any>;
}) {
const response = await fetch(`${this.baseUrl}/charges`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${await getAuthToken()}`,
},
body: JSON.stringify(params),
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Failed to create charge');
}

return response.json();
}

async verifyCharge(chargeId: string) {
const response = await fetch(`${this.baseUrl}/charges/${chargeId}`, {
headers: {
Authorization: `Bearer ${await getAuthToken()}`,
},
});

if (!response.ok) {
throw new Error('Failed to verify charge');
}

return response.json();
}

async createCustomer(params: {
email: string;
description?: string;
metadata?: Record<string, any>;
}) {
const response = await fetch(`${this.baseUrl}/customers`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${await getAuthToken()}`,
},
body: JSON.stringify(params),
});

if (!response.ok) {
throw new Error('Failed to create customer');
}

return response.json();
}

async attachCard(customerId: string, tokenId: string) {
const response = await fetch(
`${this.baseUrl}/customers/${customerId}/cards`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${await getAuthToken()}`,
},
body: JSON.stringify({ card: tokenId }),
}
);

if (!response.ok) {
throw new Error('Failed to attach card');
}

return response.json();
}
}

export const omiseService = new OmiseService();

การจัดการข้อผิดพลาด

ยูทิลิตี้ตัวจัดการข้อผิดพลาด

export class PaymentError extends Error {
constructor(
message: string,
public code?: string,
public details?: any
) {
super(message);
this.name = 'PaymentError';
}
}

export function handlePaymentError(error: any): PaymentError {
if (error.response) {
// ข้อผิดพลาด API
const { code, message } = error.response.data;
return new PaymentError(message, code, error.response.data);
} else if (error.request) {
// ข้อผิดพลาดเครือข่าย
return new PaymentError('Network error. Please check your connection.');
} else {
// ข้อผิดพลาดอื่น ๆ
return new PaymentError(error.message || 'An unexpected error occurred');
}
}

export function getErrorMessage(error: PaymentError): string {
const errorMessages: Record<string, string> = {
invalid_card: 'Invalid card information',
insufficient_funds: 'Insufficient funds on card',
stolen_or_lost_card: 'Card reported as lost or stolen',
failed_processing: 'Payment processing failed',
};

return errorMessages[error.code || ''] || error.message;
}

กรณีการใช้งานทั่วไป

การซื้อครั้งเดียว

async function handleOneTimePurchase(
items: CartItem[],
cardData: CardData
) {
try {
// คำนวณยอดรวม
const total = items.reduce((sum, item) => sum + item.price, 0);

// สร้างโทเค็น
const token = await createToken({ card: cardData });

// สร้างใบเรียกเก็บเงิน
const charge = await omiseService.createCharge({
token: token.id,
amount: total,
currency: 'THB',
return_uri: 'myapp://payment/complete',
metadata: {
items: items.map(i => i.id),
},
});

return charge;
} catch (error) {
throw handlePaymentError(error);
}
}

ตั้งค่าการสมัครสมาชิก

async function setupSubscription(
plan: string,
cardData: CardData,
email: string
) {
try {
// สร้างลูกค้า
const customer = await omiseService.createCustomer({
email,
description: `${plan} subscription`,
});

// สร้างโทเค็นและแนบการ์ด
const token = await createToken({ card: cardData });
await omiseService.attachCard(customer.id, token.id);

// สร้างใบเรียกเก็บเงินครั้งแรก
const charge = await omiseService.createCharge({
token: token.id,
amount: getPlanAmount(plan),
currency: 'THB',
return_uri: 'myapp://payment/complete',
metadata: {
plan,
type: 'subscription',
},
});

// สร้างตารางเวลาบนเซิร์ฟเวอร์ด้านหลัง
await createSchedule(customer.id, plan);

return { customer, charge };
} catch (error) {
throw handlePaymentError(error);
}
}

แนวปฏิบัติที่ดีที่สุด

ความปลอดภัย

  1. อย่าเก็บข้อมูลที่ไว

    // ✅ ดี - ใช้โทเค็น
    const token = await createToken({ card: cardData });
    await sendToBackend(token.id);

    // ❌ ไม่ดี - อย่าเก็บข้อมูลการ์ด
    await AsyncStorage.setItem('cardNumber', cardNumber);
  2. ตรวจสอบอินพุต

    function validateCardNumber(number: string): boolean {
    const cleaned = number.replace(/\s/g, '');
    return /^\d{13,19}$/.test(cleaned) && luhnCheck(cleaned);
    }

    function luhnCheck(cardNumber: string): boolean {
    // ใช้อัลกอริธึม Luhn
    let sum = 0;
    let isEven = false;

    for (let i = cardNumber.length - 1; i >= 0; i--) {
    let digit = parseInt(cardNumber[i]);

    if (isEven) {
    digit *= 2;
    if (digit > 9) {
    digit -= 9;
    }
    }

    sum += digit;
    isEven = !isEven;
    }

    return sum % 10 === 0;
    }
  3. ใช้ HTTPS

    // บังคับใช้ HTTPS ในการผลิต
    const API_URL = __DEV__
    ? 'http://localhost:3000'
    : 'https://api.yourapp.com';

ประสิทธิภาพ

  1. การตรวจสอบการ์ดการดีบาウน์ด

    import { useMemo } from 'react';
    import debounce from 'lodash/debounce';

    const debouncedValidation = useMemo(
    () => debounce((number: string) => {
    setIsValid(validateCardNumber(number));
    }, 500),
    []
    );
  2. วิธีการชำระเงินแคช

    import { useQuery } from 'react-query';

    function useSavedCards(customerId: string) {
    return useQuery(
    ['cards', customerId],
    () => omiseService.getCards(customerId),
    {
    staleTime: 5 * 60 * 1000,
    cacheTime: 10 * 60 * 1000,
    }
    );
    }

การแก้ไขปัญหา

ปัญหาที่พบบ่อย

การสร้างโทเค็นล้มเหลว

// ตรวจสอบการตั้งค่าคีย์สาธารณะ
console.log('Public Key:', publicKey);

// ตรวจสอบข้อมูลการ์ด
console.log('Card Number Valid:', validateCardNumber(cardNumber));
console.log('CVV Valid:', /^\d{3,4}$/.test(cvv));

3D Secure ไม่ทำงาน

// ตรวจสอบว่า WebView ได้รับการตั้งค่าอย่างถูกต้อง
import { WebView } from 'react-native-webview';

// ตรวจสอบการตั้งค่าการเชื่อมโยงลึก
Linking.canOpenURL('myapp://payment/complete').then(supported => {
console.log('Deep linking supported:', supported);
});

ปัญหาการรวบรวม

# ล้างแคช
npm start -- --reset-cache

# สร้าง iOS ใหม่
cd ios && pod install && cd ..
npx react-native run-ios

# สร้าง Android ใหม่
cd android && ./gradlew clean && cd ..
npx react-native run-android

คำถามที่ถามบ่อย

คำถามทั่วไป

Q: SDK Omise React Native ได้รับการสนับสนุนอย่างเป็นทางการหรือไม่?

A: แม้ว่าจะไม่มี SDK Omise React Native เป็นทางการ คุณสามารถใช้โมดูลเนทีฟเพื่อเชื่อม iOS/Android SDK หรือใช้ REST API โดยตรง

Q: ฉันสามารถใช้ Expo ได้หรือไม่?

A: การสนับสนุนบางส่วน เวิร์กโฟลว์ Expo ที่จัดการมีข้อ จำกัด ด้วยโมดูลเนทีฟ ใช้เวิร์กโฟลว์เปล่าหรือสร้างโมดูลเนทีฟแบบกำหนดเอง

Q: ฉันจะทดสอบการชำระเงินได้อย่างไร?

A: ใช้ไพ่ทดสอบ 4242 4242 4242 4242 ที่มีวันหมดอายุใด ๆ ในอนาคตและ CVV 3 หลักใด ๆ ใช้คีย์สาธารณะทดสอบ (pkey_test_xxx)

Q: เวอร์ชัน React Native ขั้นต่ำคืออะไร?

A: React Native 0.64+ ถูกแนะนำสำหรับความเข้ากันได้ที่ดีที่สุดกับไลบรารีสมัยใหม่

Q: ฉันสามารถใช้ TypeScript ได้หรือไม่?

A: ใช่ TypeScript ได้รับการสนับสนุนอย่างเต็มที่และแนะนำสำหรับความปลอดภัยของประเภท

Q: ฉันจะจัดการการชำระเงินแบบออฟไลน์ได้อย่างไร?

A: เก็บความตั้งใจในการชำระเงินเฉพาะที่และประมวลผลเมื่อเชื่อมต่ออีกครั้ง ตรวจสอบสถานะหลังจากเชื่อมต่อใหม่เสมอ

คำถามการใช้งาน

Q: ฉันควรใช้ WebView สำหรับ 3D Secure หรือไม่?

A: ใช่ react-native-webview เป็นวิธีที่แนะนำสำหรับการจัดการขั้นตอนการรับรอง 3D Secure

Q: ฉันจะจัดการสกุลเงินที่แตกต่างกันได้อย่างไร?

A: ส่งรหัสสกุลเงินในคำขอเรียกเก็บเงิน จัดรูปแบบจำนวนเงินที่แสดงตามตำแหน่งที่ตั้งของผู้ใช้

Q: ฉันสามารถปรับแต่ง UI อย่างสมบูรณ์ได้หรือไม่?

A: ใช่ สร้างส่วนประกอบ UI ของคุณเองและใช้ SDK สำหรับการทำโทเค็นเท่านั้น

Q: ฉันจะจัดการปุ่มย้อนกลับ Android ระหว่าง 3DS ได้อย่างไร?

A: ใช้งานตัวจัดการปุ่มในโมดัล WebView เพื่อถือว่าเป็นการยกเลิก

ทรัพยากรที่เกี่ยวข้อง

ขั้นตอนถัดไป