Prerequisites
To integrate the Dodo Payments API into your mobile app, you’ll need:
- A Dodo Payments merchant account
- API Credentials (API key and webhook secret key) from dashboard
- A mobile app project (Android, iOS, Flutter, or React Native)
If you don’t have an account yet, you can get your business approved by contacting the founder or by filling out this form.
Dashboard Setup
-
Navigate to the Dodo Payments Dashboard
-
Create a product (one-time payment or subscription)
-
Test the checkout flow:
- Click the share button on the product page
- Open the link in your browser
- Use test card number:
4242 4242 4242 4242
- Enter any future expiration date and any 3-digit CVV
-
Generate your API key:
- Go to Settings > API
- Detailed Guide
- Copy the API key to use in your mobile app
-
Configure webhooks:
- Go to Settings > Webhooks
- Create a webhook URL for payment notifications
- Detailed Guide
- Copy the webhook secret key
Integration Steps Overview
Dodo Payments can be integrated into mobile apps using our payment links. The integration involves:
- Creating a payment link using our API
- Opening the payment link in a WebView or browser
- Handling the payment completion callback
1. Creating Payment Links
You can create payment links either statically or dynamically:
1. Static Payment Links
Simple to create by appending your product ID to the base URL:
https://checkout.dodopayments.com/buy/{productid}?quantity=1&redirect_url={your website return_url}
2. Dynamic Payment Links
Created via API call with customer details:
// 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. Opening Payment Link
Check our gu
3. Handle Payment Completion Callback
// index.ts in express
const { Webhook } = require("standardwebhooks");
const webhook = new Webhook('DODO_PAYMENTS_WEBHOOK_KEY');
// Add this endpoint in the Dodo Payments Website | Side Bar -> Developer -> Webhooks -> create
app.post('/webhook/dodo-payments', async (req: any, res: any) => {
try {
const body = req.body;
const webhookHeaders: WebhookUnbrandedRequiredHeaders = {
"webhook-id": (req.headers["webhook-id"] || "") as string,
"webhook-signature": (req.headers["webhook-signature"] || "") as string,
"webhook-timestamp": (req.headers["webhook-timestamp"] || "") as string,
};
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' });
}
});
// index.ts in express
const { Webhook } = require("standardwebhooks");
const webhook = new Webhook('DODO_PAYMENTS_WEBHOOK_KEY');
// Add this endpoint in the Dodo Payments Website | Side Bar -> Developer -> Webhooks -> create
app.post('/webhook/dodo-payments', async (req: any, res: any) => {
try {
const body = req.body;
const webhookHeaders: WebhookUnbrandedRequiredHeaders = {
"webhook-id": (req.headers["webhook-id"] || "") as string,
"webhook-signature": (req.headers["webhook-signature"] || "") as string,
"webhook-timestamp": (req.headers["webhook-timestamp"] || "") as string,
};
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.js seperate file or maybe even in the index.js with correct routes
const { Webhook } = require("standardwebhooks");
const timestamp = new Date();
const generatedId = crypto.randomUUID();
const payload = { key: "value" };
const payloadString = JSON.parse(payload);
// Create a new Webhook instance and sign the 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" // Ensure Express parses JSON body correctly.
};
// Define the target endpoint URL.
const endpointUrl = "http://localhost:8080/webhook/dodo-payments";
// Send the POST request using Axios.
const response = await axios.post(endpointUrl, payload, { headers });
console.log("Webhook request successful, response:", response.data);
iOS Specific Guidelines
For digital goods sold outside the App Store, Dodo Payments enables:
- Web-based checkout via WebView
- Compliance with Apple’s reader app guidelines
- Support for cross-platform purchases
Android Considerations
- Google Play Billing is required for in-app digital goods
- Dodo Payments is ideal for non-in-app purchases (e.g. via browser or emails)
Best Practices
-
Security:
- Never store API keys in your app’s code
- Use secure storage solutions for sensitive data
- Implement proper SSL pinning
- Validate all payment responses
-
User Experience:
- Show loading indicators during payment processing
- Handle network errors gracefully
- Provide clear error messages
- Implement proper back navigation
-
Testing:
- Test with test card numbers in development
- Test different payment scenarios
- Test network error handling
- Test on different device sizes and OS versions
Additional Resources
For any questions or support, please contact our support team.