Back to Skills

third-party-integration

aj-geddes
Updated Today
26 views
7
7
View on GitHub
Designapidata

About

This Claude Skill provides robust third-party API integration with built-in error handling, retry logic, and data transformation capabilities. Use it when connecting to external services like payment processors, messaging platforms, or analytics tools. It offers standardized patterns for API calls, authentication, and client wrappers to ensure reliable external service connections.

Documentation

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

Quick Install

/plugin add https://github.com/aj-geddes/useful-ai-prompts/tree/main/third-party-integration

Copy and paste this command in Claude Code to install this skill

GitHub 仓库

aj-geddes/useful-ai-prompts
Path: skills/third-party-integration

Related Skills

evaluating-llms-harness

Testing

This Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.

View skill

langchain

Meta

LangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.

View skill

llamaindex

Meta

LlamaIndex is a data framework for building RAG-powered LLM applications, specializing in document ingestion, indexing, and querying. It provides key features like vector indices, query engines, and agents, and supports over 300 data connectors. Use it for document Q&A, chatbots, and knowledge retrieval when building data-centric applications.

View skill

csv-data-summarizer

Meta

This skill automatically analyzes CSV files to generate comprehensive statistical summaries and visualizations using Python's pandas and matplotlib/seaborn. It should be triggered whenever a user uploads or references CSV data without prompting for analysis preferences. The tool provides immediate insights into data structure, quality, and patterns through automated analysis and visualization.

View skill