Skip to content

agramms/jdpi-client

Repository files navigation

jdpi_client

CI Test Coverage Ruby Version Gem Version License Code Style Maintainability

A lightweight Ruby client for JDPI microservices (Auth, DICT, QR, SPI OP/OD, Participants). No Rails dependency. All service base URLs are fully configurable by environment.

📋 Prerequisites

Before installing jdpi_client, ensure you have:

  • Ruby 3.0 or higher (tested on Ruby 3.0, 3.1, 3.2, 3.3, and 3.4)
  • Bundler 2.0+ for dependency management
  • System dependencies (for advanced token storage):
    • libpq-dev (for PostgreSQL storage backend)
    • libsqlite3-dev (for SQLite storage backend)

Verify Prerequisites

ruby --version # Should be 3.0+ bundle --version # Should be 2.0+

Install System Dependencies

Ubuntu/Debian:

sudo apt-get update sudo apt-get install -y libpq-dev libsqlite3-dev

macOS (Homebrew):

brew install postgresql sqlite3

Installation

From Source (Development)

Add this line to your application's Gemfile:

gem 'jdpi_client', git: 'https://github.com/agramms/jdpi-client.git'

Then execute:

bundle install

Local Development Install

Clone and install from source:

git clone https://github.com/agramms/jdpi-client.git cd jdpi-client bundle install gem build jdpi_client.gemspec gem install jdpi_client-0.1.0.gem

Docker Development Setup

For a containerized development environment:

git clone https://github.com/agramms/jdpi-client.git cd jdpi-client docker-compose up -d # Starts Redis, PostgreSQL, and DynamoDB Local bundle install

🚀 Quick Setup Checklist

Follow these steps to get jdpi_client up and running:

1. Environment Setup

  • Install prerequisites (Ruby 3.0+, Bundler 2.0+, system dependencies)
  • Install the gem using one of the methods above
  • Create environment file (.env for local development)

2. Basic Configuration

Create a configuration file or add to your application initializer:

# config/initializers/jdpi_client.rb (Rails) # or create a separate configuration file require 'jdpi_client' JDPIClient.configure do |config| # Required settings config.jdpi_client_host = ENV.fetch('JDPI_CLIENT_HOST') config.oauth_client_id = ENV.fetch('JDPI_CLIENT_ID') config.oauth_secret = ENV.fetch('JDPI_CLIENT_SECRET') # Optional settings config.timeout = 10 config.logger = Logger.new($stdout) # Enable logging end

3. Environment Variables Setup

Create your .env file or set these environment variables:

# Required JDPI_CLIENT_HOST=api.yourbank.homl.jdpi.pstijd JDPI_CLIENT_ID=your_oauth_client_id JDPI_CLIENT_SECRET=your_oauth_secret # Optional (for advanced features) JDPI_TOKEN_ENCRYPTION_KEY=your_32_character_encryption_key_here REDIS_URL=redis://localhost:6379/0 DATABASE_URL=postgresql://user:password@localhost:5432/your_app

4. Verify Installation

Test your setup with this simple script:

require 'jdpi_client' # Test configuration puts "✅ Gem loaded successfully" puts "🌍 Environment: #{JDPIClient.config&.environment || 'not configured'}" puts "🔗 Host: #{JDPIClient.config&.jdpi_client_host || 'not configured'}" # Test authentication (requires valid credentials) begin auth_client = JDPIClient::Auth::Client.new token = auth_client.token! puts "🔑 Authentication: SUCCESS" rescue => e puts "❌ Authentication failed: #{e.message}" end

5. First API Call

Try your first JDPI API call:

# Get participants list (simple read operation) begin participants = JDPIClient::Participants.new result = participants.list puts "📋 Participants API: SUCCESS (#{result.size} participants)" rescue => e puts "❌ API call failed: #{e.message}" end

Quick start

require 'jdpi_client' JDPIClient.configure do |c| c.jdpi_client_host = ENV.fetch('JDPI_CLIENT_HOST', 'api.mybank.homl.jdpi.pstijd') # Environment and protocol auto-detected from hostname: # - Contains 'prod' or 'production' -> HTTPS + production # - Otherwise -> HTTP + homolog c.oauth_client_id = ENV['JDPI_CLIENT_ID'] c.oauth_secret = ENV['JDPI_CLIENT_SECRET'] c.timeout = 8 c.open_timeout = 2 c.logger = Logger.new($stdout) end token = JDPIClient::Auth::Client.new.token! resp = JDPIClient::SPI::OP.new.create_order!( valor: 1050, chave: "fulano@bank.com", prioridade_pagamento: 0, finalidade: 1, dt_hr_requisicao_psp: Time.now.utc.iso8601 )

Quality Metrics

Tests Test Status Line Coverage Branch Coverage Ruby Support

Features

  • 🔐 Automatic OAuth2 token management with thread-safe caching
  • 🏢 Multi-backend token storage (Memory, Redis, Database, DynamoDB)
  • 🔒 Token encryption for sensitive data protection
  • 🌐 Environment auto-detection from hostname (prod/homl)
  • 🔄 Built-in retry logic with exponential backoff
  • 🛡️ Comprehensive error handling with structured exceptions
  • 🔑 Idempotency support for safe payment operations
  • Distributed locking for clustered environments
  • 📝 Request/response logging for debugging
  • 🚀 No Rails dependency - works with any Ruby application
  • 🧪 High test coverage (75.65%) with comprehensive test suite

Services Supported

  • Auth - OAuth2 authentication and token management
  • SPI OP - Payment initiation and settlement
  • SPI OD - Refund and dispute management
  • DICT - PIX key management, claims, and infractions
  • QR - QR code generation for payments
  • Participants - Participant information management

Configuration

Environment Detection

The gem automatically detects environment and protocol from the hostname:

Hostname Pattern Environment Protocol Example
Contains prod or production Production HTTPS https://api.bank.prod.jdpi.pstijd
Any other hostname Homolog HTTP http://api.bank.homl.jdpi.pstijd

Advanced Configuration

JDPIClient.configure do |config| config.jdpi_client_host = ENV.fetch('JDPI_CLIENT_HOST') config.oauth_client_id = ENV.fetch('JDPI_CLIENT_ID') config.oauth_secret = ENV.fetch('JDPI_CLIENT_SECRET') # Optional settings config.timeout = 10 # Request timeout in seconds config.open_timeout = 3 # Connection timeout in seconds config.logger = Rails.logger if defined?(Rails) # Token storage configuration (for clustered environments) config.token_storage_adapter = :memory # Default: in-memory storage config.token_storage_key_prefix = 'jdpi_client' # Cache key prefix config.token_encryption_enabled = true # Encrypt sensitive tokens config.token_encryption_key = ENV['JDPI_TOKEN_ENCRYPTION_KEY'] # 32+ character key end

