Claude Code Plugins

Community-maintained marketplace

Feedback

third-party-integration

@aj-geddes/useful-ai-prompts
4
0

Integrate external APIs and services with error handling, retry logic, and data transformation. Use when connecting to payment processors, messaging services, analytics platforms, or other third-party providers.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name third-party-integration
description Integrate external APIs and services with error handling, retry logic, and data transformation. Use when connecting to payment processors, messaging services, analytics platforms, or other third-party providers.

Third-Party Integration

Overview

Build robust integrations with external services using standardized patterns for API calls, error handling, authentication, and data transformation.

When to Use

  • Integrating payment processors (Stripe, PayPal)
  • Using messaging services (SendGrid, Twilio)
  • Connecting to analytics platforms (Mixpanel, Segment)
  • Syncing with storage services (AWS S3, Google Cloud)
  • Integrating CRM systems (Salesforce, HubSpot)
  • Building multi-service architectures

Instructions

1. Third-Party Client Wrapper

const axios = require('axios');

class ThirdPartyClient {
  constructor(config) {
    this.apiKey = config.apiKey;
    this.baseUrl = config.baseUrl;
    this.timeout = config.timeout || 30000;
    this.retryAttempts = config.retryAttempts || 3;
    this.retryDelay = config.retryDelay || 1000;
    this.client = axios.create({
      baseURL: this.baseUrl,
      timeout: this.timeout,
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      }
    });
  }

  async request(method, endpoint, data = null, options = {}) {
    let lastError;

    for (let attempt = 0; attempt < this.retryAttempts; attempt++) {
      try {
        const response = await this.client({
          method,
          url: endpoint,
          data,
          timeout: this.timeout,
          ...options
        });

        return {
          success: true,
          data: response.data,
          statusCode: response.status,
          headers: response.headers
        };
      } catch (error) {
        lastError = error;

        // Check if error is retryable
        if (!this.isRetryable(error) || attempt === this.retryAttempts - 1) {
          break;
        }

        // Exponential backoff
        const delay = this.retryDelay * Math.pow(2, attempt);
        await this.sleep(delay);
      }
    }

    return this.handleError(lastError);
  }

  isRetryable(error) {
    if (!error.response) return true; // Network error

    const status = error.response.status;
    // Retry on 5xx and specific 4xx errors
    return status >= 500 || [408, 429].includes(status);
  }

  handleError(error) {
    if (error.response) {
      return {
        success: false,
        error: {
          message: error.response.data?.message || error.message,
          code: error.response.data?.code || error.response.status,
          status: error.response.status,
          data: error.response.data
        }
      };
    }

    return {
      success: false,
      error: {
        message: error.message,
        code: 'NETWORK_ERROR'
      }
    };
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  async get(endpoint) {
    return this.request('GET', endpoint);
  }

  async post(endpoint, data) {
    return this.request('POST', endpoint, data);
  }

  async put(endpoint, data) {
    return this.request('PUT', endpoint, data);
  }

  async delete(endpoint) {
    return this.request('DELETE', endpoint);
  }
}

// Usage
const stripeClient = new ThirdPartyClient({
  apiKey: process.env.STRIPE_API_KEY,
  baseUrl: 'https://api.stripe.com/v1',
  timeout: 30000,
  retryAttempts: 3
});

const result = await stripeClient.post('/charges', {
  amount: 10000,
  currency: 'usd',
  source: 'tok_visa'
});

2. Payment Processor Integration (Stripe)

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

class PaymentService {
  async createCharge(userId, amount, paymentMethodId) {
    try {
      const customer = await this.getOrCreateCustomer(userId);

      const charge = await stripe.charges.create({
        amount: Math.round(amount * 100), // cents
        currency: 'usd',
        customer: customer.id,
        payment_method: paymentMethodId,
        confirm: true
      });

      // Log transaction
      await Transaction.create({
        userId,
        chargeId: charge.id,
        amount,
        status: charge.status,
        createdAt: new Date(charge.created * 1000)
      });

      return {
        success: true,
        chargeId: charge.id,
        status: charge.status
      };
    } catch (error) {
      console.error('Charge error:', error.message);

      if (error.code === 'card_declined') {
        return { success: false, error: 'Card declined' };
      }

      throw error;
    }
  }

  async refund(chargeId, amount = null) {
    try {
      const refund = await stripe.refunds.create({
        charge: chargeId,
        amount: amount ? Math.round(amount * 100) : undefined
      });

      await Transaction.updateOne(
        { chargeId },
        { refundId: refund.id, status: 'refunded' }
      );

      return { success: true, refundId: refund.id };
    } catch (error) {
      console.error('Refund error:', error.message);
      throw error;
    }
  }

  async getOrCreateCustomer(userId) {
    let customer = await Customer.findOne({ userId });

    if (!customer) {
      const stripeCustomer = await stripe.customers.create({
        metadata: { userId }
      });

      customer = await Customer.create({
        userId,
        stripeId: stripeCustomer.id
      });
    }

    return customer;
  }

  async handleWebhook(event) {
    switch (event.type) {
      case 'charge.succeeded':
        await this.handleChargeSucceeded(event.data.object);
        break;
      case 'charge.failed':
        await this.handleChargeFailed(event.data.object);
        break;
      case 'refund.created':
        await this.handleRefund(event.data.object);
        break;
    }
  }
}

// Webhook endpoint
app.post('/webhooks/stripe', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];

  try {
    const event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );

    await paymentService.handleWebhook(event);
    res.json({ received: true });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

