The Payment Integration Guide — AI Code Toolkit
💳 Discipline

The Payment Integration Guide

Payment infrastructure that survives edge cases + processor changes + growth. Platform selection framework (Stripe, Adyen, Braintree, Square, regional processors, orchestrators like Primer/Spreedly); PCI compliance scope (SAQ A vs. SAQ A-EP vs. SAQ D-Merchant vs. Level 1); minimizing scope via hosted forms; webhook signature verification; idempotency pattern; subscription state machine design; trial handling; dunning schedule; cancellation timing; proration; refund flows; dispute + chargeback handling; dispute rate thresholds; multi-currency; tax (Stripe Tax, TaxJar, Anrok); invoicing; fraud (3D Secure, Radar); reconciliation; observability; marketplace patterns (Stripe Connect, Adyen for Platforms); migration; failure patterns; metrics. Twenty-five sections; 22 minutes.

By Sana K.
22 min read
Updated Mar 15, 2027
Guide v1.0

What payment integration discipline actually is

Payments are what everyone integrates fast and hardens slowly. Get a checkout working; ship. Then a trial + partial refund + mid-period upgrade breaks the model. Then a webhook retry causes duplicate charges. Then a dunning retry after cancellation confuses users. Then a dispute wave hits the rate threshold and processor puts you on chargeback monitoring. Discipline is the framework that anticipates these evolutions from day one: state machine-first thinking, idempotency in every webhook handler, minimized PCI scope, reconciliation with processor, observability of transitions.

Not a “buy Stripe” guide. Stripe is often right; discipline matters more than vendor. Adyen, Braintree, Square, regional processors all benefit from the same discipline. Sana K.; pairs with the payments-architect subagent, the stripe MCP, and the payment state machine blog.

Platform selection framework

Stripe: default for most cases; excellent DX; broad global coverage; ~2.9% + 30¢ typical.

Adyen: enterprise-heavy; global reach with local payment methods (iDEAL, SEPA Direct Debit, WeChat Pay, etc.); complex integration; strong for international expansion.

Braintree: PayPal-owned; native PayPal integration; good when PayPal is critical checkout option.

Square: retail + in-person + online; strong POS integration; fits omnichannel commerce.

Regional processors: often required for local payment methods, tax compliance, currency support in specific markets (Rapyd, Nuvei, dLocal, Adyen for local methods).

Payment orchestrators: Primer, Spreedly, Gr4vy. Abstract across multiple processors for redundancy + smart routing + optimization. Advanced; matters at scale.

Selection framework:

  • SaaS / subscription with global card acceptance: Stripe.
  • Enterprise + international + local methods: Adyen.
  • PayPal-native checkout critical: Braintree.
  • In-person + online omnichannel: Square.
  • Local markets requiring specific methods: regional processor + Stripe/Adyen.
  • Multi-processor optimization at scale: orchestrator.

PCI compliance scope

PCI DSS (Payment Card Industry Data Security Standard) applies to any system handling card data. Scope levels:

SAQ A: card data handled entirely by third-party (Stripe Elements, Adyen Drop-in). Your systems never see card data. Simplest scope; annual self-assessment questionnaire.

SAQ A-EP: your site includes third-party (iframe, redirect) but delivers page. Slightly larger scope; still self-assessment.

SAQ D-Merchant: your systems receive card data (server-side POST from form). Significantly larger scope; more audit requirements; higher cost.

Level 1 (audit): >6 million transactions/year. Formal PCI audit by Qualified Security Assessor (QSA). Substantial ongoing compliance program.

Design goal: minimize scope. SAQ A vs. SAQ D-Merchant is often the difference between annual questionnaire and quarterly formal audit + significant infrastructure requirements.

Minimizing PCI scope via hosted forms

Hosted forms keep card data out of your servers. Options:

  • Stripe Elements / Checkout: card fields hosted; token returned to your server. SAQ A scope.
  • Adyen Drop-in / Components: same concept; SAQ A scope.
  • Braintree Hosted Fields: card fields in iframes; SAQ A-EP scope.
  • Redirect flows: user redirected to processor's page; return to yours. SAQ A scope.

Anti-pattern: form on your page POSTs card data to your server, then to processor. SAQ D-Merchant scope. Larger scope; audit-heavier; higher risk.

Even “we log the card number for debugging” is a scope violation. Never log card data. Even 4-digit truncations require care.

Webhook signature verification