Token Storage Configuration

The gem supports multiple token storage backends for clustered and distributed environments:

Memory Storage (Default)

config.token_storage_adapter = :memory # ✅ Fast and simple # ❌ Not shared between processes/servers # 👍 Best for: Single-server applications, development

Redis Storage

config.token_storage_adapter = :redis config.token_storage_url = ENV['REDIS_URL'] || 'redis://localhost:6379/0' config.token_storage_options = { timeout: 5, reconnect_attempts: 3, reconnect_delay: 1 } # ✅ Distributed caching with automatic expiration # ✅ High performance with built-in clustering # 👍 Best for: Production clustered environments

Database Storage

config.token_storage_adapter = :database config.token_storage_url = ENV['DATABASE_URL'] # Any database supported by Ruby config.token_storage_options = { table_name: 'jdpi_client_tokens' # Custom table name } # ✅ Persistent storage with transaction safety # ✅ Works with existing database infrastructure # 👍 Best for: Applications with existing database setup

DynamoDB Storage

config.token_storage_adapter = :dynamodb config.token_storage_options = { table_name: 'jdpi-tokens', region: 'us-east-1', access_key_id: ENV['AWS_ACCESS_KEY_ID'], # Optional if using IAM roles secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] # Optional if using IAM roles } # ✅ Serverless with automatic scaling # ✅ Built-in TTL for automatic token cleanup # 👍 Best for: AWS-based serverless applications

Token Encryption

Enable encryption for sensitive token data:

config.token_encryption_enabled = true config.token_encryption_key = ENV['JDPI_TOKEN_ENCRYPTION_KEY'] # 32+ characters required # Generate a secure encryption key: # ruby -e "require 'securerandom'; puts SecureRandom.hex(32)"

Production Configuration Examples

Rails Application with Redis:

# config/initializers/jdpi_client.rb JDPIClient.configure do |config| config.jdpi_client_host = ENV.fetch('JDPI_CLIENT_HOST') config.oauth_client_id = ENV.fetch('JDPI_CLIENT_ID') config.oauth_secret = ENV.fetch('JDPI_CLIENT_SECRET') config.logger = Rails.logger # Shared Redis cache for clustered Rails servers config.token_storage_adapter = :redis config.token_storage_url = ENV.fetch('REDIS_URL') config.token_encryption_enabled = Rails.env.production? config.token_encryption_key = ENV.fetch('JDPI_TOKEN_ENCRYPTION_KEY') config.token_storage_key_prefix = "#{Rails.env}:jdpi" end

Serverless Application with DynamoDB:

JDPIClient.configure do |config| config.jdpi_client_host = ENV.fetch('JDPI_CLIENT_HOST') config.oauth_client_id = ENV.fetch('JDPI_CLIENT_ID') config.oauth_secret = ENV.fetch('JDPI_CLIENT_SECRET') # DynamoDB for serverless environments config.token_storage_adapter = :dynamodb config.token_storage_options = { table_name: ENV.fetch('JDPI_TOKENS_TABLE', 'jdpi-tokens'), region: ENV.fetch('AWS_REGION', 'us-east-1') } config.token_encryption_enabled = true config.token_encryption_key = ENV.fetch('JDPI_TOKEN_ENCRYPTION_KEY') end

Development/Testing:

JDPIClient.configure do |config| config.jdpi_client_host = 'api.test.homl.jdpi.pstijd' config.oauth_client_id = 'test_client_id' config.oauth_secret = 'test_secret' # Simple memory storage for development config.token_storage_adapter = :memory config.logger = Logger.new($stdout) if ENV['DEBUG'] end

🔧 Environment Variables Reference

This section lists all environment variables that jdpi_client recognizes:

Required Variables

Variable Description Example Notes
JDPI_CLIENT_HOST JDPI API hostname api.mybank.homl.jdpi.pstijd Auto-detects environment and protocol
JDPI_CLIENT_ID OAuth2 client ID your_client_id Provided by JDPI
JDPI_CLIENT_SECRET OAuth2 client secret your_client_secret Keep secure!

Optional Variables

Variable Description Default Example
JDPI_TIMEOUT Request timeout (seconds) 8 10
JDPI_OPEN_TIMEOUT Connection timeout (seconds) 2 3
JDPI_LOG_LEVEL Logging level info debug, warn, error
JDPI_TOKEN_ENCRYPTION_KEY Token encryption key (32+ chars) nil Generated with SecureRandom.hex(32)

Storage Backend Variables

Redis Storage

Variable Description Default Example
REDIS_URL Redis connection URL redis://localhost:6379/0 redis://user:pass@host:port/db
JDPI_REDIS_TIMEOUT Redis operation timeout 5 10
JDPI_REDIS_RECONNECT_ATTEMPTS Reconnection attempts 3 5

Database Storage

Variable Description Default Example
DATABASE_URL Database connection URL nil postgresql://user:pass@host/db
JDPI_DB_TABLE_NAME Token storage table name jdpi_client_tokens my_tokens

DynamoDB Storage

Variable Description Default Example
AWS_REGION AWS region us-east-1 us-west-2
AWS_ACCESS_KEY_ID AWS access key nil IAM user access key
AWS_SECRET_ACCESS_KEY AWS secret key nil IAM user secret
JDPI_DYNAMODB_TABLE DynamoDB table name jdpi-tokens my-jdpi-tokens
DYNAMODB_ENDPOINT DynamoDB endpoint (for local) nil http://localhost:8000

Development & Testing Variables

Variable Description Default Example
RAILS_ENV / RACK_ENV Application environment nil development, production
DEBUG Enable debug logging false true
COVERAGE Enable test coverage false true
TEST_ADAPTER Test storage adapter memory redis, database, dynamodb

Example .env Files

Development (.env.development):

JDPI_CLIENT_HOST=api.test.homl.jdpi.pstijd JDPI_CLIENT_ID=test_client_id JDPI_CLIENT_SECRET=test_client_secret JDPI_TIMEOUT=10 DEBUG=true

Production (.env.production):

JDPI_CLIENT_HOST=api.mybank.prod.jdpi.pstijd JDPI_CLIENT_ID=prod_client_id JDPI_CLIENT_SECRET=super_secure_secret JDPI_TOKEN_ENCRYPTION_KEY=1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef REDIS_URL=redis://cache.production.com:6379/1 JDPI_TIMEOUT=8 JDPI_OPEN_TIMEOUT=3

Testing (.env.test):

JDPI_CLIENT_HOST=api.test.homl.jdpi.pstijd JDPI_CLIENT_ID=test_client JDPI_CLIENT_SECRET=test_secret TEST_ADAPTER=memory COVERAGE=true

Usage Examples

Authentication & Token Management

The gem automatically handles OAuth2 token management with intelligent caching:

# Basic token usage - automatically cached and refreshed auth_client = JDPIClient::Auth::Client.new token = auth_client.token! # Thread-safe, auto-refreshes when expired # Scope-specific tokens for different JDPI services dict_token = auth_client.token!(requested_scopes: ['auth_apim', 'dict_api']) spi_token = auth_client.token!(requested_scopes: ['auth_apim', 'spi_api']) # Use as a proc for HTTP clients token_provider = auth_client.to_proc http_client.authorization = token_provider # Force token refresh if needed auth_client.refresh! # Get token information for debugging info = auth_client.token_info puts "Token cached: #{info[:cached]}" puts "Storage type: #{info[:storage_type]}" puts "Expires at: #{info[:expires_at]}"

PIX Payment

# Create payment order spi_client = JDPIClient::SPI::OP.new response = spi_client.create_order!( valor: 2500, # R$ 25.00 in centavos chave: "user@bank.com", descricao: "Payment for services", prioridade_pagamento: 0, finalidade: 1, dt_hr_requisicao_psp: Time.now.utc.iso8601, idempotency_key: SecureRandom.uuid ) # Check payment status status = spi_client.consult_request(response['id_req'])

PIX Key Management

# Register new PIX key dict_client = JDPIClient::DICT::Keys.new dict_client.create_key!( tipo: "EMAIL", chave: "user@bank.com", conta: account_info ) # Claim existing key claims_client = JDPIClient::DICT::Claims.new claims_client.create_claim!( chave: "user@bank.com", tipo_reivindicacao: "OWNERSHIP" )

QR Code Generation

# Generate payment QR code qr_client = JDPIClient::QR::Client.new qr_response = qr_client.create_qr!( valor: 1500, # R$ 15.00 descricao: "Coffee payment" ) puts qr_response['qr_code'] puts qr_response['pix_copia_cola']

📚 API Reference

This section provides detailed information about JDPI APIs and their request/response formats.

Complete API Documentation

For comprehensive API documentation, see the /docs directory:

Authentication API

Endpoint: /auth/jdpi/connect/token

Request Format:

POST /auth/jdpi/connect/token Content-Type: application/x-www-form-urlencoded client_id=YOUR_CLIENT_ID& client_secret=YOUR_CLIENT_SECRET& grant_type=client_credentials& scope=auth_apim spi_api dict_api

Response Format:

{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 3600, "scope": "auth_apim spi_api dict_api" }

Ruby Example:

auth_client = JDPIClient::Auth::Client.new token = auth_client.token! # Token is automatically cached and refreshed

SPI OP (Outbound Payments) API

Create Payment Order

Endpoint: /spi/api/v1/op

Request Format:

POST /spi/api/v1/op Authorization: Bearer {token} Content-Type: application/json { "valor": 1050, "chave": "user@bank.com", "descricao": "Payment description", "prioridade_pagamento": 0, "finalidade": 1, "dt_hr_requisicao_psp": "2024-01-15T10:30:00Z", "idempotency_key": "unique-request-id-123" }

Response Format:

{ "id_req": "12345678-1234-1234-1234-123456789012", "status": "ACSP", "dt_hr_resposta": "2024-01-15T10:30:01Z", "valor": 1050, "chave": "user@bank.com" }

Ruby Example:

spi_client = JDPIClient::SPI::OP.new response = spi_client.create_order!( valor: 1050, # R$ 10.50 in centavos chave: "user@bank.com", descricao: "Coffee payment", prioridade_pagamento: 0, finalidade: 1, dt_hr_requisicao_psp: Time.now.utc.iso8601, idempotency_key: SecureRandom.uuid ) puts response['id_req'] # Payment ID

Query Payment Status

Endpoint: /spi/api/v1/op/{id_req}

Request Format:

GET /spi/api/v1/op/12345678-1234-1234-1234-123456789012 Authorization: Bearer {token}

Response Format:

{ "id_req": "12345678-1234-1234-1234-123456789012", "status": "ACCC", "dt_hr_resposta": "2024-01-15T10:30:01Z", "valor": 1050, "chave": "user@bank.com", "end_to_end_id": "E12345678202401151030123456789012" }

DICT (PIX Key Management) API

Register PIX Key

Endpoint: /dict/api/v2/key

Request Format:

POST /dict/api/v2/key Authorization: Bearer {token} Content-Type: application/json { "tipo": "EMAIL", "chave": "user@bank.com", "conta": { "ispb": "12345678", "agencia": "0001", "conta": "12345678", "tipo_conta": "CACC" } }

Response Format:

{ "chave": "user@bank.com", "tipo": "EMAIL", "status": "OWNED", "dt_criacao": "2024-01-15T10:30:00Z" }

Ruby Example:

dict_client = JDPIClient::DICT::Keys.new response = dict_client.create_key!( tipo: "EMAIL", chave: "user@bank.com", conta: { ispb: "12345678", agencia: "0001", conta: "12345678", tipo_conta: "CACC" } )

Query PIX Key

Endpoint: /dict/api/v2/key/{key_value}

Request Format:

GET /dict/api/v2/key/user@bank.com Authorization: Bearer {token}

Response Format:

{ "chave": "user@bank.com", "tipo": "EMAIL", "conta": { "ispb": "12345678", "nome": "Bank Name", "agencia": "0001", "conta": "12345678", "tipo_conta": "CACC" }, "status": "OWNED" }

QR Code API

Generate QR Code

Endpoint: /qr/api/v1/qr

Request Format:

POST /qr/api/v1/qr Authorization: Bearer {token} Content-Type: application/json { "valor": 2500, "descricao": "Payment for services", "chave": "merchant@bank.com" }

Response Format:

{ "qr_code": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51...", "pix_copia_cola": "00020126580014br.gov.bcb.pix013639c02...", "txid": "12345678901234567890123456789012" }

Ruby Example:

qr_client = JDPIClient::QR::Client.new response = qr_client.create_qr!( valor: 2500, # R$ 25.00 descricao: "Coffee and pastry", chave: "merchant@bank.com" ) puts "QR Code: #{response['qr_code']}" puts "PIX Copy/Paste: #{response['pix_copia_cola']}"

Common Response Codes

HTTP Status Description Action
200 Success Request completed successfully
201 Created Resource created successfully
400 Bad Request Check request format and parameters
401 Unauthorized Refresh authentication token
403 Forbidden Check permissions and scope
404 Not Found Resource doesn't exist
429 Rate Limited Implement backoff/retry logic
500 Internal Error Retry with exponential backoff
502/503 Service Unavailable Check JDPI service status

Request/Response Headers

Common Request Headers:

Authorization: Bearer {access_token} Content-Type: application/json Accept: application/json X-Idempotency-Key: {unique-request-id} # For payment operations

Common Response Headers:

Content-Type: application/json X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1640995200 X-Request-ID: 12345678-1234-1234-1234-123456789012

Error Response Format

All JDPI APIs return structured error responses:

{ "error": { "code": "INVALID_REQUEST", "message": "The request format is invalid", "details": [ { "field": "valor", "message": "Value must be greater than 0" } ] }, "timestamp": "2024-01-15T10:30:00Z", "path": "/spi/api/v1/op" }

Webhooks & Notifications

JDPI can send webhooks for payment status updates:

Webhook Payload Format:

{ "event_type": "payment.status_changed", "id_req": "12345678-1234-1234-1234-123456789012", "status": "ACCC", "timestamp": "2024-01-15T10:30:01Z", "end_to_end_id": "E12345678202401151030123456789012" }

Webhook Signature Verification:

# Verify webhook authenticity def verify_webhook(payload, signature, secret) expected_signature = OpenSSL::HMAC.hexdigest('sha256', secret, payload) Rack::Utils.secure_compare(signature, expected_signature) end

Testing & Sandbox

Homolog Environment:

  • Base URL: http://api.bank.homl.jdpi.pstijd
  • Use test credentials provided by JDPI
  • Safe for testing without real money

Test PIX Keys:

  • Email: test@example.com
  • Phone: +5511999999999
  • CPF: 12345678901 (test CPF)
  • Random Key: 123e4567-e89b-12d3-a456-426614174000

Error Handling

The gem provides structured error handling for different scenarios:

begin response = spi_client.create_order!(payment_data) rescue JDPIClient::Errors::Validation => e # Handle validation errors (400) puts "Invalid request: #{e.message}" rescue JDPIClient::Errors::Unauthorized => e # Handle authentication errors (401) puts "Authentication failed: #{e.message}" rescue JDPIClient::Errors::RateLimited => e # Handle rate limiting (429) puts "Rate limited, please retry later" rescue JDPIClient::Errors::ServerError => e # Handle server errors (5xx) puts "Server error: #{e.message}" end

🔒 Security Considerations

Security is critical when working with PIX payments. Follow these best practices:

1. Credential Management

✅ DO:

  • Store credentials in environment variables, never in code
  • Use encrypted storage for production credentials
  • Rotate OAuth secrets regularly
  • Use different credentials for each environment

❌ DON'T:

  • Commit credentials to version control
  • Log or print sensitive information
  • Share credentials across environments
  • Store credentials in plain text files
# ✅ Good - Environment variables config.oauth_secret = ENV.fetch('JDPI_CLIENT_SECRET') # ❌ Bad - Hard-coded credentials config.oauth_secret = 'my-secret-key' # Never do this!

2. Token Security

Enable token encryption in production:

config.token_encryption_enabled = true config.token_encryption_key = ENV.fetch('JDPI_TOKEN_ENCRYPTION_KEY') # Generate a secure encryption key: # ruby -e "require 'securerandom'; puts SecureRandom.hex(32)"

Token storage recommendations:

  • Use Redis/Database/DynamoDB in production (not memory)
  • Enable encryption for all stored tokens
  • Set appropriate token TTL values
  • Monitor token usage patterns

3. Network Security

HTTPS/TLS Configuration:

  • Always use HTTPS in production (auto-detected for prod hostnames)
  • Validate SSL certificates
  • Use TLS 1.2 or higher
  • Configure proper timeout values
config.timeout = 8 # Request timeout config.open_timeout = 3 # Connection timeout

Firewall and Access:

  • Whitelist JDPI IP addresses
  • Restrict outbound connections to JDPI endpoints only
  • Use VPN or private networks when possible
  • Monitor network traffic for anomalies

4. Logging Security

Safe logging practices:

# ✅ Good - Structured logging without sensitive data logger.info "PIX payment initiated", { order_id: order_id, amount_cents: amount, timestamp: Time.now.utc.iso8601 } # ❌ Bad - Logging sensitive information logger.info "Token: #{token}" # Never log tokens! logger.info "Request: #{request_body.inspect}" # May contain secrets

Configure logging levels:

# Production - minimal logging config.logger.level = Logger::WARN # Development - detailed logging config.logger.level = Logger::DEBUG if Rails.env.development?

5. Application Security

Input validation:

  • Validate all payment parameters
  • Sanitize user inputs
  • Use strong typing where possible
  • Implement request size limits

Error handling:

  • Never expose internal errors to users
  • Log security events (failed authentication, etc.)
  • Implement rate limiting
  • Use structured error responses
begin response = spi_client.create_order!(payment_data) rescue JDPIClient::Errors::Unauthorized => e # ✅ Good - Log security event, return generic error security_logger.warn "Authentication failed for client #{client_id}" render json: { error: 'Authentication failed' }, status: :unauthorized # ❌ Bad - Expose detailed error information render json: { error: e.message }, status: :unauthorized end

6. Production Deployment

Environment separation:

  • Use completely separate credentials for each environment
  • Never use production credentials in development/testing
  • Implement proper CI/CD security practices
  • Use infrastructure as code for consistent deployments

Secret management:

  • Use dedicated secret management services (AWS Secrets Manager, Azure Key Vault, etc.)
  • Implement secret rotation procedures
  • Monitor secret access and usage
  • Use service accounts with minimal permissions

Example secure production configuration:

# config/initializers/jdpi_client.rb JDPIClient.configure do |config| config.jdpi_client_host = ENV.fetch('JDPI_CLIENT_HOST') config.oauth_client_id = ENV.fetch('JDPI_CLIENT_ID') config.oauth_secret = ENV.fetch('JDPI_CLIENT_SECRET') # Security settings config.timeout = 8 config.open_timeout = 3 config.logger = Rails.logger config.logger.level = Logger::WARN # Minimal production logging # Encrypted token storage config.token_storage_adapter = :redis config.token_storage_url = ENV.fetch('REDIS_URL') config.token_encryption_enabled = true config.token_encryption_key = ENV.fetch('JDPI_TOKEN_ENCRYPTION_KEY') config.token_storage_key_prefix = "#{Rails.env}:jdpi" end

7. Monitoring & Alerting

Set up monitoring for:

  • Failed authentication attempts
  • Unusual payment patterns
  • Token refresh failures
  • Network connectivity issues
  • Performance degradation

Security alerts:

  • Multiple authentication failures
  • Unexpected geographic access
  • Token encryption/decryption failures
  • Suspicious payment amounts or patterns

8. Compliance & Auditing

PIX Compliance:

  • Follow Central Bank of Brazil regulations
  • Implement proper audit logs
  • Maintain transaction records
  • Follow data protection requirements (LGPD)

Audit logging:

  • Log all payment operations with timestamps
  • Track user actions and changes
  • Maintain immutable audit trails
  • Regular compliance reviews

⚡ Performance & Rate Limiting

Understanding JDPI's performance characteristics and rate limits is crucial for production applications.

JDPI Rate Limits

Standard Rate Limits:

  • Authentication: 10 requests/minute per client
  • PIX Payments (SPI OP): 100 requests/minute per participant
  • PIX Queries (SPI OD): 200 requests/minute per participant
  • DICT Operations: 50 requests/minute per participant
  • QR Generation: 300 requests/minute per participant

Rate Limit Headers: JDPI returns these headers with rate limit information:

X-RateLimit-Limit: 100 X-RateLimit-Remaining: 45 X-RateLimit-Reset: 1640995200 

Performance Characteristics

Expected Response Times:

  • Token Requests: < 200ms
  • PIX Payments: < 500ms
  • Balance Queries: < 300ms
  • DICT Lookups: < 400ms
  • QR Generation: < 100ms

Best Practices for Performance:

  1. Token Caching (automatically handled by jdpi_client):
# Tokens are cached automatically with intelligent refresh auth_client = JDPIClient::Auth::Client.new token = auth_client.token! # Cached for subsequent requests
  1. Connection Reuse:
# Configure reasonable timeouts config.timeout = 8 # Request timeout config.open_timeout = 3 # Connection timeout
  1. Request Batching (where supported):
# Batch DICT lookups when possible dict_client = JDPIClient::DICT::Keys.new keys = ["user1@bank.com", "user2@bank.com", "user3@bank.com"] # Process in batches to respect rate limits

Rate Limiting Strategies

1. Exponential Backoff:

def make_request_with_backoff(retries = 3, delay = 1) begin spi_client.create_order!(payment_data) rescue JDPIClient::Errors::RateLimited => e if retries > 0 sleep(delay) make_request_with_backoff(retries - 1, delay * 2) else raise e end end end

2. Rate Limit Monitoring:

# Check rate limit status before making requests def check_rate_limits(response) remaining = response.headers['X-RateLimit-Remaining'].to_i reset_time = response.headers['X-RateLimit-Reset'].to_i if remaining < 10 wait_time = reset_time - Time.now.to_i puts "⚠️ Rate limit low (#{remaining} remaining), resets in #{wait_time}s" end end

3. Request Queue Management:

class JDPIRequestQueue def initialize(requests_per_minute: 90) # Stay under 100/min limit @requests_per_minute = requests_per_minute @request_times = [] end def throttled_request(&block) wait_if_needed result = block.call @request_times << Time.now result end private def wait_if_needed now = Time.now @request_times.reject! { |time| time < now - 60 } # Keep last minute if @request_times.size >= @requests_per_minute sleep_time = 60 - (now - @request_times.first) sleep(sleep_time) if sleep_time > 0 end end end # Usage queue = JDPIRequestQueue.new(requests_per_minute: 90) queue.throttled_request { spi_client.create_order!(payment_data) }

High-Volume Applications

Connection Pooling: For high-volume applications, consider implementing connection pooling:

require 'connection_pool' JDPI_POOL = ConnectionPool.new(size: 25, timeout: 5) do JDPIClient::SPI::OP.new end # Use pooled connections JDPI_POOL.with do |spi_client| spi_client.create_order!(payment_data) end

Distributed Rate Limiting: For multiple application instances, use Redis for distributed rate limiting:

class DistributedRateLimit def initialize(redis, limit:, window:) @redis = redis @limit = limit @window = window end def allow_request?(key) current_time = Time.now.to_i window_start = current_time - @window pipe = @redis.pipelined do @redis.zremrangebyscore(key, 0, window_start) @redis.zcard(key) @redis.zadd(key, current_time, current_time) @redis.expire(key, @window) end current_requests = pipe[1] current_requests < @limit end end # Usage rate_limiter = DistributedRateLimit.new( Redis.current, limit: 90, window: 60 ) if rate_limiter.allow_request?("jdpi:spi:#{institution_id}") spi_client.create_order!(payment_data) else # Handle rate limit exceeded raise "Rate limit exceeded" end

Performance Monitoring

Key Metrics to Track:

  • Request latency percentiles (P50, P95, P99)
  • Rate limit utilization
  • Token refresh frequency
  • Connection pool utilization
  • Error rates by endpoint

Example Monitoring:

class JDPIMetrics def self.track_request(endpoint, &block) start_time = Time.now begin result = block.call duration = Time.now - start_time # Log successful request Rails.logger.info "JDPI Request", { endpoint: endpoint, duration_ms: (duration * 1000).round(2), status: 'success' } result rescue => e duration = Time.now - start_time # Log failed request Rails.logger.warn "JDPI Request Failed", { endpoint: endpoint, duration_ms: (duration * 1000).round(2), error: e.class.name, status: 'error' } raise e end end end # Usage JDPIMetrics.track_request('spi.create_order') do spi_client.create_order!(payment_data) end

Optimization Tips

  1. Cache DICT Lookups: PIX keys don't change frequently
  2. Batch Operations: Group related requests when possible
  3. Use Connection Pooling: For multi-threaded applications
  4. Implement Circuit Breakers: Prevent cascade failures
  5. Monitor Performance: Track latency and error rates
  6. Optimize Payload Size: Send only required fields
  7. Use Compression: Enable gzip compression for large requests

Development

Setup

git clone <repository-url> cd jdpi-client bundle install

Running Tests

# Run all tests bundle exec rake test # Run specific test file bundle exec ruby test/test_config.rb # Run with verbose output bundle exec rake test TESTOPTS="-v" # Run linter bundle exec rubocop # Run everything (tests + linting) bundle exec rake

Code Quality & Coverage

# Auto-fix linting issues bundle exec rubocop -a # Run tests with coverage bundle exec rake test_coverage # Generate coverage report only bundle exec rake coverage # Full CI suite (tests + coverage + linting) bundle exec rake ci # Alternative coverage command COVERAGE=true bundle exec rake test

🚀 CI/CD Pipeline

This project uses GitHub Actions for continuous integration and deployment. Understanding the CI/CD pipeline helps contributors write code that passes all checks.

GitHub Actions Workflow

The CI pipeline is defined in .github/workflows/ci.yml and includes:

  1. Multi-Version Testing:

    • Tests across Ruby 3.0, 3.1, 3.2, 3.3, and 3.4
    • Ensures compatibility across all supported Ruby versions
    • Uses matrix strategy for parallel execution
  2. Code Quality Checks:

    • RuboCop: Linting and style checking
    • Test Coverage: Minimum 70% line coverage required
    • Security: Dependency vulnerability scanning
  3. Test Execution:

    • Runs complete test suite (330+ tests, 1800+ assertions)
    • Uses mocked HTTP responses for reliable testing
    • Includes integration tests for all storage backends
  4. Gem Building:

    • Validates gem can be built successfully
    • Checks gemspec structure and dependencies

CI Commands

Local CI Simulation:

# Run the same checks as CI bundle exec rake ci # Individual steps: bundle exec rubocop # Style checking bundle exec rake test # Run test suite COVERAGE=true bundle exec rake test # With coverage gem build jdpi_client.gemspec # Build gem

Pre-commit Checks:

# Recommended before committing bundle exec rubocop -a # Auto-fix style issues bundle exec rake test # Ensure tests pass git add . && git commit # Git hooks will clean commit messages

CI Environment Variables

The CI environment uses these variables:

Variable Purpose Value in CI
CI Indicates CI environment "true"
COVERAGE Enable coverage reporting "true"
TEST_ADAPTER Default storage adapter for tests "memory"
RUBY_VERSION Matrix variable for Ruby version "3.0", "3.1", etc.

Branch Protection

The master branch is protected with these rules:

  • Required status checks: All CI jobs must pass
  • Up-to-date branches: Must be current with master
  • Linear history: No merge commits allowed (use squash/rebase)

Coverage Reporting

Coverage is automatically calculated and reported:

  • Coverage threshold: 70% minimum line coverage
  • Per-file threshold: 25% minimum per file
  • Branch coverage: Tracked but not enforced
  • HTML reports: Generated in coverage/ directory
  • CI comments: Coverage percentage posted on PRs

Automated Checks

The CI pipeline automatically:

  1. Installs system dependencies (libpq-dev, libsqlite3-dev)
  2. Sets up Ruby with bundler caching
  3. Installs gems with bundle install
  4. Runs RuboCop with zero-tolerance for violations
  5. Executes tests with coverage reporting
  6. Builds the gem and validates structure
  7. Reports results via GitHub status checks

Debugging CI Failures

Common CI failure scenarios:

  1. RuboCop violations:

    # Fix locally bundle exec rubocop -a git add . && git commit --amend --no-edit
  2. Test failures:

    # Run specific test bundle exec ruby test/test_failing_file.rb # Run with verbose output bundle exec rake test TESTOPTS="-v"
  3. Coverage drops:

    # Check coverage locally COVERAGE=true bundle exec rake test open coverage/index.html # View detailed report
  4. Gem build failures:

    # Test gem build gem build jdpi_client.gemspec gem spec jdpi_client-*.gem --ruby

Performance Optimization

CI runs are optimized for speed:

  • Bundler caching: Dependencies cached between runs
  • Parallel execution: Multiple Ruby versions tested simultaneously
  • Minimal system setup: Only installs required dependencies
  • Efficient test structure: Fast unit tests with mocked external calls

Contributing Workflow

For contributors, the recommended workflow is:

  1. Fork and clone the repository
  2. Create a feature branch (git checkout -b feature/your-feature)
  3. Make changes following code style guidelines
  4. Run local CI checks (bundle exec rake ci)
  5. Commit changes (git hooks clean commit messages)
  6. Push and create PR - CI runs automatically
  7. Address CI feedback if any checks fail
  8. Merge after approval and passing CI

Security Scanning

The CI pipeline includes security checks:

  • Bundler audit: Scans for known vulnerabilities in dependencies
  • Code analysis: Static analysis for potential security issues
  • Dependency updates: Dependabot creates PRs for security updates

Running security checks locally:

# Install bundler-audit gem install bundler-audit # Check for vulnerabilities bundle audit check --update # Update vulnerable dependencies bundle update

Release Process

When ready to release (maintainers only):

  1. Update version in jdpi_client.gemspec
  2. Update CHANGELOG.md with release notes
  3. Create release PR with version bump
  4. Merge after CI passes and approval
  5. Tag release (git tag v0.2.0 && git push --tags)
  6. Publish gem (when RubyGems integration is ready)

Monitoring CI Health

CI pipeline health is monitored through:

  • GitHub Actions dashboard: View recent runs and success rates
  • Branch protection status: Ensures quality gates are maintained
  • Coverage trends: Track coverage changes over time
  • Performance metrics: Monitor CI execution time and resource usage

Coverage Features

  • Current coverage: 75.65% (minimum threshold enforced at 70%)
  • Per-file minimum: 25%
  • Branch coverage: 53.07%
  • HTML reports generated in coverage/index.html
  • Branch coverage enabled for detailed analysis
  • Works across all supported Ruby versions (3.0+)
  • High coverage ensures reliability and maintainability

Building the Gem

# Build gem gem build jdpi_client.gemspec # Install locally for testing gem install jdpi_client-*.gem --local # Uninstall gem uninstall jdpi_client

🔧 Troubleshooting

This section covers common issues and their solutions. For more detailed troubleshooting, see the complete troubleshooting guide.

Installation Issues

Problem: "command not found: bundle"

Solution: Install Bundler:

gem install bundler

Problem: "libpq-dev not found" during bundle install

Solution: Install system dependencies:

# Ubuntu/Debian sudo apt-get install -y libpq-dev libsqlite3-dev # macOS brew install postgresql sqlite3

Problem: Ruby version errors

Solution: Ensure Ruby 3.0+ is installed:

ruby --version # Should show 3.0+ rbenv install 3.2.0 # If using rbenv rbenv global 3.2.0

Configuration Issues

Problem: "JDPI_CLIENT_HOST is missing" error

Solution: Set required environment variables:

export JDPI_CLIENT_HOST=api.yourbank.homl.jdpi.pstijd export JDPI_CLIENT_ID=your_client_id export JDPI_CLIENT_SECRET=your_client_secret

Problem: Environment auto-detection not working

Solution: Check hostname patterns:

# Production hostnames should contain "prod" or "production" config.jdpi_client_host = "api.bank.prod.jdpi.pstijd" # → HTTPS # Other hostnames are treated as homolog config.jdpi_client_host = "api.bank.homl.jdpi.pstijd" # → HTTP

Problem: SSL certificate verification errors

Solution:

  1. Check network connectivity
  2. Verify firewall settings
  3. For development/testing only, you can disable SSL verification:
# ⚠️ DEVELOPMENT/TESTING ONLY - NEVER in production! config.verify_ssl = false # If this option exists

Authentication Issues

Problem: "Authentication failed" or 401 errors

Solution: Verify credentials and configuration:

  1. Check credentials:
# Test configuration puts "Host: #{JDPIClient.config.jdpi_client_host}" puts "Client ID: #{JDPIClient.config.oauth_client_id}" puts "Secret set: #{!JDPIClient.config.oauth_secret.nil?}"
  1. Test authentication directly:
auth_client = JDPIClient::Auth::Client.new begin token = auth_client.token! puts "✅ Authentication successful" puts "Token expires: #{auth_client.token_info[:expires_at]}" rescue => e puts "❌ Authentication failed: #{e.message}" end
  1. Check token storage:
# Clear cached tokens to force refresh auth_client.refresh!

Problem: Token refresh failures

Solution:

  • Verify credentials haven't expired
  • Check network connectivity to JDPI
  • Clear token storage and retry
  • Contact JDPI support if issue persists

API Request Issues

Problem: Rate limiting errors (429)

Solution: Implement proper rate limiting:

begin response = spi_client.create_order!(payment_data) rescue JDPIClient::Errors::RateLimited => e sleep(60) # Wait for rate limit reset retry end

Problem: Request validation errors (400)

Solution: Check request format:

# Enable debug logging to see request details JDPIClient.config.logger.level = Logger::DEBUG # Common validation issues: payment_data = { valor: 1050, # Amount in centavos (required) chave: "user@bank.com", # Valid PIX key (required) dt_hr_requisicao_psp: Time.now.utc.iso8601, # ISO8601 format (required) idempotency_key: SecureRandom.uuid # Unique key (recommended) }

Problem: Connection timeouts

Solution: Adjust timeout settings:

JDPIClient.configure do |config| config.timeout = 15 # Increase request timeout config.open_timeout = 5 # Increase connection timeout end

Storage Backend Issues

Problem: Redis connection errors

Solution: Verify Redis configuration:

# Test Redis connectivity redis-cli -u $REDIS_URL ping
# Test Redis from Ruby require 'redis' redis = Redis.new(url: ENV['REDIS_URL']) redis.ping # Should return "PONG"

Problem: Database connection errors

Solution: Verify database setup:

# Test database connectivity require 'active_record' ActiveRecord::Base.establish_connection(ENV['DATABASE_URL']) ActiveRecord::Base.connection.execute("SELECT 1")

Problem: DynamoDB access errors

Solution: Verify AWS configuration:

# Check AWS credentials aws sts get-caller-identity # Test DynamoDB access aws dynamodb list-tables --region us-east-1

Performance Issues

Problem: Slow response times

Solution:

  1. Check network latency to JDPI endpoints
  2. Enable connection pooling for multi-threaded apps
  3. Monitor token caching effectiveness
  4. Verify rate limiting isn't being triggered

Problem: High memory usage

Solution:

  1. Ensure proper connection cleanup
  2. Monitor token storage size
  3. Use connection pooling instead of creating new clients
  4. Review application architecture for memory leaks

Development & Testing Issues

Problem: Tests failing with authentication errors

Solution: Use test stubs and mocks:

# In test_helper.rb or spec_helper.rb require 'webmock/minitest' # or webmock/rspec # Stub OAuth requests stub_request(:post, %r{.*/auth/jdpi/connect/token}) .to_return( status: 200, body: { access_token: "test_token", token_type: "Bearer", expires_in: 3600 }.to_json, headers: { "Content-Type" => "application/json" } )

Problem: Coverage tests failing

Solution: Run tests with coverage enabled:

COVERAGE=true bundle exec rake test

Problem: RuboCop style errors

Solution: Auto-fix style issues:

bundle exec rubocop -a # Auto-fix issues bundle exec rubocop # Check remaining issues

Debugging Tips

Enable detailed logging:

JDPIClient.configure do |config| config.logger = Logger.new($stdout) config.logger.level = Logger::DEBUG end

Inspect configuration:

config = JDPIClient.config puts "Environment: #{config.environment}" puts "Base URL: #{config.base_url}" puts "Timeout: #{config.timeout}s" puts "Token storage: #{config.token_storage_adapter}"

Monitor token usage:

auth_client = JDPIClient::Auth::Client.new info = auth_client.token_info puts "Token cached: #{info[:cached]}" puts "Storage type: #{info[:storage_type]}" puts "Expires at: #{info[:expires_at]}" puts "Time until expiry: #{info[:expires_at] - Time.now} seconds" if info[:expires_at]

Getting Help

If you're still experiencing issues:

  1. Check the logs for detailed error messages
  2. Review the FAQ section for common questions
  3. Search existing issues on GitHub
  4. Create a minimal reproduction case
  5. Open a GitHub issue with:
    • Ruby version
    • Gem version
    • Configuration (sanitized)
    • Full error message and stack trace
    • Steps to reproduce

Contributing

We welcome contributions! Please follow these steps:

  1. Fork the repository

  2. Create a feature branch

    git checkout -b feature/your-feature-name
  3. Make your changes following our coding standards:

    • Use frozen string literals
    • Follow existing code style and patterns
    • Add tests for new functionality
    • Update documentation as needed
  4. Run the test suite

    bundle exec rake test bundle exec rubocop
  5. Commit your changes

    git commit -am "Add feature: your feature description"
  6. Push to your fork

    git push origin feature/your-feature-name
  7. Create a Pull Request

    • Provide a clear description of your changes
    • Reference any related issues
    • Ensure CI tests pass

Code Style

  • Follow the existing code style
  • Use meaningful variable and method names
  • Add comments for complex business logic
  • Keep methods focused and concise
  • Use proper error handling

Testing Guidelines

  • Write tests for all new functionality
  • Use descriptive test names
  • Mock external API calls
  • Test both success and error scenarios
  • Maintain good test coverage

Documentation

  • /docs - Complete JDPI API documentation and PIX rules
  • CLAUDE.md - Claude Code specific configuration and patterns
  • docs/13-Claude-Examples.md - Comprehensive usage examples
  • docs/14-Development-Workflow.md - Development and testing guide
  • docs/15-Troubleshooting.md - Common issues and solutions

Badge Setup

To get dynamic badges that update automatically:

Coverage Badge Setup

  1. CodeCov Integration (recommended):

    # Add to .github/workflows/ci.yml - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: file: ./coverage/coverage.xml

    Badge: [![codecov](https://codecov.io/gh/agramms/jdpi-client/branch/main/graph/badge.svg)](https://codecov.io/gh/agramms/jdpi-client)

  2. GitHub Pages Coverage:

    # Add job to deploy coverage reports - name: Deploy coverage to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./coverage

Automated Badge Updates

For automatically updating badges, consider:

  • GitHub Actions: Auto-update README badges based on test results
  • Shields.io: Dynamic badges from GitHub API endpoints
  • CodeClimate: Code quality and coverage integration
  • RubyGems: Automatic gem version badges

Current Badge Configuration

All badges are configured with current project metrics:

  • CI Status: Links to GitHub Actions workflow results
  • Coverage: Shows current 75.65% test coverage
  • Ruby Support: Indicates Ruby 3.0+ compatibility
  • License: MIT license badge
  • Code Style: RuboCop compliance

❓ Frequently Asked Questions (FAQ)

General Questions

Q: What is JDPI and how does it relate to PIX? A: JDPI (Java Development Platform Integration) is the platform that facilitates PIX payments in Brazil. PIX is the instant payment system created by the Central Bank of Brazil. This gem provides a Ruby client to interact with JDPI services for PIX operations.

Q: Do I need to be a bank to use this gem? A: Not necessarily. You need to be a PIX participant, which includes banks, payment institutions, and other authorized financial entities. You must obtain credentials from the Central Bank of Brazil.

Q: Is this gem production-ready? A: Yes, the gem is designed for production use with comprehensive error handling, token management, rate limiting, and security features. It has 75.65% test coverage and supports Ruby 3.0+.

Installation & Setup

Q: Why do I get "gem not found" when trying to install? A: This gem is currently available from source. Use the Git installation method shown in the Installation section. Publishing to RubyGems is planned for future releases.

Q: What Ruby versions are supported? A: Ruby 3.0 and higher are supported. The gem is tested on Ruby 3.0, 3.1, 3.2, 3.3, and 3.4.

Q: Do I need Rails to use this gem? A: No, this gem has no Rails dependency and works with any Ruby application (Sinatra, plain Ruby scripts, etc.).

Configuration

Q: How do I know if my configuration is correct? A: Use the verification script from the Quick Setup Checklist section. It will test your configuration and authentication.

Q: What's the difference between production and homolog environments? A: The gem auto-detects environments based on hostname:

  • Hostnames containing "prod" or "production" use HTTPS and production settings
  • All other hostnames use HTTP and are treated as homolog/testing environments

Q: Should I enable token encryption? A: Yes, for production environments. Token encryption protects sensitive OAuth tokens when stored in Redis, databases, or other storage backends.

Authentication & Tokens

Q: How often do tokens expire? A: OAuth tokens typically expire after 1 hour. The gem automatically handles token refresh, so you don't need to manage this manually.

Q: Can I share tokens between application instances? A: Yes, use Redis, Database, or DynamoDB token storage adapters for sharing tokens across multiple application instances or servers.

Q: Why am I getting authentication errors? A: Common causes:

  1. Invalid client credentials
  2. Incorrect hostname/environment
  3. Network connectivity issues
  4. Expired credentials (contact JDPI support)

PIX Operations

Q: What's the difference between SPI OP and SPI OD? A: - SPI OP: Outbound payments (initiating PIX payments)

  • SPI OD: Inbound operations (refunds, disputes, queries)

Q: How do I handle PIX key validation? A: Use the DICT services to validate PIX keys before creating payments:

dict_client = JDPIClient::DICT::Keys.new key_info = dict_client.consult_key("user@bank.com")

Q: What is idempotency and why is it important? A: Idempotency ensures that duplicate requests don't create multiple payments. Always provide a unique idempotency_key for payment operations:

response = spi_client.create_order!( # ... payment data ... idempotency_key: SecureRandom.uuid )

Error Handling

Q: How do I handle rate limiting? A: The gem provides JDPIClient::Errors::RateLimited exceptions. Implement exponential backoff and monitor rate limit headers (see Performance section).

Q: What should I do when payments fail? A: Always implement proper error handling for different scenarios:

  • Validation errors (400): Fix the request data
  • Authentication errors (401): Check credentials/tokens
  • Rate limiting (429): Implement backoff/retry logic
  • Server errors (5xx): Log and retry with exponential backoff

Q: How do I debug API calls? A: Enable debug logging:

config.logger = Logger.new($stdout) config.logger.level = Logger::DEBUG

Performance & Scaling

Q: How many requests per second can I make? A: JDPI has rate limits (typically 100 requests/minute for PIX payments). See the Performance & Rate Limiting section for details and optimization strategies.

Q: Can this gem handle high-volume applications? A: Yes, with proper configuration:

  • Use connection pooling for multi-threaded applications
  • Implement distributed rate limiting with Redis
  • Use appropriate token storage backends
  • Monitor performance metrics

Q: Should I use connection pooling? A: For multi-threaded applications or high-volume scenarios, yes. Use the connection_pool gem as shown in the Performance section.

Security

Q: How should I store credentials securely? A: - Use environment variables, never hard-code credentials

  • Enable token encryption in production
  • Use dedicated secret management services
  • Rotate credentials regularly

Q: What should I log and what shouldn't I log? A: Log: Request metadata, response status, timing, error messages Never log: OAuth tokens, client secrets, sensitive payment data, PIX keys

Q: Is it safe to use in production? A: Yes, when following security best practices:

  • Use HTTPS in production
  • Enable token encryption
  • Implement proper error handling
  • Monitor for security events
  • Follow the Security Considerations section

Troubleshooting

Q: Tests are failing with authentication errors A: Ensure your test environment uses mock/stub HTTP requests. The gem includes comprehensive test helpers for this purpose.

Q: I'm getting SSL certificate errors A: This usually indicates:

  1. Network connectivity issues
  2. Firewall blocking HTTPS traffic
  3. Invalid/expired certificates (rare with JDPI)

Q: Token storage isn't working A: Verify:

  1. Storage backend is properly configured and accessible
  2. Required gems are installed (redis, pg, aws-sdk-dynamodb)
  3. Network connectivity to storage services
  4. Proper credentials/permissions

Q: Performance is slower than expected A: Check:

  1. Network latency to JDPI endpoints
  2. Token caching is working (should see cached token reuse)
  3. Connection pooling configuration
  4. Rate limiting isn't being triggered
  5. Database/Redis performance if using those storage backends

Migration & Updates

Q: How do I upgrade to a new version? A: Update the gem reference in your Gemfile and run bundle update jdpi_client. Check the CHANGELOG for breaking changes.

Q: Can I migrate from another PIX client? A: Yes, but you'll need to:

  1. Update configuration format
  2. Adjust API call patterns
  3. Update error handling
  4. Test thoroughly in homolog environment

Q: How do I contribute to this project? A: See the Contributing section for guidelines on submitting issues, feature requests, and pull requests.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

For issues and questions:

About

Ruby Client for access JDPI

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages