React Native SDK
เรียนรู้วิธีการรับชำระเงินในแอปพลิเคชัน React Native โดยใช้ Omise คำแนะนำนี้ ให้กลยุทธ์การใช้งานที่สมบูรณ์สำหรับการรวมการชำระเงินข้ามแพลตฟอร์มที่สนับสนุนทั้ง iOS และ Android
ภาพรวม
การรวม Omise ในแอปพลิเคชัน React Native ต้องรวมความสามารถของ SDK ในตัวกับรหัส JavaScript/TypeScript วิธีนี้ให้กลยุทธ์การใช้งานที่สมบูรณ์สำหรับการรับชำระเงินข้ามแพลตฟอร์ม
คุณสมบัติหลัก
- การสนับสนุนข้ามแพลตฟอร์ม - รหัสฐานเดียวสำหรับ iOS/Android
- ประสิทธิภาพในตัว - ใช้ประโยชน์จาก SDK ในตัวภายใต้ประทุน
- การสนับสนุน TypeScript - รวมนิยามประเภทแบบเต็ม
- React Hooks - รูปแบบและ Hooks ของ React สมัยใหม่
- การจัดการ 3D Secure - การไหลการรับรองอย่างราบรื่น
- เข้ากันได้ Expo - ทำงานกับเวิร์กโฟลว์ Expo ที่จัดการ
ข้อกำหนดเบื้องต้น
ก่อนที่จะใช้งานการรับชำระเงิน:
- ตั้งค่าสภาพแวดล้อม React Native (0.64+)
- ตั้งค่าโมดูลเนทีฟสำหรับ iOS และ Android
- API ด้านหลังสำหรับการสร้างใบเรียกเก็บเงิน
- บัญชี 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);
}
}
แนวปฏิบัติที่ดีที่สุด
ความปลอดภัย
-
อย่าเก็บข้อมูลที่ไว
// ✅ ดี - ใช้โทเค็น
const token = await createToken({ card: cardData });
await sendToBackend(token.id);
// ❌ ไม่ดี - อย่าเก็บข้อมูลการ์ด
await AsyncStorage.setItem('cardNumber', cardNumber); -
ตรวจสอบอินพุต
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;
} -
ใช้ HTTPS
// บังคับใช้ HTTPS ในการผลิต
const API_URL = __DEV__
? 'http://localhost:3000'
: 'https://api.yourapp.com';
ประสิทธิภาพ
-
การตรวจสอบการ์ดการดีบาウน์ด
import { useMemo } from 'react';
import debounce from 'lodash/debounce';
const debouncedValidation = useMemo(
() => debounce((number: string) => {
setIsValid(validateCardNumber(number));
}, 500),
[]
); -
วิธีการชำระเงินแคช
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
คำถามที่ถามบ่อย
SDK Omise React Native ได้รับการสนับสนุนอย่างเป็นทางการหรือไม่?
แม้ว่าจะไม่มี SDK Omise React Native เป็นทางการ คุณสามารถใช้โมดูลเนทีฟเพื่อเชื่อม iOS/Android SDK หรือใช้ REST API โดยตรง
ฉันสามารถใช้ Expo ได้หรือไม่?
การสนับสนุนบางส่วน เวิร์กโฟลว์ Expo ที่จัดการมีข้อ จำกัด ด้วยโมดูลเนทีฟ ใช้เวิร์กโฟลว์เปล่าหรือสร้างโมดูลเนทีฟแบบกำหนดเอง
ฉันจะทดสอบการชำระเงินได้อย่างไร?
ใช้ไพ่ทดสอบ 4242 4242 4242 4242 ที่มีวันหมดอายุใด ๆ ในอนาคตและ CVV 3 หลักใด ๆ ใช้คีย์สาธารณะทดสอบ (pkey_test_xxx)
เวอร์ชัน React Native ขั้นต่ำคืออะไร?
React Native 0.64+ ถูกแนะนำสำหรับความเข้ากันได้ที่ดีที่สุดกับไลบรารีสมัยใหม่
ฉันสามารถใช้ TypeScript ได้หรือไม่?
ใช่ TypeScript ได้รับการสนับสนุนอย่างเต็มที่และแนะนำสำหรับความปลอดภัยของประเภท
ฉันจะจัดการการชำระเงินแบบออฟไลน์ได้อย่างไร?
เก็บความตั้งใจในการชำระเงินเฉพาะที่และประมวลผลเมื่อเชื่อมต่ออีกครั้ง ตรวจสอบสถานะหลังจากเชื่อมต่อใหม่เสมอ
คำถามการใช้งาน
ฉันควรใช้ WebView สำหรับ 3D Secure หรือไม่?
ใช่ react-native-webview เป็นวิธีที่แนะนำสำหรับการจัดการขั้นตอนการรับรอง 3D Secure
ฉันจะจัดการสกุลเงินที่แตกต่างกันได้อย่างไร?
ส่งรหัสสกุลเงินในคำขอเรียกเก็บเงิน จัดรูปแบบจำนวนเงินที่แสดงตามตำแหน่งที่ตั้งของผู้ใช้
ฉันสามารถปรับแต่ง UI อย่างสมบูรณ์ได้หรือไม่?
ใช่ สร้างส่วนประกอบ UI ของคุณเองและใช้ SDK สำหรับการทำโทเค็นเท่านั้น
ฉันจะจัดการปุ่มย้อนกลับ Android ระหว่าง 3DS ได้อย่างไร?
ใช้งานตัวจัดการปุ่มในโมดัล WebView เพื่อถือว่าเป็นการยกเลิก
ท รัพยากรที่เกี่ยวข้อง
- เครื่องมือสำหรับนักพัฒนา - SDK มือถือ
- iOS SDK การชำระเงิน
- Android SDK การชำระเงิน
- อ้างอิง Charges API
- อ้างอิง Tokens API
- คู่มือ 3D Secure
- คู่มือการทดสอบ