Every processor signs webhooks. Verify before processing:

  • Stripe: stripe-signature header contains HMAC-SHA256 of raw payload + timestamp. Verify against webhook signing secret.
  • Adyen: HMAC signature; verify per API.
  • Braintree: Notification::parse method verifies signature automatically.

Handler pattern:

def handle_webhook(request):
    signature = request.headers['Stripe-Signature']
    payload = request.raw_body  # NOT parsed JSON; raw bytes
    event = stripe.Webhook.construct_event(payload, signature, WEBHOOK_SECRET)
    # Signature valid; proceed

Rejected signatures = attacker attempting webhook injection. Log + alert.

Common bug: parsing JSON before signature verification. Signature verifies raw bytes; parsed JSON may differ. Verify against raw payload.

Webhook idempotency pattern

Processor retries webhooks. Duplicate delivery is expected. Idempotency prevents duplicate processing:

def handle_webhook(event):
    signature_verify(event)

    if is_duplicate(event.id):
        return 200  # ack idempotent replay

    with transaction():
        process(event)
        mark_processed(event.id)
    return 200

Idempotency store: Redis with TTL (e.g., 7 days), database table with unique index on event ID. Cleanup old entries.

Always return 200. If your handler errors, processor retries; you get more copies. Return 200 for legitimate + duplicate + irrelevant events. Log errors; don't reject.

Common bug: exception in handler causes non-200 response; processor retries; on retry, partial state from first attempt causes different behavior. Transactions + idempotency + always-200 pattern prevents.

Subscription state machine design

Explicit states, not boolean columns:

  • PENDING: created; not yet paid or trialing.
  • TRIALING: in trial period.
  • ACTIVE: paid; current period valid.
  • PAST_DUE: charge failed; dunning in progress.
  • CANCELED_END_OF_PERIOD: user requested cancel; will end at period end.
  • CANCELED: not active; can be reactivated within window.
  • UNPAID: dunning exhausted; not active.

Legal transitions explicit; illegal transitions rejected + logged. See the payment state machine blog for full pattern.

Payment records (individual charges) also state-machine: PENDING → SUCCEEDED → REFUNDED_PARTIAL / REFUNDED_FULL / DISPUTED → DISPUTE_LOST / DISPUTE_WON.

Trial handling

Trials vary by strategy:

  • No card required: user signs up; trial starts. Higher trial conversion; higher trial signup rate. Some abuse.
  • Card required upfront: user provides card; not charged until trial ends. Lower trial signup; higher conversion.
  • Card at end: user signs up without card; before trial ends, prompted to add card. Middle ground.

State machine: PENDING → TRIALING on trial start. TRIALING → ACTIVE at trial end + successful charge. TRIALING → CANCELED if user cancels or auto-conversion fails.

Anti-pattern: trial + immediate re-enrollment loop. User signs up, cancels, signs up again with different email. Detect via device fingerprint or email variations; enforce trial limit per user.

Dunning schedule

Charge fails on renewal; retry pattern (dunning):

Standard: retry days 3, 7, 14 after initial failure. Grace period during. State: PAST_DUE. If all retries fail: transition to UNPAID or CANCELED (business decision).

Considerations:

  • Grace period access: subscription still active during PAST_DUE? Business decision. Common: yes for first N days.
  • Email cadence: notify user before retries + on each retry failure. Careful not to spam.
  • Manual intervention: support team can extend grace or attempt retry manually.
  • Payment method update flow: user should be able to update card easily. Email links directly to update page.

Recovery rate typical 30-50% for expired cards; lower for insufficient funds. Different failure reasons have different retry likelihood.

Cancellation timing

Two policies:

End of period: user cancels; access continues until end of current billing period; no refund. Standard SaaS.

Immediate: user cancels; access ends now; potentially prorated refund. Common for consumer.

State machine: end-of-period = CANCELED_END_OF_PERIOD state until period ends, then CANCELED. Immediate = ACTIVE → CANCELED.

Reactivation window: how long can user reactivate before permanent cancellation? Common: 30 days. Enables win-back campaigns.

Proration

Upgrade mid-period: charge pro-rata for remaining period. Or credit for remaining period + charge new plan.

Downgrade mid-period: credit for unused period (unusual) OR take effect next period (common).

Processors handle proration (Stripe: automatic; Adyen: manual configuration). Verify math on invoice; edge cases (multiple mid-period changes, upgrade + downgrade) get complex.

UX: preview proration before user confirms. Prevents billing surprises.

Refund flows

You initiate refund via API. Full or partial; money returns to customer's card.

State: SUCCEEDED → REFUNDED_PARTIAL or REFUNDED_FULL.