3. Email Service Integration (SendGrid)

const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);

class EmailService {
  async sendEmail(to, templateId, templateData = {}) {
    try {
      const message = {
        to,
        from: process.env.FROM_EMAIL,
        templateId,
        dynamicTemplateData: {
          ...templateData,
          timestamp: new Date().toISOString()
        },
        trackingSettings: {
          clickTracking: { enabled: true },
          openTracking: { enabled: true }
        }
      };

      const response = await sgMail.send(message);

      // Log email
      await EmailLog.create({
        to,
        templateId,
        messageId: response[0].headers['x-message-id'],
        status: 'sent',
        sentAt: new Date()
      });

      return { success: true, messageId: response[0].headers['x-message-id'] };
    } catch (error) {
      console.error('Email error:', error.message);

      await EmailLog.create({
        to,
        templateId,
        error: error.message,
        status: 'failed'
      });

      throw error;
    }
  }

  async sendBulk(recipients, templateId, templateData) {
    const promises = recipients.map(recipient =>
      this.sendEmail(recipient, templateId, templateData).catch(err => ({
        recipient,
        error: err.message
      }))
    );

    return Promise.allSettled(promises);
  }

  async handleWebhook(event) {
    const { messageId, event: eventType } = event;

    await EmailLog.updateOne(
      { messageId },
      { status: eventType, updatedAt: new Date() }
    );
  }
}

// Usage
const emailService = new EmailService();

app.post('/api/send-welcome-email', async (req, res) => {
  const { email, firstName } = req.body;

  const result = await emailService.sendEmail(email, 'd-welcome-template-id', {
    firstName
  });

  res.json(result);
});

4. Python Third-Party Integration

import requests
import time
from typing import Optional, Dict, Any
from datetime import datetime
import logging

logger = logging.getLogger(__name__)

class APIClient:
    def __init__(self, api_key: str, base_url: str, timeout: int = 30):
        self.api_key = api_key
        self.base_url = base_url
        self.timeout = timeout
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        })

    def request(
        self,
        method: str,
        endpoint: str,
        data: Optional[Dict] = None,
        max_retries: int = 3
    ) -> Dict[str, Any]:
        url = f"{self.base_url}/{endpoint}"
        attempt = 0

        while attempt < max_retries:
            try:
                response = self.session.request(
                    method,
                    url,
                    json=data,
                    timeout=self.timeout
                )

                if response.status_code >= 200 and response.status_code < 300:
                    return {
                        'success': True,
                        'data': response.json(),
                        'status': response.status_code
                    }

                if response.status_code >= 500 or response.status_code == 429:
                    raise requests.RequestException(f"HTTP {response.status_code}")

                return {
                    'success': False,
                    'error': response.json().get('message', 'Error'),
                    'status': response.status_code
                }

            except requests.RequestException as e:
                attempt += 1
                if attempt >= max_retries:
                    logger.error(f"API request failed: {e}")
                    return {
                        'success': False,
                        'error': str(e),
                        'status': None
                    }

                wait_time = 2 ** attempt
                time.sleep(wait_time)

        return {'success': False, 'error': 'Max retries exceeded'}

    def get(self, endpoint: str) -> Dict[str, Any]:
        return self.request('GET', endpoint)

    def post(self, endpoint: str, data: Dict) -> Dict[str, Any]:
        return self.request('POST', endpoint, data)

    def put(self, endpoint: str, data: Dict) -> Dict[str, Any]:
        return self.request('PUT', endpoint, data)

    def delete(self, endpoint: str) -> Dict[str, Any]:
        return self.request('DELETE', endpoint)

# Payment processor example
class PaymentGateway(APIClient):
    def create_payment(self, amount: float, currency: str, customer_id: str):
        return self.post('charges', {
            'amount': int(amount * 100),
            'currency': currency,
            'customer': customer_id
        })

    def refund(self, charge_id: str, amount: Optional[float] = None):
        return self.post(f'charges/{charge_id}/refund', {
            'amount': int(amount * 100) if amount else None
        })

5. Data Transformation

class DataMapper {
  static stripeChargeToTransaction(charge) {
    return {
      id: charge.id,
      amount: charge.amount / 100,
      currency: charge.currency,
      status: charge.status,
      customerId: charge.customer,
      createdAt: new Date(charge.created * 1000),
      metadata: charge.metadata
    };
  }

  static sendgridEmailToLog(event) {
    return {
      messageId: event.sg_message_id,
      email: event.email,
      eventType: event.event,
      timestamp: new Date(event.timestamp * 1000),
      metadata: event
    };
  }

  static awsS3FileToRecord(s3Object) {
    return {
      key: s3Object.Key,
      size: s3Object.Size,
      lastModified: s3Object.LastModified,
      etag: s3Object.ETag,
      bucket: s3Object.Bucket
    };
  }
}

Best Practices

✅ DO

  • Implement retry logic with exponential backoff
  • Validate webhook signatures
  • Log all API interactions
  • Use environment variables for secrets
  • Transform API responses to internal models
  • Implement circuit breakers for critical services
  • Monitor API quota and rate limits
  • Add proper error handling
  • Use timeouts appropriately
  • Test with sandbox/test API keys

❌ DON'T

  • Hardcode API keys
  • Retry all errors indefinitely
  • Log sensitive data
  • Trust unvalidated webhook data
  • Ignore rate limits
  • Make synchronous blocking calls
  • Expose vendor-specific details to clients
  • Skip error handling
  • Use production keys in tests