executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
ecommerce-payment-processingUse when the user asks about payment processing, payment gateway integration, Stripe, PayPal, subscription billing, PCI DSS compliance, payment orchestration, or recurring payments. Do NOT use for: general e-commerce checkout flow (ecommerce-checkout-cart), or in-app purchases (mobile-in-app-purchase).
Independent GitHub snapshot · Automated classification · Original documentation governs
Implement payment processing: gateway integration, subscription management, PCI DSS compliance, payment orchestration, fraud detection, and multi-currency support.
Exact user phrases: payment, Stripe, PayPal, credit card processing, PCI DSS, payment gateway, subscription billing, recurring payment, payment orchestration, fraud detection, chargeback, refund, payout, merchant account, payment intent, tokenization, 3D Secure.
Payment architecture design, gateway integration plan, PCI DSS compliance scope document, subscription billing model.
## Payment Processing Architecture
### Gateway: {gateway name}
### Methods: {payment methods}
### PCI Scope: {compliance level}
### Payment Flow
{flow: checkout -> payment intent -> authorization -> capture -> settlement}
### Recurring Billing
{subscription model, billing cycle, dunning process}
### Fraud Prevention
{rules engine, 3DS, velocity checks, address verification}
### Compliance Controls
{PCI DSS controls, data handling, tokenization, audit trail}
No preamble. No postamble. No explanations.
7000 tokens
What is the primary business model?
What geographic regions are served?
What is the projected transaction volume?
What subscription/recurring features are needed?
Is the payment amount known at checkout?
Is the customer present during payment?
Does the payment require additional verification?
Is the payment recurring or one-time?
How is payment data handled?
What is the annual transaction volume?
6M/year: Level 1. Full QSA audit, ASV scan quarterly, ROC report. Most stringent requirements.
Are you storing any cardholder data?
Client -> Checkout Page -> Backend -> Payment Gateway
+
Webhook Handler
+
Order Service
+
Database (orders, transactions)
| Gateway | Best For | Key Feature |
|---|---|---|
| Stripe | SaaS, subscriptions | Complete API, strong docs |
| PayPal | Consumer market | High trust, buyer protection |
| Braintree | Multi-gateway | PayPal + credit cards unified |
| Adyen | Enterprise | 250+ payment methods globally |
| Mollie | European market | Local payment methods (iDEAL, SEPA) |
abstraction_layer:
purpose: "Decouple business logic from specific gateway implementation"
components:
payment_service:
methods: ["create_payment", "capture_payment", "refund_payment", "get_payment_status"]
gateway_adapters:
stripe: "StripePaymentAdapter implements PaymentGatewayInterface"
paypal: "PayPalPaymentAdapter implements PaymentGatewayInterface"
adyen: "AdyenPaymentAdapter implements PaymentGatewayInterface"
webhook_handler:
stripe: "StripeWebhookHandler parses stripe events"
paypal: "PayPalWebhookHandler parses paypal IPN"
generic: "Normalized PaymentEvent emitted to internal event bus"
Plan -> Customer -> Subscription -> Invoice -> Payment
+
Failed -> Retry (dunning)
+
Max retries -> Cancel
tokenization_flow:
step_1: "Customer enters card details in gateway-hosted UI (Stripe Elements, PayPal SDK)"
step_2: "Gateway returns single-use token to frontend (tok_xxx, pm_xxx)"
step_3: "Frontend sends token to backend"
step_4: "Backend uses token to create payment or save for future use"
step_5: "For recurring: create customer + attach payment method (cus_xxx, pm_xxx)"
step_6: "Store payment method reference in database (gateway customer ID + payment method ID)"
step_7: "Use saved payment method for subsequent payments without re-entering card details"
benefits:
pci_scope: "Card data never touches server. SAQ A eligibility."
recurring: "Token stored for future payments. No need to re-collect card details."
security: "Token is useless if database breached (unlike PAN)."
refund_flow:
partial_refund: "Refund specific amount. Order status: partially_refunded."
full_refund: "Refund entire amount. Order status: refunded."
timing:
gateway_window: "Most gateways support refunds within 120 days"
beyond_window: "Manual refund required (bank transfer, check)"
chargeback_flow:
notification: "Gateway notifies via webhook. Chargeback reason code provided."
response_window: "Typically 7-21 days to respond with evidence."
evidence: "Proof of delivery, customer communication, refund record."
outcome:
won: "Funds returned. May have dispute fee."
lost: "Funds deducted. Dispute fee charged. Customer not refundable."
prevention:
- "Clear descriptor on statement"
- "Delivery confirmation for physical goods"
- "IP + AVS + CVV matching"
- "3D Secure authentication"
- "Customer service contact easily accessible"
multi_currency:
pricing:
- "Store prices in base currency (USD)"
- "Convert to display currency at checkout"
- "Update exchange rates daily"
- "Show total in local currency with exchange rate disclosure"
settlement:
- "Receive settlement in local currency or base currency"
- "Gateway handles currency conversion"
- "Conversion fees apply (typically 1-2%)"
regional_requirements:
eu:
pds2_sca: "Strong Customer Authentication required"
local_methods: "SEPA, iDEAL, Bancontact, Klarna"
us:
avs: "Address Verification System"
local_methods: "ACH, credit cards, PayPal, BNPL"
asia:
local_methods: "Alipay, WeChat Pay, PayTM, GrabPay"
qr_codes: "Common payment method"
fraud_rules:
velocity_checks:
- "Max 3 failed payment attempts per card per hour"
- "Max 10 failed attempts per customer per day"
- "Max $5000 total from single IP in 24 hours"
avs_check:
- "Street address mismatch: flag for manual review"
- "ZIP code mismatch: flag for manual review"
- "Address not verified: allow but mark as higher risk"
cvv_check:
- "CVV mismatch: decline transaction"
- "CVV not provided: decline if CVV required"
ip_analysis:
- "IP country mismatch with billing country: review"
- "Proxy/VPN detected: additional verification required"
- "IP on blocklist: decline"
amount_thresholds:
- "Single transaction > $10,000: manual review required"
- "Total daily > $25,000: manual review required"
- "Multiple transactions same card different shipping addresses: review"
machine_learning:
- "Gateway ML score < 50: auto-approve"
- "Score 50-80: manual review"
- "Score > 80: auto-decline"
payment_errors:
decline_codes:
insufficient_funds: "Notify customer, suggest alternative payment method"
card_expired: "Request updated card details"
fraud_suspected: "Notify customer to contact bank"
do_not_honor: "Request alternative payment method"
lost_card_stolen: "Report to gateway, block customer"
processing_error: "Retry with exponential backoff (max 3 attempts)"
network_errors:
timeout: "Check payment status via gateway API. Retry or mark as failed."
gateway_unavailable: "Queue payment for retry. Alert operations team."
webhook_delivery_failure: "Webhook has retry mechanism (typically 24h). Backfill via API query."
reconciliation:
daily: "Reconcile gateway transaction log against internal order records"
process: "Match on gateway transaction ID. Flag unmatched transactions for investigation."
frequency: "Automated daily reconciliation. Manual weekly review of exceptions."
double_charge_prevention: "Idempotency key on payment creation. Same key = same payment."
Pitfall 1: Storing raw card numbers. Increases PCI DSS scope to SAQ D. Massive compliance burden. Mitigation: use client-side tokenization (Stripe Elements, Braintree Hosted Fields). Never let card data reach your server.
Pitfall 2: Not handling webhook idempotency. Gateway may send the same webhook event multiple times. Processing twice results in double charge. Mitigation: idempotency key on webhook processing. Store processed event IDs.
Pitfall 3: Ignoring webhook delivery failures. Gateway webhooks can fail to deliver. Order stays in pending state. Mitigation: build webhook retry with monitoring. Implement fallback via scheduled API queries. Alert on webhook delivery failures.
Pitfall 4: No payment method abstraction. Direct coupling to Stripe API makes switching gateways impossible without full rewrite. Mitigation: payment service abstraction layer with gateway adapters. All business logic uses generic interface.
Pitfall 5: Incorrect 3D Secure handling. Not handling SCA exemptions correctly. EU payments declined. Mitigation: implement 3DS with proper exemption logic. Use gateway SDK for SCA handling. Test with real card network sandbox.
Pitfall 6: No retry logic for failed payments. One failure permanently marks subscription as failed. Customer churn increases. Mitigation: implement dunning process with multiple retry attempts over several days. Notify customer before final attempt.
Pitfall 7: Not handling currency conversion edge cases. Exchange rate changes between price display and payment capture. Mitigation: lock exchange rate at checkout. Display rates with timestamp. Recalculate only on explicit user action.
Pitfall 8: Overlooking refund timing rules. Attempting to refund a payment that has not settled. Refund fails. Mitigation: check settlement status before processing refund. Queue refunds for unsettled payments.
Practice 1: Use payment intent API pattern (Stripe PaymentIntent, Adyen /payments). Single API call handles authorization, 3DS, and confirmation. Idempotent by design. Better error handling than legacy charge API.
Practice 2: Implement idempotency keys on all payment operations. Generate unique key per payment attempt. Same key with same request = no duplicate. Store processed keys with expiry (24-48 hours).
Practice 3: Use gateway-hosted payment UI (Stripe Elements, Braintree Hosted Fields, PayPal Buttons). Reduces PCI scope. Handles card brand detection, validation, and formatting. Accessible by default. Less development effort.
Practice 4: Implement asynchronous payment confirmation via webhooks. Do not rely on frontend callback alone. Gateway webhook confirms settlement. Frontend callback is optimistic. Always validate with webhook.
Practice 5: Log all payment events with correlation ID. Gateway transaction ID, order ID, and internal correlation ID in every log line. Enables debugging across systems. Critical for chargeback response.
Practice 6: Build payment failure recovery flows. Allow customers to retry with different payment method. Save cart state during payment attempt. Do not lose the order on payment failure.
Practice 7: Test with real card network sandboxes. Stripe test mode, PayPal sandbox, Adyen test. Test all scenarios: success, decline, 3DS required, network error, timeout, webhook delivery.
Practice 8: Implement gateway failover for critical payments. If primary gateway is unavailable, route to secondary gateway. Requires consistent API abstraction layer and merchant accounts with both gateways.
## Payment Integration Checklist
### Pre-Integration
- [ ] Gateway selected and merchant account created
- [ ] PCI DSS compliance level determined
- [ ] Payment methods decided (credit card, digital wallet, local methods)
- [ ] 3D Secure requirements assessed (SCA for EU)
- [ ] Multi-currency requirements documented
- [ ] Fraud detection strategy defined
### Integration
- [ ] Payment service abstraction layer implemented
- [ ] Gateway adapter for selected gateway coded
- [ ] Client-side payment UI integrated (Elements, hosted fields, buttons)
- [ ] Payment intent API implemented
- [ ] Webhook handler with idempotency implemented
- [ ] Subscription billing flow implemented (if recurring)
- [ ] Refund flow implemented
- [ ] Multi-currency support implemented
### Testing
- [ ] All gateway test cards tested (success, decline, 3DS, insufficient funds)
- [ ] Webhook delivery tested (success, failure, retry)
- [ ] Idempotency verified (duplicate requests rejected)
- [ ] Edge cases: network timeout, gateway unavailable, partial refund
- [ ] Subscription billing tested (create, renew, fail, dunning, cancel)
- [ ] Currency conversion verified
- [ ] Fraud rules tested (velocity, AVS, CVV, IP)
### Compliance
- [ ] PCI DSS SAQ completed or QSA audit scheduled
- [ ] ASV scan passed (quarterly)
- [ ] Data handling review: no card data stored on servers
- [ ] Tokenization verified
- [ ] Security review of payment integration completed
### Production Readiness
- [ ] Monitoring dashboards configured (success rate, decline rate, latency, webhook delivery)
- [ ] Alerts configured (gateway down, elevated decline rate, reconciliation failures)
- [ ] Runbooks documented (payment incident response, gateway failover, manual refund process)
- [ ] Reconciliation process automated
- [ ] Operations team trained on payment support
| Level | Transaction Volume | Requirements |
|---|---|---|
| 1 | > 6M/year | Full QSA audit, ASV scan quarterly, ROC report |
| 2 | 1-6M/year | SAQ D + ASV scan quarterly, quarterly self-assessment |
| 3 | 20k-1M/year | SAQ + ASV scan quarterly, annual self-assessment |
| 4 | < 20k/year | SAQ (self-assessment), ASV scan quarterly |
interface PaymentGateway {
createPaymentIntent(params: PaymentIntentParams): Promise<PaymentIntentResult>;
capturePayment(paymentId: string, amount?: number): Promise<CaptureResult>;
refundPayment(paymentId: string, amount?: number): Promise<RefundResult>;
getPaymentStatus(paymentId: string): Promise<PaymentStatus>;
}
class StripeAdapter implements PaymentGateway {
async createPaymentIntent(params: PaymentIntentParams): Promise<PaymentIntentResult> {
const intent = await stripe.paymentIntents.create({
amount: Math.round(params.amount * 100),
currency: params.currency.toLowerCase(),
payment_method_types: ['card'],
metadata: { orderId: params.orderId },
idempotencyKey: params.idempotencyKey,
});
return { id: intent.id, clientSecret: intent.client_secret, status: intent.status };
}
async capturePayment(paymentId: string, amount?: number): Promise<CaptureResult> {
const intent = await stripe.paymentIntents.capture(paymentId, { amount_to_capture: amount ? Math.round(amount * 100) : undefined });
return { id: intent.id, status: intent.status, amountCaptured: intent.amount_received / 100 };
}
async refundPayment(paymentId: string, amount?: number): Promise<RefundResult> {
const refund = await stripe.refunds.create({ payment_intent: paymentId, amount: amount ? Math.round(amount * 100) : undefined });
return { id: refund.id, status: refund.status, amount: refund.amount / 100 };
}
}
async function handleStripeWebhook(req: Request, res: Response): Promise<void> {
const sig = req.headers['stripe-signature'];
const event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
// Idempotency check
const processed = await ProcessedEvent.findOne({ eventId: event.id });
if (processed) { res.json({ received: true }); return; }
await db.transaction(async (trx) => {
await ProcessedEvent.create({ eventId: event.id, handledAt: new Date() });
switch (event.type) {
case 'payment_intent.succeeded':
await OrderService.markPaid(event.data.object.metadata.orderId);
break;
case 'payment_intent.payment_failed':
await OrderService.markFailed(event.data.object.metadata.orderId, event.data.object.last_payment_error?.message);
break;
case 'charge.refunded':
await OrderService.markRefunded(event.data.object.metadata.orderId);
break;
}
});
res.json({ received: true });
}
**** + last 4.| Anti-Pattern | Why It Hurts | Fix |
|---|---|---|
| Synchronous payment in HTTP request | Blocks server for 5-30s on 3DS. Timeouts. | Async payment flow. Webhook confirms completion. |
| Storing full card number | PCI SAQ D scope. Massive compliance burden. | Client-side tokenization. Save only gateway token. |
| No webhook idempotency | Duplicate events double-charge customer. | Store processed event IDs. Reject duplicates. |
| Ignoring gateway error codes | "card_declined" vs "insufficient_funds" need different UX. | Map gateway codes to user-facing messages. |
| Single payment method | Customer with only Amex can't pay on Visa-only setup. | Support at least credit card + digital wallet. |