Mobile App Development for Donation Collection
Users do not trust if they cannot see where the money goes, especially with recurring donations and arbitrary amounts. We have encountered projects where poor transparency and UX led to a drop in donor LTV. For example, one charity foundation lost up to 40% of users on the payment screen due to the lack of Apple Pay and unclear fundraising progress. Transparency in fundraising is not just a progress indicator, but a whole system: a transaction feed, animated metrics, and automatic reports. This article explains how to avoid common mistakes and build an app that users trust.
Developing a Mobile App for Fundraising
Basic functionality includes three payment types: one-time donation, recurring subscription, and wallet payment. Each requires its own implementation.
One-Time Donation
Standard payment through a provider. The user enters the amount, clicks a button, and sees the result. The key is to minimize the number of fields. Ideally, a single "Donate" button with preset amounts and a "custom amount" field.
Recurring Subscription
The user subscribes to a monthly charge. This is implemented via recurring payments: on the first payment, the provider returns a card token, and then the server initiates charges on the specified day. This is the basis for predictable cash flow for non-profits.
// iOS: Stripe recurring donation setup import StripePayments let params = STPConfirmSetupIntentParams( paymentMethodParams: cardParams, clientSecret: setupIntentClientSecret ) STPPaymentHandler.shared().confirmSetupIntent( params, with: self ) { [weak self] status, setupIntent, error in switch status { case .succeeded: // setupIntent.paymentMethodID — save on server self?.saveRecurringMethod(setupIntent?.paymentMethodID) case .failed: self?.showError(error?.localizedDescription) case .canceled: break @unknown default: break } } Apple Pay / Google Pay for One-Time Donations
Maximum low friction — the user does not enter card details manually. Setup of merchant ID ($99/year) and provisioning profile is included in the scope of work. Example on iOS:
let request = PKPaymentRequest() request.merchantIdentifier = "merchant.com.yourcharity.app" request.countryCode = "RU" request.currencyCode = "RUB" request.supportedNetworks = [.visa, .masterCard] request.merchantCapabilities = [.capability3DS] request.paymentSummaryItems = [ PKPaymentSummaryItem( label: "Help animals", amount: NSDecimalNumber(string: donationAmount) ) ] Apple Pay converts 2–3 times better than manual card payments for one-time donations — confirmed by A/B tests on our clients' projects. Our clients report a 95% completion rate for Apple Pay donations.
Comparison of Payment Approaches
| Criteria | One-Time Donation | Recurring Subscription | Apple/Google Pay |
|---|---|---|---|
| Payment time | ~30 seconds | ~15 seconds (first) | 2–3 seconds |
| User trust | Medium | High | High |
| Error rate | 3–5% | 1–2% | <1% |
| Tax deduction support | On request | Automatic | On request |
How to Implement Custom Amounts Without Errors?
The "custom amount" field is a common source of errors. Problems:
- User enters "1000.5" or "1 000" with a space — normalization to
Decimalis required - Provider minimum limit of $10
- Maximum limit of $1500 without 3DS
// Android: custom amount normalization fun parseAmount(input: String): Result<Long> { val cleaned = input .replace(",", ".") .replace(Regex("\\s"), "") .trim() return try { val decimal = cleaned.toBigDecimal() if (decimal < BigDecimal("10")) { Result.failure(Exception("Minimum amount is $10")) } else if (decimal > BigDecimal("150000")) { Result.failure(Exception("For amounts over $1500, verification is required")) } else { Result.success((decimal * BigDecimal("100")).toLong()) // in kopeks } } catch (e: NumberFormatException) { Result.failure(Exception("Enter a valid amount")) } } How to Ensure Fundraising Transparency?
Users donate more willingly when they see a specific goal and progress. This is the fundraising progress bar — target vs. current. For animation, we use animateFloatAsState in Flutter or Compose. The server sends new data via WebSocket, and the scale updates smoothly in 800ms, creating an engaging effect.
// Android: Jetpack Compose progress indicator for fundraising @Composable fun FundraisingProgress( current: Long, target: Long, modifier: Modifier = Modifier ) { val progress = (current.toFloat() / target.toFloat()).coerceIn(0f, 1f) val animatedProgress by animateFloatAsState( targetValue = progress, animationSpec = tween(durationMillis = 800) ) Column(modifier) { LinearProgressIndicator( progress = animatedProgress, modifier = Modifier.fillMaxWidth().height(8.dp), trackColor = MaterialTheme.colorScheme.surfaceVariant, color = MaterialTheme.colorScheme.primary ) Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Text("${formatAmount(current)} RUB raised") Text("of ${formatAmount(target)} RUB") } } } In addition to the goal tracker, we add a transaction feed — each user sees that their donation is recorded. This increases trust and reduces chargebacks.
What Are the Advantages of Recurring Donations?
Recurring donations yield 30–50% higher average check compared to one-time payments. Technically, we implement recurring via card tokenization: after the first successful charge, we store the paymentMethodID on the server and initiate new charges via Stripe API. We always set up charge reminders (push + email) 3 days before the due date — this reduces chargebacks by 4 times.
Additional details on charge reminders
Reminders are sent via push notifications and email. The system checks the user's timezone and sends notifications at 10 AM local time. We also provide a link to update payment method if the card expires.Step-by-Step: Apple Pay Integration
- Obtain a merchant ID from Apple Developer ($99/year).
- Create a PKPaymentRequest with appropriate parameters.
- Present the payment sheet and handle the result.
Technology Stack for Assembly
| Component | iOS | Android | Flutter |
|---|---|---|---|
| Payment SDK | Stripe Payments iOS | Stripe Android SDK | Stripe Flutter |
| Push notifications | APNs | FCM (Firebase) | FCM (Firebase) |
| Deep linking | Universal Links | App Links | app_links plugin |
Tax Deductions and Documents
For charity foundations, generating a certificate for tax deduction is often required. This is server-side logic: aggregating user payments for the year, generating a PDF. The app only shows a "Download certificate" button. We guarantee the document meets local tax authority requirements.
What's Included in Turnkey Development?
- Analytics: interviews with the foundation, audit of current processes, provider selection (Stripe / YooKassa / Cloud Payments)
- Design: UX prototypes focused on the payment screen and history, architecture (iOS / Android / Flutter)
- Implementation: screen layout, payment integration, push notification setup (APNs + FCM), deep linking (Universal Links)
- Testing: unit tests for logic, UI tests for critical paths, test payments in sandbox, compliance with App Store Review Guidelines (Section 4.2/5.1)
- Deployment: publishing to App Store and Google Play, analytics setup, restricting access to test data
- Post-release: monitoring crash-free rate, updating libraries, 3-month support
Timeline Estimates
Basic version (one-time donations, card + Apple/Google Pay, history): 3–5 weeks. Recurring subscriptions: additional 1–2 weeks. Progress bars for targeted fundraisers: additional 1 week. Cost is calculated individually. Typical project budget for a full-featured donation app starts at $15,000. With over 7 years of experience and 50+ successful projects in fintech and charity, we deliver reliable solutions. Get a consultation on your project — we have extensive experience in fintech app development. Contact us to discuss the details.







