agentsclimarketplace

Payment gateways stripe adyen integration

Skill hamzabellouch/agent-skills/Financial and Fintech Engineering/payment-gateways-stripe-adyen-integration

Comprehensive collection of 380+ production-ready Agent Skills (26 domains) conforming to the Agent Skills Standard, featuring native auto-discovery for Antigravity, Gemini CLI, Claude Code, Cursor, and Codex.

Install
npx -y skills add hamzabellouch/agent-skills --skill payment-gateways-stripe-adyen-integration

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 18 days oldThe repository was created 18 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Architect and implement secure, PCI-DSS compliant payment processing integrations with Stripe and Adyen, featuring idempotent webhook handling, tokenization, multi-currency processing, automatic retries, and ledger-based reconciliation.

SKILL.md

13.3 KB, as published. Nobody here has run it

Payment Gateways Integration Architecture (Stripe & Adyen)

This skill package provides enterprise-grade patterns, architectural principles, PCI-DSS v4.0 compliance guidelines, and production code implementations for orchestrating payment processing across Stripe and Adyen.


1. Core Architectural & Compliance Standards

PCI-DSS v4.0 Compliance & Tokenization

  • Out-of-Scope Architecture (SAQ A / SAQ A-EP): Never ingest, process, or store Raw Primary Account Numbers (PAN), Card Verification Values (CVV/CVC), or PINs on your infrastructure. Use client-side SDKs (Stripe Elements / Adyen Web Drop-in) to tokenization endpoints directly owned by payment service providers (PSPs).
  • Network & Transport Layer Security: All API endpoints and client SDK communications must enforce TLS 1.3 with strong cipher suites (e.g., TLS_AES_256_GCM_SHA384).
  • Data Sanitization & Zero Logging: Redact all sensitive fields from application logs, trace contexts, and error payloads. Never log header signatures, API tokens, raw webhook payloads with PII, or tokens susceptible to account takeover.

Webhook Idempotency & Replay Protection

  • Idempotency Keying: Webhook events must be processed exactly once using distributed locking (e.g., Redis SET key value NX PX) keyed by the unique Event ID (event.id for Stripe, eventCode + pspReference for Adyen).
  • Time-Window & Signature Verification: Reject webhooks missing cryptographic HMAC signatures or with timestamps older than 300 seconds to prevent replay attacks.
  • Asynchronous Execution: Webhook HTTP handlers must validate signatures, enqueue the event into an isolated message queue (e.g., Kafka / RabbitMQ), and return an immediate HTTP 200 OK within 500ms to avoid PSP retry loops.

Reconciliation & Ledger Architecture

  • Double-Entry Bookkeeping: Model payments as state transitions across double-entry ledger accounts (Asset: PSP Clearing, Revenue, Expense: Gateway Fees, Liability: Customer Balance).
  • State Machine Integrity: Enforce strict status transitions (AUTHORIZED -> CAPTURED -> SETTLED / REFUNDED / DISPUTED).

2. PSP Capabilities Comparison Matrix

CapabilityStripe IntegrationAdyen Integration
Client TokenizationStripe Elements / PaymentIntents APIAdyen Drop-in / Components API
Webhook VerificationHMAC-SHA256 (Stripe-Signature header)HMAC-SHA256 Base64 encoded payload validation
Capture ModelImmediate or Manual (capture_method: 'manual')Automatic, Manual, or Delayed Capture rules
Payout & SettlementBalance Transactions & Payouts APIFinancial Reports (Settlement Detail Report)
Global Payment MethodsiDEAL, SEPA, Klarna, Alipay via PaymentIntents250+ local payment methods via /payments

3. Production Code Implementations

Python Implementation: Production-Grade Webhook Handler with Redis Idempotency & HMAC Validation

import base64
import hashlib
import hmac
import json
import time
import logging
from typing import Dict, Any, Optional
import redis
import stripe

logger = logging.getLogger("payment_orchestrator")
logger.setLevel(logging.INFO)

