Documentation Index Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
Use this file to discover all available pages before exploring further.
Quick Start Thiết lập tích hợp thanh toán di động của bạn trong 4 bước đơn giản
Platform Examples Các ví dụ mã hoàn chỉnh cho Android, iOS, React Native và Flutter
Điều Kiện Tiên Quyết
Trước khi tích hợp Dodo Payments vào ứng dụng di động của bạn, hãy đảm bảo bạn đã có:
Tài Khoản Dodo Payments : Tài khoản thương nhân hoạt động với quyền truy cập API
Thông Tin Xác Thực API : Khóa API và khóa bí mật webhook từ bảng điều khiển của bạn
Dự Án Ứng Dụng Di Động : Ứng dụng Android, iOS, React Native hoặc Flutter
Máy Chủ Backend : Để xử lý an toàn việc tạo phiên thanh toán
Quy Trình Tích Hợp
Tích hợp di động tuân theo quy trình 4 bước an toàn, trong đó backend của bạn xử lý các cuộc gọi API và ứng dụng di động của bạn quản lý trải nghiệm người dùng.
Backend: Create Checkout Session
Checkout Session API Docs Tìm hiểu cách tạo phiên thanh toán tại backend bằng Node.js, Python và hơn thế. Xem các ví dụ và tham chiếu tham số đầy đủ trong tài liệu Checkout Sessions API chuyên dụng.
Bảo mật : Checkout sessions must be created on your backend server, never in the mobile app. This protects your API keys and ensures proper validation.
Mobile: Get Checkout URL
Ứng dụng di động của bạn gọi backend của bạn để lấy URL thanh toán: func getCheckoutURL ( productId : String , customerEmail : String , customerName : String ) async throws -> String {
let url = URL ( string : "https://your-backend.com/api/create-checkout-session" ) !
var request = URLRequest ( url : url)
request. httpMethod = "POST"
request. setValue ( "application/json" , forHTTPHeaderField : "Content-Type" )
let requestData: [ String : Any ] = [
"productId" : productId,
"customerEmail" : customerEmail,
"customerName" : customerName
]
request. httpBody = try JSONSerialization. data ( withJSONObject : requestData)
let (data, _ ) = try await URLSession. shared . data ( for : request)
let response = try JSONDecoder (). decode (CheckoutResponse. self , from : data)
return response. checkout_url
}
suspend fun getCheckoutURL (productId: String , customerEmail: String , customerName: String ): String {
val client = OkHttpClient ()
val requestBody = JSONObject (). apply {
put ( "productId" , productId)
put ( "customerEmail" , customerEmail)
put ( "customerName" , customerName)
}. toString (). toRequestBody ( "application/json" . toMediaType ())
val request = Request. Builder ()
. url ( "https://your-backend.com/api/create-checkout-session" )
. post (requestBody)
. build ()
val response = client. newCall (request). execute ()
val responseBody = response.body?. string ()
val jsonResponse = JSONObject (responseBody ?: "" )
return jsonResponse. getString ( "checkout_url" )
}
const getCheckoutURL = async ( productId , customerEmail , customerName ) => {
try {
const response = await fetch ( 'https://your-backend.com/api/create-checkout-session' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ({
productId ,
customerEmail ,
customerName
})
});
const data = await response . json ();
return data . checkout_url ;
} catch ( error ) {
console . error ( 'Failed to get checkout URL:' , error );
throw error ;
}
};
Bảo mật : Ứng dụng di động chỉ liên lạc với backend của bạn, không bao giờ trực tiếp với Dodo Payments API.
Mobile: Open Checkout in Browser
Mở URL thanh toán trong trình duyệt an toàn bên trong ứng dụng để xử lý thanh toán.
See platform-specific integration examples Xem đầy đủ mã và hướng dẫn cài đặt cho các thanh toán di động Android, iOS và Flutter.
Backend: Handle Payment Completion
Xử lý hoàn tất thanh toán qua webhooks và URL chuyển hướng để xác nhận trạng thái thanh toán.
Tích Hợp Cụ Thể Nền Tảng
Chọn nền tảng di động của bạn bên dưới để xem các ví dụ triển khai hoàn chỉnh:
Android
iOS
React Native
Flutter
Tích Hợp Android Triển Khai Chrome Custom Tabs // Add Chrome Custom Tabs dependency to build.gradle
implementation 'androidx.browser:browser:1.5.0'
// In your Activity
class PaymentActivity : AppCompatActivity () {
private var customTabsSession: CustomTabsSession ? = null
private var customTabsClient: CustomTabsClient ? = null
private var customTabsServiceConnection: CustomTabsServiceConnection ? = null
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
// Initialize Custom Tabs
customTabsServiceConnection = object : CustomTabsServiceConnection () {
override fun onCustomTabsServiceConnected (name: ComponentName , client: CustomTabsClient ) {
customTabsClient = client
customTabsClient?. warmup ( 0L )
customTabsSession = customTabsClient?. newSession ( object : CustomTabsCallback () {
override fun onNavigationEvent (navigationEvent: Int , extras: Bundle ?) {
// Handle navigation events
}
})
}
override fun onServiceDisconnected (name: ComponentName ) {
customTabsClient = null
}
}
CustomTabsClient. bindCustomTabsService (
this ,
"com.android.chrome" ,
customTabsServiceConnection !!
)
// Get checkout URL from backend and launch
lifecycleScope. launch {
try {
val checkoutURL = getCheckoutURL ( "prod_123" , "customer@example.com" , "Customer Name" )
val customTabsIntent = CustomTabsIntent. Builder (customTabsSession)
. build ()
customTabsIntent. launchUrl ( this@PaymentActivity , Uri. parse (checkoutURL))
} catch (e: Exception ) {
// Handle error
Log. e ( "PaymentActivity" , "Failed to get checkout URL" , e)
}
}
}
}
Tích Hợp iOS Triển Khai SFSafariViewController import SafariServices
class PaymentViewController : UIViewController {
override func viewDidLoad () {
super . viewDidLoad ()
Task {
do {
let checkoutURL = try await getCheckoutURL (
productId : "prod_123" ,
customerEmail : "customer@example.com" ,
customerName : "Customer Name"
)
if let url = URL ( string : checkoutURL) {
let safariVC = SFSafariViewController ( url : url)
present (safariVC, animated : true , completion : nil )
}
} catch {
// Handle error
print ( "Failed to get checkout URL: \( error ) " )
}
}
}
}
Xử Lý Liên Kết Sâu Cấu hình các sơ đồ URL để xử lý các chuyển hướng hoàn tất thanh toán: 1. Thiết lập sơ đồ URL trong Info.plist: < key > CFBundleURLTypes </ key >
< array >
< dict >
< key > CFBundleURLName </ key >
< string > myapp </ string >
< key > CFBundleURLSchemes </ key >
< array >
< string > myapp </ string >
</ array >
</ dict >
</ array >
2. Xử lý chuyển hướng trong AppDelegate: func application ( _ app : UIApplication, open url : URL, options : [UIApplication.OpenURLOptionsKey : Any ] = [ : ]) -> Bool {
if url.scheme == "myapp" && url.host == "payment-status" {
NotificationCenter. default . post ( name : . paymentCompleted , object : url)
return true
}
return false
}
3. Lắng nghe hoàn tất thanh toán: NotificationCenter. default . addObserver ( self , selector : #selector ( handlePaymentCompletion (_:)), name : . paymentCompleted , object : nil )
@objc func handlePaymentCompletion ( _ notification : Notification) {
if let url = notification.object as? URL {
safariVC ? . dismiss ( animated : true )
// Parse payment status from URL parameters
let components = URLComponents ( url : url, resolvingAgainstBaseURL : false )
let paymentId = components ? . queryItems ? . first ( where : { $0 . name == "payment_id" }) ? . value
let status = components ? . queryItems ? . first ( where : { $0 . name == "status" }) ? . value
// Handle payment completion
}
}
Tích hợp React Native Sử Dụng Liên Kết Thanh Toán (InAppBrowser) Mở trang thanh toán được lưu trữ của Dodo trong trình duyệt trong ứng dụng. Triển Khai InAppBrowser import React from 'react' ;
import { View , TouchableOpacity , Text , Alert } from 'react-native' ;
import { InAppBrowser } from 'react-native-inappbrowser-reborn' ;
const PaymentButton = ({ productId , onPaymentComplete }) => {
const openPaymentLink = async () => {
try {
// Get checkout URL from backend
const checkoutURL = await getCheckoutURL (
productId ,
'customer@example.com' ,
'Customer Name'
);
const result = await InAppBrowser . open ( checkoutURL , {
// iOS options
dismissButtonStyle: 'cancel' ,
preferredBarTintColor: '#453AA4' ,
preferredControlTintColor: 'white' ,
readerMode: false ,
animated: true ,
modalPresentationStyle: 'fullScreen' ,
modalTransitionStyle: 'coverVertical' ,
modalEnabled: true ,
enableBarCollapsing: false ,
// Android options
showTitle: true ,
toolbarColor: '#453AA4' ,
secondaryToolbarColor: 'black' ,
navigationBarColor: 'black' ,
navigationBarDividerColor: 'white' ,
enableUrlBarHiding: true ,
enableDefaultShare: true ,
forceCloseOnRedirection: false ,
animations: {
startEnter: 'slide_in_right' ,
startExit: 'slide_out_left' ,
endEnter: 'slide_in_left' ,
endExit: 'slide_out_right'
},
headers: {
'my-custom-header' : 'my custom header value'
}
});
// Handle payment completion based on result
if ( result . type === 'dismiss' ) {
// User dismissed the browser
console . log ( 'Payment browser dismissed' );
}
} catch ( error ) {
Alert . alert ( 'Error' , 'Failed to open payment page' );
console . error ( 'InAppBrowser error:' , error );
}
};
return (
< View style = { { padding: 20 } } >
< TouchableOpacity
style = { {
backgroundColor: '#007AFF' ,
padding: 15 ,
borderRadius: 8 ,
alignItems: 'center'
} }
onPress = { openPaymentLink }
>
< Text style = { { color: 'white' , fontSize: 16 , fontWeight: 'bold' } } >
Pay Now
</ Text >
</ TouchableOpacity >
</ View >
);
};
export default PaymentButton ;
Xử Lý Liên Kết Sâu cho Các Callback Thanh Toán // App.js or your main component
import React , { useEffect } from 'react' ;
import { Linking } from 'react-native' ;
const App = () => {
useEffect (() => {
// Handle deep links when app is already running
const handleDeepLink = ( url ) => {
if ( url . includes ( 'payment-status' )) {
const urlParams = new URLSearchParams ( url . split ( '?' )[ 1 ]);
const paymentId = urlParams . get ( 'payment_id' );
const status = urlParams . get ( 'status' );
// Handle payment completion
handlePaymentCallback ( paymentId , status );
}
};
// Listen for deep links
const subscription = Linking . addEventListener ( 'url' , ({ url }) => {
handleDeepLink ( url );
});
// Handle deep link if app was opened from a link
Linking . getInitialURL (). then (( url ) => {
if ( url ) {
handleDeepLink ( url );
}
});
return () => subscription ?. remove ();
}, []);
const handlePaymentCallback = ( paymentId , status ) => {
if ( status === 'succeeded' ) {
// Navigate to success screen or show success message
console . log ( 'Payment successful:' , paymentId );
} else {
// Handle payment failure
console . log ( 'Payment failed:' , paymentId );
}
};
// Your app components...
};
export default App ;
Phụ Thuộc Gói Add this dependency to your package.json: {
"dependencies" : {
"react-native-inappbrowser-reborn" : "^6.4.0"
}
}
Lệnh Cài Đặt # Install InAppBrowser
npm install react-native-inappbrowser-reborn
cd ios && pod install
Cấu Hình Android Thêm vào android/app/src/main/AndroidManifest.xml: < activity
android:name = "com.reactnativeinappbrowserreborn.InAppBrowserActivity"
android:theme = "@style/Theme.AppCompat.Dialog"
android:exported = "false" />
Cấu Hình iOS Thêm vào ios/YourApp/Info.plist: < key > CFBundleURLTypes </ key >
< array >
< dict >
< key > CFBundleURLName </ key >
< string > myapp </ string >
< key > CFBundleURLSchemes </ key >
< array >
< string > myapp </ string >
</ array >
</ dict >
</ array >
Sử dụng InAppBrowser để có trải nghiệm trình duyệt gốc. Xử lý liên kết sâu cho các phản hồi thanh toán.
Lựa Chọn Khác: SDK Thanh Toán Gốc Để có trải nghiệm thanh toán tích hợp hơn với giao diện người dùng gốc và tùy chỉnh đầy đủ, Dodo Payments cung cấp một SDK React Native. Tuân thủ vùng : SDK thanh toán trong ứng dụng có hạn chế theo vùng. Trên iOS , SDK thanh toán gốc chỉ hợp pháp ở EU (theo điều khoản DMA). Trên Android , chúng hợp pháp ở các thị trường User Choice Billing (UCB). Đối với các khu vực khác, hãy sử dụng cách tiếp cận liên kết thanh toán phía trên để đảm bảo tuân thủ App Store và Play Store.
React Native SDK Integration Guide Hướng dẫn đầy đủ để tích hợp SDK React Native của Dodo với các bảng thanh toán gốc, các tùy chọn tùy chỉnh đầy đủ và xử lý thanh toán trực tiếp. Bao gồm thiết lập, triển khai và hướng dẫn kiểm tra.
Tích Hợp Flutter import 'package:flutter/material.dart' ;
import 'package:webview_flutter/webview_flutter.dart' ;
import 'package:http/http.dart' as http;
import 'dart:convert' ;
Future < String > getCheckoutURL ( String productId, String customerEmail, String customerName) async {
final response = await http. post (
Uri . parse ( 'https://your-backend.com/api/create-checkout-session' ),
headers : { 'Content-Type' : 'application/json' },
body : json. encode ({
'productId' : productId,
'customerEmail' : customerEmail,
'customerName' : customerName,
}),
);
if (response.statusCode == 200 ) {
final data = json. decode (response.body);
return data[ 'checkout_url' ];
} else {
throw Exception ( 'Failed to get checkout URL: ${ response . statusCode } ' );
}
}
class PaymentScreen extends StatefulWidget {
@override
_PaymentScreenState createState () => _PaymentScreenState ();
}
class _PaymentScreenState extends State < PaymentScreen > {
late WebViewController _controller;
String ? checkoutURL;
@override
void initState () {
super . initState ();
_getCheckoutURL ();
}
Future < void > _getCheckoutURL () async {
try {
final url = await getCheckoutURL ( 'prod_123' , 'customer@example.com' , 'Customer Name' );
setState (() {
checkoutURL = url;
});
} catch (e) {
// Handle error
print ( 'Failed to get checkout URL: $ e ' );
}
}
@override
Widget build ( BuildContext context) {
return Scaffold (
appBar : AppBar (title : Text ( 'Payment' )),
body : checkoutURL == null
? Center (child : CircularProgressIndicator ())
: WebView (
initialUrl : checkoutURL,
javascriptMode : JavascriptMode .unrestricted,
onWebViewCreated : ( WebViewController webViewController) {
_controller = webViewController;
},
navigationDelegate : ( NavigationRequest request) {
if (request.url. contains ( 'payment_id' ) && request.url. contains ( 'status' )) {
// Handle payment completion
final uri = Uri . parse (request.url);
final paymentId = uri.queryParameters[ 'payment_id' ];
final status = uri.queryParameters[ 'status' ];
handlePaymentCompletion (paymentId, status);
return NavigationDecision .prevent;
}
return NavigationDecision .navigate;
},
),
);
}
void handlePaymentCompletion ( String ? paymentId, String ? status) {
if (status == 'succeeded' ) {
// Payment successful
} else {
// Payment failed
}
}
}
Sử dụng WebView cho các liên kết thanh toán. Xử lý URL chuyển hướng để phát hiện hoàn tất thanh toán.
Thực Hành Tốt Nhất
Bảo Mật : Không bao giờ lưu trữ các khóa API trong mã ứng dụng của bạn. Sử dụng lưu trữ an toàn và SSL pinning.
Trải Nghiệm Người Dùng : Hiển thị chỉ báo tải, xử lý lỗi một cách duyên dáng và cung cấp thông điệp rõ ràng.
Kiểm Tra : Sử dụng thẻ thử nghiệm, mô phỏng lỗi mạng và kiểm tra trên nhiều thiết bị.
Khắc Phục Sự Cố
Các Vấn Đề Thường Gặp
WebView không mở liên kết thanh toán : Đảm bảo liên kết thanh toán hợp lệ và sử dụng HTTPS.
Callback không nhận được : Kiểm tra URL trả về và cấu hình webhook của bạn.
Lỗi khóa API : Xác minh rằng khóa API của bạn là chính xác và có quyền truy cập cần thiết.
Tài Nguyên Bổ Sung