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 Jalankan integrasi pembayaran seluler Anda dalam 4 langkah sederhana
Platform Examples Contoh kode lengkap untuk Android, iOS, React Native, dan Flutter
Prasyarat
Sebelum mengintegrasikan Dodo Payments ke dalam aplikasi mobile Anda, pastikan Anda memiliki:
Akun Dodo Payments : Akun merchant aktif dengan akses API
Kredensial API : Kunci API dan kunci rahasia webhook dari dasbor Anda
Proyek Aplikasi Mobile : Aplikasi Android, iOS, React Native, atau Flutter
Server Backend : Untuk menangani pembuatan sesi checkout dengan aman
Alur Kerja Integrasi
Integrasi mobile mengikuti proses aman 4 langkah di mana backend Anda menangani panggilan API dan aplikasi mobile Anda mengelola pengalaman pengguna.
Backend: Create Checkout Session
Checkout Session API Docs Pelajari cara membuat sesi checkout di backend Anda menggunakan Node.js, Python, dan lainnya. Lihat contoh lengkap dan referensi parameter di dokumentasi khusus Checkout Sessions API .
Keamanan : Sesi checkout harus dibuat di server backend Anda, jangan pernah di aplikasi seluler. Ini melindungi kunci API Anda dan memastikan validasi yang tepat.
Mobile: Get Checkout URL
Aplikasi mobile Anda memanggil backend Anda untuk mendapatkan URL checkout: 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 ;
}
};
Keamanan : Aplikasi seluler hanya berkomunikasi dengan backend Anda, tidak pernah langsung dengan API Dodo Payments.
Mobile: Open Checkout in Browser
Buka URL checkout di browser dalam aplikasi yang aman untuk pemrosesan pembayaran.
See platform-specific integration examples Lihat kode lengkap dan instruksi pengaturan untuk pembayaran seluler Android, iOS, dan Flutter.
Backend: Handle Payment Completion
Proses penyelesaian pembayaran melalui webhook dan URL pengalihan untuk mengonfirmasi status pembayaran.
Pilih platform mobile Anda di bawah untuk contoh implementasi lengkap:
Android
iOS
React Native
Flutter
Integrasi Android Implementasi 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)
}
}
}
}
Integrasi iOS Implementasi 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 ) " )
}
}
}
}
Penanganan Deep Link Konfigurasi skema URL untuk menangani pengalihan penyelesaian pembayaran: 1. Siapkan skema URL di Info.plist: < key > CFBundleURLTypes </ key >
< array >
< dict >
< key > CFBundleURLName </ key >
< string > myapp </ string >
< key > CFBundleURLSchemes </ key >
< array >
< string > myapp </ string >
</ array >
</ dict >
</ array >
2. Tangani pengalihan di 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. Dengarkan penyelesaian pembayaran: 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
}
}
Integrasi React Native Menggunakan Tautan Checkout (InAppBrowser) Buka halaman checkout yang dihosting oleh Dodo di browser dalam aplikasi. Implementasi 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 ;
Penanganan Deep Link untuk Callback Pembayaran // 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 ;
Ketergantungan Paket Tambahkan dependensi ini ke package.json: {
"dependencies" : {
"react-native-inappbrowser-reborn" : "^6.4.0"
}
}
Perintah Instalasi # Install InAppBrowser
npm install react-native-inappbrowser-reborn
cd ios && pod install
Konfigurasi Android Tambahkan ke android/app/src/main/AndroidManifest.xml: < activity
android:name = "com.reactnativeinappbrowserreborn.InAppBrowserActivity"
android:theme = "@style/Theme.AppCompat.Dialog"
android:exported = "false" />
Konfigurasi iOS Tambahkan ke 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 >
Gunakan InAppBrowser untuk pengalaman browser native. Tangani tautan mendalam untuk callback pembayaran.
Alternatif: SDK Pembayaran Native Untuk pengalaman pembayaran yang lebih terintegrasi dengan UI native dan opsi kustomisasi penuh, Dodo Payments menawarkan SDK React Native. Kepatuhan Regional : SDK pembayaran dalam aplikasi memiliki pembatasan regional. Di iOS , SDK pembayaran native hanya legal di UE (di bawah ketentuan DMA). Di Android , mereka legal di pasar User Choice Billing (UCB). Untuk wilayah lain, gunakan pendekatan tautan checkout di atas untuk memastikan kepatuhan App Store dan Play Store.
React Native SDK Integration Guide Panduan lengkap untuk mengintegrasikan SDK React Native Dodo dengan lembar pembayaran native, opsi kustomisasi penuh, dan penanganan pembayaran langsung. Termasuk pengaturan, implementasi, dan instruksi pengujian.
Integrasi 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
}
}
}
Gunakan WebView untuk tautan pembayaran. Tangani URL pengalihan untuk mendeteksi penyelesaian pembayaran.
Praktik Terbaik
Keamanan : Jangan pernah menyimpan kunci API di kode aplikasi Anda. Gunakan penyimpanan yang aman dan SSL pinning.
Pengalaman Pengguna : Tampilkan indikator pemuatan, tangani kesalahan dengan baik, dan berikan pesan yang jelas.
Pengujian : Gunakan kartu uji, simulasi kesalahan jaringan, dan uji di berbagai perangkat.
Pemecahan Masalah
Masalah Umum
WebView tidak membuka tautan pembayaran : Pastikan tautan pembayaran valid dan menggunakan HTTPS.
Callback tidak diterima : Periksa URL pengembalian dan konfigurasi webhook Anda.
Kesalahan kunci API : Verifikasi bahwa kunci API Anda benar dan memiliki izin yang diperlukan.
Sumber Daya Tambahan