class PaymentWebhookProcessor:
    def __init__(self, stripe_webhook_secret: str, adyen_hmac_key: str, redis_client: redis.Redis):
        self.stripe_webhook_secret = stripe_webhook_secret
        self.adyen_hmac_key = adyen_hmac_key
        self.redis = redis_client
        self.lock_ttl_ms = 60000  # 60s processing lock

    def process_stripe_webhook(self, raw_body: bytes, sig_header: str) -> Dict[str, Any]:
        """
        Validates Stripe signature, enforces idempotency, and queues event.
        """
        try:
            event = stripe.Webhook.construct_event(
                payload=raw_body,
                sig_header=sig_header,
                secret=self.stripe_webhook_secret,
                tolerance=300
            )
        except stripe.error.SignatureVerificationError as e:
            logger.error("Invalid Stripe webhook signature", exc_info=True)
            raise ValueError("Invalid signature") from e

        event_id = event["id"]
        event_type = event["type"]

        if not self._acquire_idempotency_lock(f"stripe:{event_id}"):
            logger.info(f"Duplicate Stripe event skipped: {event_id}")
            return {"status": "already_processed", "event_id": event_id}

        try:
            self._route_stripe_event(event_type, event["data"]["object"])
            return {"status": "success", "event_id": event_id}
        except Exception as e:
            # Release lock on processing error to allow retry
            self.redis.delete(f"stripe:{event_id}")
            raise e

    def verify_adyen_hmac(self, notification_item: Dict[str, Any]) -> bool:
        """
        Verifies Adyen HMAC signature according to Adyen HMAC validator specs.
        """
        item = notification_item.get("NotificationRequestItem", {})
        hmac_signature = item.get("additionalData", {}).get("hmacSignature")
        if not hmac_signature:
            return False

        keys = [
            "pspReference", "originalReference", "merchantAccountCode", "merchantReference",
            "value", "currency", "eventCode", "success"
        ]
        
        # Format string to sign
        signed_vals = []
        for k in keys:
            val = str(item.get(k, "") or "")
            # Escape backslashes and colons
            escaped_val = val.replace("\\", "\\\\").replace(":", "\\:")
            signed_vals.append(escaped_val)

        payload_to_sign = ":".join(signed_vals).encode("utf-8")
        hmac_key_bytes = bytes.fromhex(self.adyen_hmac_key)
        
        calculated_hmac = base64.b64encode(
            hmac.new(hmac_key_bytes, payload_to_sign, hashlib.sha256).digest()
        ).decode("utf-8")

        return hmac.compare_digest(calculated_hmac, hmac_signature)

    def process_adyen_webhook(self, notification_payload: Dict[str, Any]) -> Dict[str, Any]:
        """
        Processes Adyen notification items with HMAC validation and idempotency.
        """
        items = notification_payload.get("notificationItems", [])
        processed = []

        for container in items:
            item = container.get("NotificationRequestItem", {})
            if not self.verify_adyen_hmac(container):
                logger.error("Adyen HMAC signature mismatch")
                continue

            psp_ref = item.get("pspReference")
            event_code = item.get("eventCode")
            idempotency_key = f"adyen:{psp_ref}:{event_code}"

            if not self._acquire_idempotency_lock(idempotency_key):
                logger.info(f"Duplicate Adyen notification skipped: {idempotency_key}")
                processed.append({"pspReference": psp_ref, "status": "duplicate"})
                continue

            # Process notification logic
            self._route_adyen_event(event_code, item)
            processed.append({"pspReference": psp_ref, "status": "processed"})

        return {"status": "ok", "processed": processed}

    def _acquire_idempotency_lock(self, lock_key: str) -> bool:
        """Atomic Redis distributed lock for idempotency enforcement."""
        return bool(self.redis.set(lock_key, "1", nx=True, px=self.lock_ttl_ms))

    def _route_stripe_event(self, event_type: str, data_object: Dict[str, Any]):
        if event_type == "payment_intent.succeeded":
            payment_intent_id = data_object["id"]
            amount = data_object["amount"]
            currency = data_object["currency"]
            logger.info(f"Stripe Payment Succeeded: {payment_intent_id} | {amount}{currency}")
            # Execute double-entry ledger settlement...
        elif event_type == "charge.dispute.created":
            dispute_id = data_object["id"]
            logger.warning(f"Stripe Dispute Created: {dispute_id}")

    def _route_adyen_event(self, event_code: str, item: Dict[str, Any]):
        if event_code == "AUTHORISATION" and item.get("success") == "true":
            psp_ref = item["pspReference"]
            val = item.get("value", {}).get("value")
            currency = item.get("value", {}).get("currency")
            logger.info(f"Adyen Authorisation Succeeded: {psp_ref} | {val} {currency}")

TypeScript Implementation: Modern Payment Intent & Checkout Orchestration

import stripe from 'stripe';
import crypto from 'crypto';

export interface PaymentRequest {
  merchantReference: string;
  amountCents: number;
  currency: string;
  paymentMethodToken: string;
  customerId: string;
}

