How to Add Payment Processing to Your App

Veld Systems||4 min read

Payment processing is one of the most complex integrations in any application. Money is involved, so every edge case matters, failed charges, duplicate payments, webhook retries, and refund handling all need to work perfectly. Here is how to do it right.

Why Stripe

We default to Stripe for almost every project. The API is the best designed in the industry, documentation is excellent, and it handles the hard parts (PCI compliance, fraud detection, payment method management) so you do not have to.

Stripe's pricing: 2.9% + $0.30 per transaction for cards. For a product charging $50/month, that is $1.75 per transaction. No monthly fees, no setup fees. You pay only when you process payments.

Alternatives to consider: Square if you need in person payments, Paddle or Lemon Squeezy if you want them to handle tax compliance (they act as merchant of record), Adyen for enterprise scale processing with lower per transaction fees at high volume.

The Integration Architecture

Payment processing has three layers:

Client side. Stripe Elements or Checkout Session UI. Never handle raw card numbers, let Stripe's prebuilt components collect payment details. This keeps you out of PCI scope entirely. Stripe Elements is more customizable; Checkout is faster to implement.

Server side. Your backend creates Payment Intents (one time payments) or Subscriptions (recurring). The server talks to Stripe's API with your secret key. This is where pricing logic, discount calculation, and payment confirmation happen. On Traderly, all payment validation runs server side in Edge Functions, the client never sees the real transaction logic.

Webhooks. Stripe sends events to your server when things happen: payment succeeds, subscription renews, charge fails, dispute opened. Webhooks are not optional, they are the primary mechanism for staying in sync with Stripe. More on this below.

Webhook Architecture

Webhooks are the most important and most error prone part of payment processing.

Make webhooks idempotent. Stripe may send the same event multiple times. Your handler must produce the same result whether it runs once or five times. Use the event ID as a deduplication key, store processed event IDs and skip duplicates.

Verify webhook signatures. Every webhook request includes a signature header. Verify it using your webhook secret to prevent spoofed events. Never process an unverified webhook.

Handle events asynchronously. Return 200 immediately, then process the event in a background job. If your webhook handler takes more than 10 seconds, Stripe will retry, and now you are processing the same event twice with your first handler still running.

Critical events to handle: payment_intent.succeeded (activate the purchase), invoice.payment_failed (notify user, retry logic), customer.subscription.deleted (revoke access), charge.dispute.created (alert your team immediately).

Subscription Billing

Subscriptions are significantly more complex than one time payments:

Plan management. Define your pricing tiers in Stripe's dashboard or via API. Use Stripe's Price objects with recurring intervals. Support monthly and annual billing (annual at a discount increases LTV).

Upgrades and downgrades. Proration is the hard part. If a user upgrades mid billing cycle, do you charge the difference immediately or at next renewal? Stripe handles proration automatically, but you need to decide the behavior and configure it.

Failed payment recovery. Stripe's Smart Retries automatically retry failed charges with optimized timing. Configure dunning emails (Stripe sends them for you) to notify users of failed payments. After 3-4 failed attempts, cancel the subscription or move to a grace period.

Trial periods. Free trials that convert to paid require a payment method upfront (higher conversion but lower trial signups) or payment method at trial end (lower conversion but more trial users). Test both.

Marketplace Payments

If your app facilitates transactions between users (like a marketplace), use Stripe Connect:

Standard Connect, users create their own Stripe accounts. Simplest to implement, most control for the seller, but worse onboarding UX.

Express Connect, Stripe hosted onboarding flow. Better UX, you control the payout schedule. Our default recommendation for most marketplaces.

Custom Connect, full control over the experience. Only worth the complexity for large platforms with specific UX requirements.

Key decisions: your platform fee (typically 5-15% per transaction), payout frequency (instant, daily, weekly), and how to handle refunds (does the platform or the seller absorb the cost?).

Testing Payments

Stripe test mode gives you a complete sandbox. Test card numbers: 4242424242424242 (success), 4000000000000002 (decline), 4000000000009995 (insufficient funds). Always test the failure paths, they are where the real bugs hide.

Test webhooks locally with Stripe CLI: stripe listen --forward-to localhost:3000/api/webhooks/stripe. This forwards real webhook events to your development server.

Test subscription lifecycle: Create a subscription, let it renew (use Stripe's test clocks to fast forward time), fail a payment, recover, cancel, and re subscribe. Every one of these transitions is a potential bug.

What It Costs to Build

A basic Stripe integration (one time payments, checkout page): 1-2 weeks, $5K-$10K. Subscription billing with plan management: 3-4 weeks, $15K-$25K. Full marketplace with Connect: 4-6 weeks, $25K-$40K. Building a SaaS product typically falls in the subscription tier.

Our full stack development practice has built payment integrations handling millions in monthly volume. We know where the edge cases hide.

Need to add payments to your app? Let us build it right the first time.

Ready to Build?

Let us talk about your project

We take on 3-4 projects at a time. Get an honest assessment within 24 hours.