Skip to main content

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
  • iOS
  • Flutter

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.
I