export interface PaymentResult {
  transactionId: string;
  status: 'AUTHORIZED' | 'CAPTURED' | 'REQUIRES_ACTION' | 'FAILED';
  clientSecret?: string;
  rawResponse: Record<string, unknown>;
}

export class StripePaymentProvider {
  private stripeClient: stripe;

  constructor(apiKey: string) {
    this.stripeClient = new stripe(apiKey, {
      apiVersion: '2023-10-16',
      typescript: true,
    });
  }

  /**
   * Orchestrates PaymentIntent creation with 3D Secure 2.0 Strong Customer Authentication (SCA)
   */
  public async createPaymentIntent(req: PaymentRequest): Promise<PaymentResult> {
    const idempotencyKey = crypto
      .createHash('sha256')
      .update(`${req.merchantReference}-${req.amountCents}-${req.currency}`)
      .digest('hex');

    try {
      const intent = await this.stripeClient.paymentIntents.create(
        {
          amount: req.amountCents,
          currency: req.currency.toLowerCase(),
          customer: req.customerId,
          payment_method: req.paymentMethodToken,
          confirm: true,
          off_session: false,
          capture_method: 'manual', // Auth and capture separated for risk checks
          confirmation_method: 'automatic',
          metadata: {
            merchant_reference: req.merchantReference,
          },
        },
        {
          idempotencyKey,
        }
      );

      if (intent.status === 'requires_action' || intent.status === 'requires_source_action') {
        return {
          transactionId: intent.id,
          status: 'REQUIRES_ACTION',
          clientSecret: intent.client_secret ?? undefined,
          rawResponse: intent as unknown as Record<string, unknown>,
        };
      }

      if (intent.status === 'requires_capture') {
        return {
          transactionId: intent.id,
          status: 'AUTHORIZED',
          rawResponse: intent as unknown as Record<string, unknown>,
        };
      }

      throw new Error(`Unexpected PaymentIntent status: ${intent.status}`);
    } catch (err: any) {
      throw new Error(`Stripe Payment Intent Execution Failed: ${err.message}`);
    }
  }

  /**
   * Captures a previously authorized payment after manual anti-fraud review
   */
  public async captureAuthorizedPayment(paymentIntentId: string, amountToCaptureCents?: number): Promise<PaymentResult> {
    const captureOptions: stripe.PaymentIntentCaptureParams = {};
    if (amountToCaptureCents) {
      captureOptions.amount_to_capture = amountToCaptureCents;
    }

    const intent = await this.stripeClient.paymentIntents.capture(paymentIntentId, captureOptions);

    return {
      transactionId: intent.id,
      status: 'CAPTURED',
      rawResponse: intent as unknown as Record<string, unknown>,
    };
  }
}

4. Anti-Patterns & Pitfalls

Critical Architectural Mistakes

  1. Processing Webhooks Synchronously in HTTP Worker Threads

    • Impact: Gateways like Stripe/Adyen time out after 5-10 seconds and re-send webhooks, causing cascading duplicate executions and worker thread starvation.
    • Remediation: Accept payload, verify HMAC, publish event to broker (Kafka/AWS SQS), return HTTP 200 immediately.
  2. Using Floating-Point Numbers for Currency Operations

    • Impact: IEEE 754 floating-point rounding errors (0.1 + 0.2 = 0.30000000000000004) lead to balance mismatches in accounting ledgers.
    • Remediation: Always store and compute amounts as 64-bit integers in minor currency units (cents, pence) or fixed-point decimal objects (Decimal).
  3. Ignoring Client-Side 3D-Secure (3DS2) Challenge Flows

    • Impact: Soft-deletes and authorization rejections increase due to PSD2 SCA requirements in Europe and worldwide mandate compliance.
    • Remediation: Correctly propagate requires_action states back to client SDKs to render 3DS authentication modals.
  4. Storing API Keys or Webhook Secrets in Code Repositories

    • Impact: Secrets leak, leading to unauthorized refund triggers, customer data breach, or fraud.
    • Remediation: Inject secrets using KMS (HashiCorp Vault, AWS Secrets Manager) and support seamless secret rotation.

5. Verification & Testing Playbook

  • Stripe CLI Webhook Testing:
    stripe listen --forward-to localhost:8080/api/v1/webhooks/stripe
    stripe trigger payment_intent.succeeded
    
  • Adyen Mock Webhook Replay: Test HMAC generation using official Adyen test payload fixtures and verify zero-drop idempotency under simulated concurrent execution.
  • Chaos & Failure Testing: Simulate network splits during capture API calls and verify double-entry ledger idempotency keys prevent double charging.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.