Overview

This guide walks you through integrating Dodo Payments into your mobile applications, including Android, iOS, and Flutter. You’ll learn about prerequisites, dashboard setup, integration workflow, platform-specific code, best practices, and troubleshooting tips.

Prerequisites

  • Dodo Payments merchant account
  • API credentials (API key and webhook secret key)
  • Mobile app project (Android, iOS, or Flutter)
If you don’t have an account, get your business approved by contacting the founder or filling out this form.

Dashboard Setup

1

Access the Dashboard

2

Create a Product

Set up a one-time or subscription product.
3

Test the Checkout Flow

Share and open the product link, use test card 4242 4242 4242 4242 with any future expiry and 3-digit CVV.
4

Generate API Key

Settings → API. See guide.
5

Configure Webhooks

Settings → Webhooks. See guide.

Integration Workflow

1

Create a Payment Link

You can create payment links either statically or dynamically:Static Payment Links
https://checkout.dodopayments.com/buy/{productid}?quantity=1&redirect_url={your website return_url}
Dynamic Payment Links
// Create a payment link using the Dodo Payments API
func createPaymentLink() async throws -> String {
    let url = URL(string: "https://api.dodopayments.com/v1/payments")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("Bearer \(DODO_SECRET_KEY)", forHTTPHeaderField: "Authorization")
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    let paymentData: [String: Any] = [
        "payment_link": true,
        "billing": [
            "city": "city",
            "country": "US",
            "state": "state",
            "street": "street",
            "zipcode": 0
        ],
        "customer": [
            "email": "customer@example.com",
            "name": "Customer Name"
        ],
        "product_cart": [
            [
                "product_id": "prod_xyz789",
                "quantity": 1
            ]
        ],
        "return_url": "myapp://payment-status"
    ]
    request.httpBody = try JSONSerialization.data(withJSONObject: paymentData)
    let (data, _) = try await URLSession.shared.data(for: request)
    let response = try JSONDecoder().decode(PaymentResponse.self, from: data)
    return response.payment_link
}
2

Open Payment Link in App

Open the payment link in your mobile app using a secure in-app browser or web view. This ensures a seamless and secure payment experience.
Use Chrome Custom Tabs (Android), SFSafariViewController (iOS), or WebView (Flutter) for best results.
See the Platform-Specific Integration section below for code examples.
3

Handle Payment Completion Callback

After payment, Dodo Payments will redirect to your specified return URL. Handle this callback in your app to confirm payment status.Webhook Example (Node.js/Express):
// index.ts in express 
const { Webhook } = require("standardwebhooks");
const webhook = new Webhook('DODO_PAYMENTS_WEBHOOK_KEY');
app.post('/webhook/dodo-payments', async (req, res) => {
  try {
    const body = req.body;
    const webhookHeaders = {
      "webhook-id": req.headers["webhook-id"] || "",
      "webhook-signature": req.headers["webhook-signature"] || "",
      "webhook-timestamp": req.headers["webhook-timestamp"] || "",
    };
    const raw = JSON.stringify(body);
    const samePayloadOutput = await webhook.verify(raw, webhookHeaders);
    console.log(samePayloadOutput == body); //should be true except the order of keys
    // ... Rest of your code HERE ...
    res.status(200).json({ received: true });
  } catch (error) {
    console.error('Error processing webhook:', error);
    res.status(400).json({ error: 'Webhook handler failed' });
  }
});
Test Webhook (Node.js):
// test.js
const { Webhook } = require("standardwebhooks");
const timestamp = new Date();
const generatedId = crypto.randomUUID();
const payload = { key: "value" };
const payloadString = JSON.parse(payload);
const webhookInstance = new Webhook('DODOS_DONT_FLY');
const signature = webhookInstance.sign(generatedId, timestamp, payloadString);
const headers = {
    "webhook-id": generatedId,
    "webhook-timestamp": Math.floor(timestamp.getTime() / 1000),
    "webhook-signature": signature,
    "Content-Type": "application/json"
};
const endpointUrl = "http://localhost:8080/webhook/dodo-payments";
const response = await axios.post(endpointUrl, payload, { headers });
console.log("Webhook request successful, response:", response.data);

Platform-Specific Integration

Android Integration

Using 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!!
        )
        // Launch payment link
        val paymentLink = "https://checkout.dodopayments.com/buy/{productid}"
        val customTabsIntent = CustomTabsIntent.Builder(customTabsSession)
            .build()
        customTabsIntent.launchUrl(this, Uri.parse(paymentLink))
    }
}

Best Practices

  • Security: Never store API keys in your app code. Use secure storage and SSL pinning.
  • User Experience: Show loading indicators, handle errors gracefully, and provide clear messages.
  • Testing: Use test cards, simulate network errors, and test on various devices.

Troubleshooting

Common Issues

  • WebView not opening payment link: Ensure the payment link is valid and uses HTTPS.
  • Callback not received: Check your return URL and webhook configuration.
  • API key errors: Verify that your API key is correct and has the necessary permissions.

Additional Resources

For questions or support, contact support@dodopayments.com.