Considerations:

  • Refund fees: some processors keep transaction fee even on refund; some return it.
  • Refund window: usually 90-180 days; longer via ACH.
  • Multi-currency: refund in same currency as charge; FX impact if you've settled.
  • Tax on refunds: refund the tax portion; update tax reporting.
  • Automated vs. manual: high-value refunds may require approval workflow.

Distinction from dispute: refund is you giving money back; dispute is customer taking it via card issuer.

Dispute + chargeback handling

Customer files dispute via card issuer. Card issuer sends chargeback to processor; you respond with evidence. Won: money stays. Lost: money returned + additional fees.

Evidence collection:

  • Order details: what was purchased.
  • Shipping proof: tracking number + delivery confirmation.
  • IP + device information: matched to account.
  • Terms acceptance timestamp.
  • Customer communication history.
  • For subscription: usage evidence (active use during billing period).

Automated evidence pipeline: collect this info per transaction upfront; ready to respond within processor deadline (typically 7 days).

Response templates by dispute reason (fraud, product not received, etc.) speed response.

Dispute rate thresholds

Card networks (Visa, Mastercard) monitor dispute rates:

  • Below 0.75% (Visa): healthy.
  • 0.75% - 1%: chargeback monitoring program; enhanced reporting; possible fees.
  • Above 1%: high-risk merchant; significant fees; possible account termination.

Sources of high dispute rate:

  • Fraud (stolen cards being used on your site).
  • Friendly fraud (real customer disputes legitimate charge).
  • Poor customer service (customer disputes instead of refunding).
  • Recurring billing surprises (customer forgot subscription).

Mitigation: 3D Secure for suspicious transactions; clear billing descriptor; proactive renewal reminders; easy cancellation; responsive customer service.

Multi-currency handling

Three currency concerns:

  • Display currency: what user sees. Usually user preference or geo-based.
  • Charge currency: currency card charged in. Local currency preferred (avoids user FX fee).
  • Settlement currency: currency you receive. Processor converts + settles to your bank.

Stripe multi-currency: charge in local currency; automatic conversion to your bank currency. FX rate marked-up by processor (~1-2%).

Alternative: charge in USD (or single currency); user's card issuer handles FX (may cost user more). Simpler for you; worse UX.

For subscription: multi-currency subscription = plan priced per currency. Manage price parity or accept FX-drift.

Tax handling

Tax varies by jurisdiction:

  • US sales tax: per-state; sometimes per-locality. Nexus rules (economic + physical presence) determine where you must collect.
  • EU VAT: destination-based (charge based on customer location); reverse-charge for B2B with valid VAT ID.
  • Other jurisdictions: GST (Canada, Australia, India); other consumption taxes.

Manual calculation = mistakes. Recommended:

  • Stripe Tax: automatic calculation + collection; integrates with Stripe Billing.
  • TaxJar: tax engine; broad; connects to Stripe + others.
  • Anrok: SaaS-focused; handles complex SaaS-specific tax rules.

Registration: automated calculation doesn't relieve registration obligation. Register in jurisdictions where you have nexus.

Invoicing (VAT-compliant)

B2B customers expect invoices. VAT-compliant invoices required in EU + other jurisdictions:

  • Seller name + address + tax ID.
  • Buyer name + address + tax ID (if applicable).
  • Invoice number + date.
  • Line items with description, quantity, unit price, tax rate, total.
  • Currency.
  • Tax breakdown.

Stripe Billing generates VAT-compliant invoices. Other processors similar. DIY invoice generation requires jurisdiction-specific requirements review.

Fraud considerations

Fraud detection at multiple layers:

Velocity checks: same card, IP, device used N times in window. Flag or block. In-app or via processor tools.

3D Secure: additional authentication via card issuer (customer receives OTP or biometric). Shifts liability to issuer for approved transactions. Adds friction; conversion impact.

Stripe Radar / Adyen RevenueProtect: ML-based fraud scoring; rules engine; team review queue. Standard for their platforms.

Manual review: high-value or borderline transactions to human reviewer. Common for high-ticket items or new accounts.

Address Verification System (AVS): verify billing address matches card. Fraud indicator; not blocker.

Reconciliation with processor

Internal ledger (your DB) can diverge from processor ledger. Reasons:

  • Missed webhook events.
  • Bug in state machine transitions.
  • Processor-side manual adjustments.
  • Currency conversion differences.

Daily reconciliation job:

  1. Fetch processor events (charges, refunds, subscription events) for previous day.
  2. Compare to internal records.
  3. Flag divergence for review.

Divergence rate should be near zero. Non-zero = investigate; automate resolution for common patterns; alert on unusual spike.

Without reconciliation: divergence grows silently. Eventually 6-month audit finds thousands of mismatched records.

Observability that matters

Payment-specific metrics:

  • Charge success rate: succeeded / attempted. Overall + by card issuer + by country.
  • Decline reasons: breakdown of why charges failed.
  • State transitions per second: subscription lifecycle health.
  • Illegal transition attempts: should be near zero.
  • Webhook processing latency: event to state update.
  • Reconciliation divergence: daily count.
  • Dispute rate: disputes / charges (window).
  • Dunning success rate: PAST_DUE → ACTIVE.
  • Refund rate + reasons.

Dashboards for finance + engineering + customer service. Alerts on: charge success rate drop, dispute rate climb, illegal transitions, reconciliation divergence spike, webhook processing lag.

Marketplace patterns

Marketplaces (multiple sellers) add complexity:

  • Stripe Connect: Standard, Express, Custom account types. Different levels of onboarding + control.
  • Adyen for Platforms: equivalent; enterprise-focused.

Money movement:

  • Charge to platform; transfer to seller.
  • Charge direct to seller (destination charge).
  • Application fee / commission on transfer.

KYC + compliance: sellers must be identity-verified. Automated via processor onboarding. Requirements vary by jurisdiction + volume.

Refunds affect platform + seller: refund reduces future transfers to seller or reverses prior transfer.

Migration between processors

Migration is expensive. Reasons:

  • Cost pressure at scale.
  • Feature need (specific payment methods, better fraud tools).
  • Geographic expansion requiring different processor.

Migration pattern:

  1. Deploy new processor integration alongside existing.
  2. New customers on new processor.
  3. Existing customers migrated in waves (with re-auth for tokenized cards).
  4. Dual-run for period with reconciliation on both.
  5. Sunset old processor.

Card tokenization: cards tokenized on old processor may not migrate directly (PCI + processor-specific). Some processors offer card-on-file migration (network-to-network); others require re-collection.

Months, not weeks. Migration during high-volume periods (holidays) not recommended.

The failure patterns you will see

Missing idempotency: webhook retry causes duplicate charges or double refunds.

State transitions out of order: subscription webhook events arrive in wrong order; state corrupt.

PCI scope creep: card data logged accidentally; scope explodes.

Currency mismatch: charge in USD; refund in EUR; FX loss.

Dispute rate uncontrolled: fraud loss + processor penalties.

Tax calculation wrong: legal exposure + customer complaints.

Reconciliation gap: internal ledger diverges from processor ledger; hard to fix later.

Dunning after cancellation: canceled user still receives dunning emails + retries.

Trial abuse: same user re-enrolling in trial with different emails.

Card expiry surge: many cards expire same month; renewal failures spike.

Metrics that matter

  • Charge success rate: overall + by segment.
  • Recurring charge success: renewal reliability.
  • Time to first payment: friction indicator.
  • Trial-to-paid conversion rate: TRIALING → ACTIVE.
  • Dunning recovery rate: PAST_DUE → ACTIVE.
  • Dispute rate: rolling 30/60 day.
  • Refund rate: with reason breakdown.
  • Reconciliation divergence: daily.
  • Illegal transition rate: should be zero.
  • Webhook processing latency: event to updated state.

What to do next

If your webhook handlers don't verify signatures: fix that. Not optional.

If your webhook handlers aren't idempotent: fix that. Duplicate events cause duplicate charges.

If your subscription state is boolean columns: consider migrating to explicit state machine. See the state machine blog.

If your PCI scope is SAQ D-Merchant when SAQ A would work: fix that. Substantial audit + risk reduction.

If you don't reconcile daily with your processor: start. Divergence grows silently otherwise.

If your dispute rate is climbing: analyze reasons + implement 3D Secure for suspicious transactions + review customer service response times.

Combined with the payments-architect subagent, this framework is what turns payment integration from “the risky domain we ship carefully” into infrastructure that survives edge cases + processor changes + growth. It works.

FAQ

Frequently asked

Broader scope:

  • Processor-agnostic.
  • Design discipline.

Usually yes:

  • Multi-jurisdiction: mandatory.
  • Reduces error class.

Card data touch:

  • Never touches: SAQ A.
  • Touches: SAQ D.

Varies:

  • No card: 5-15%.
  • Card required: 40-70%.

Three:

  • No idempotency.
  • No state machine.
  • No reconciliation.

Share with