Documentation Index Fetch the complete documentation index at: https://docs.chargeworx.com/llms.txt
Use this file to discover all available pages before exploring further.
The Chargeworx platform consists of multiple components that interact through well-defined interfaces. This document describes the communication patterns and integration points between components.
Communication patterns
API to domain layer
The API layer delegates business logic to the domain layer through managers:
// API Controller
[ HttpPost ]
public async Task < IActionResult > CreateTransaction ([ FromBody ] TransactionRequest request )
{
var result = await _transactionManager . CreateTransactionAsync ( request );
return Ok ( result );
}
// Domain Manager
public async Task < TransactionResponse > CreateTransactionAsync ( TransactionRequest request )
{
// Validate request
// Call payment processor
// Store transaction
// Return response
}
Key characteristics :
Controllers are thin, delegating to managers
Managers orchestrate business logic
DTOs used for request/response
Async/await for all I/O operations
Domain to data layer
The domain layer accesses data through stores (repository pattern):
// Domain Manager
public async Task < Transaction > GetTransactionAsync ( Guid id )
{
return await _transactionStore . GetByIdAsync ( id );
}
// Data Store
public async Task < Transaction > GetByIdAsync ( Guid id )
{
return await _context . Transactions
. Include ( t => t . TransactionCreditCardEvents )
. FirstOrDefaultAsync ( t => t . Id == id );
}
Key characteristics :
Stores encapsulate data access logic
Entity Framework Core for ORM
Stored procedures for complex queries
Transactions for data consistency
Admin UI to API
The Admin UI communicates with the API through typed HTTP clients:
// Admin Controller
public async Task < IActionResult > GetTransactions ()
{
var transactions = await _transactionClient . GetTransactionsAsync ();
return View ( transactions );
}
// HTTP Client
public async Task < List < Transaction >> GetTransactionsAsync ()
{
var response = await _httpClient . GetAsync ( "/api/adm/transactions" );
return await response . Content . ReadAsAsync < List < Transaction >>();
}
Key characteristics :
Typed clients for type safety
OAuth2 authentication
Retry policies for resilience
Response caching where appropriate
Payment processor integration
The domain layer integrates with payment processors through service interfaces:
// Domain Manager
public async Task < AuthorizationResponse > AuthorizeAsync ( AuthorizationRequest request )
{
var processor = _processorFactory . GetProcessor ( request . ProcessorType );
return await processor . AuthorizeAsync ( request );
}
// Payment Processor Service
public async Task < AuthorizationResponse > AuthorizeAsync ( AuthorizationRequest request )
{
// Build processor-specific request
// Call processor API
// Parse response
// Return standardized response
}
Key characteristics :
Factory pattern for processor selection
Standardized request/response models
Error handling and retry logic
Logging of all interactions
Component interaction flows
Transaction processing flow
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │────▶│ API │────▶│ Domain │────▶│ Data │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │
│ ▼
│ ┌──────────┐
│ │ Processor│
│ └──────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Response │◀────│ Response │
└──────────┘ └──────────┘
Step-by-step :
Client sends transaction request to API
API validates request and delegates to domain manager
Domain manager creates transaction record in database
Domain manager calls payment processor service
Processor service sends request to external processor
Processor returns response
Domain manager updates transaction with result
API returns response to client
Account updater flow
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│Background│────▶│ Domain │────▶│ Data │────▶│ S3 │
│ Process │ └──────────┘ └──────────┘ └──────────┘
└──────────┘ │ │
│ ▼ │
│ ┌──────────┐ │
│ │CyberSource│◀───────────────────────────┘
│ └──────────┘
│ │
│ ▼
│ ┌──────────┐
└─────────▶│ Data │
└──────────┘
Step-by-step :
Background process triggers account updater
Domain service queries cards to update from database
Domain service generates CSV file
File uploaded to S3
Domain service uploads file to CyberSource
CyberSource processes batch (24-48 hours)
Background process downloads results
Domain service parses results and updates database
Chargeback processing flow
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│Background│────▶│ Domain │────▶│CyberSource│────▶│ Data │
│ Process │ └──────────┘ └──────────┘ └──────────┘
└──────────┘ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Email │◀───────────────────────────┘
│ └──────────┘
│ │
│ ▼
│ ┌──────────┐
└─────────▶│ Admin │
└──────────┘
Step-by-step :
Background process triggers chargeback download
Domain service downloads chargeback report from CyberSource
Domain service parses CSV report
Domain service matches chargebacks to transactions
Domain service creates reversal transactions
Domain service sends email notifications
Admin UI displays chargeback alerts
Inter-service communication
HTTP clients
The platform uses typed HTTP clients for service-to-service communication:
Admin clients (Chargeworx.Api.AdminClients)
Company management
Project management
User management
Transaction management
Report generation
Payment clients (Chargeworx.Api.PaymentClients)
Transaction processing
Payment info management
Charge event processing
Configuration :
services . AddHttpClient < ITransactionClient , TransactionClient >( client =>
{
client . BaseAddress = new Uri ( configuration [ "ApiBaseUrl" ]);
client . DefaultRequestHeaders . Add ( "Accept" , "application/json" );
})
. AddPolicyHandler ( GetRetryPolicy ())
. AddPolicyHandler ( GetCircuitBreakerPolicy ());
SignalR for real-time updates
The Admin UI uses SignalR for real-time notifications:
// Server-side hub
public class UserMonitorHub : Hub
{
public async Task SendNotification ( string userId , string message )
{
await Clients . User ( userId ). SendAsync ( "ReceiveNotification" , message );
}
}
// Client-side connection
const connection = new signalR . HubConnectionBuilder ()
. withUrl ( "/hubs/userMonitor" )
. build ();
connection . on ( "ReceiveNotification" , ( message ) => {
displayNotification ( message );
});
Use cases :
Background process status updates
Transaction alerts
System notifications
User activity monitoring
Background processing
Background processes coordinate through a queue-based system:
// Create background process
var process = new BackgroundProcess
{
Type = BackgroundProcessType . AccountUpdater ,
Status = BackgroundProcessStatus . Pending ,
Priority = 1 ,
Payload = JsonConvert . SerializeObject ( payload )
};
await _backgroundProcessStore . CreateAsync ( process );
// Process execution
var process = await _backgroundProcessStore . GetNextToProcessAsync ();
if ( process != null )
{
await ExecuteProcessAsync ( process );
}
Process types :
Account updater batch processing
Chargeback report downloads
Transaction report synchronization
Data import processing
Database interactions
Multi-database coordination
The platform uses four separate databases with coordinated transactions:
// Transaction spanning multiple databases
using var mainTransaction = await _mainContext . Database . BeginTransactionAsync ();
using var keyTransaction = await _keyContext . Database . BeginTransactionAsync ();
try
{
// Create company in main database
await _mainContext . Companies . AddAsync ( company );
await _mainContext . SaveChangesAsync ();
// Create API key in key database
await _keyContext . Keys . AddAsync ( key );
await _keyContext . SaveChangesAsync ();
await mainTransaction . CommitAsync ();
await keyTransaction . CommitAsync ();
}
catch
{
await mainTransaction . RollbackAsync ();
await keyTransaction . RollbackAsync ();
throw ;
}
Stored procedures
Complex queries use stored procedures for performance:
// Call stored procedure
var transactions = await _context . Transactions
. FromSqlRaw ( "EXEC [dbo].[TransactionGetEntries] @CompanyProjectId, @StartDate, @EndDate" ,
new SqlParameter ( "@CompanyProjectId" , projectId ),
new SqlParameter ( "@StartDate" , startDate ),
new SqlParameter ( "@EndDate" , endDate ))
. ToListAsync ();
Bulk operations
Import operations use bulk inserts for performance:
// Bulk insert transactions
using var bulkCopy = new SqlBulkCopy ( connectionString );
bulkCopy . DestinationTableName = "Transactions" ;
bulkCopy . BatchSize = 1000 ;
var dataTable = ConvertToDataTable ( transactions );
await bulkCopy . WriteToServerAsync ( dataTable );
External service integration
CyberSource integration
SOAP API (legacy transactions):
var client = new TransactionProcessorClient ();
var request = BuildSoapRequest ( transaction );
var response = await client . runTransactionAsync ( request );
REST API (modern features):
var client = new HttpClient ();
client . DefaultRequestHeaders . Add ( "v-c-merchant-id" , merchantId );
client . DefaultRequestHeaders . Add ( "Signature" , GenerateSignature ( request ));
var response = await client . PostAsync ( url , content );
PayPal Payflow integration
var request = new Dictionary < string , string >
{
[ "TRXTYPE" ] = "A" ,
[ "TENDER" ] = "C" ,
[ "AMT" ] = amount . ToString ( "F2" ),
[ "ACCT" ] = cardNumber
};
var response = await _httpClient . PostAsync ( payflowUrl ,
new FormUrlEncodedContent ( request ));
AWS services
S3 for file storage :
var s3Client = new AmazonS3Client ();
await s3Client . PutObjectAsync ( new PutObjectRequest
{
BucketName = bucketName ,
Key = fileName ,
InputStream = fileStream
});
SES for email :
var sesClient = new AmazonSimpleEmailServiceClient ();
await sesClient . SendEmailAsync ( new SendEmailRequest
{
Source = fromAddress ,
Destination = new Destination { ToAddresses = toAddresses },
Message = new Message
{
Subject = new Content ( subject ),
Body = new Body { Html = new Content ( htmlBody ) }
}
});
Error handling and resilience
Retry policies
HTTP clients use Polly for retry logic:
static IAsyncPolicy < HttpResponseMessage > GetRetryPolicy ()
{
return HttpPolicyExtensions
. HandleTransientHttpError ()
. WaitAndRetryAsync ( 3 , retryAttempt =>
TimeSpan . FromSeconds ( Math . Pow ( 2 , retryAttempt )));
}
Circuit breaker
Prevent cascading failures with circuit breaker:
static IAsyncPolicy < HttpResponseMessage > GetCircuitBreakerPolicy ()
{
return HttpPolicyExtensions
. HandleTransientHttpError ()
. CircuitBreakerAsync ( 5 , TimeSpan . FromSeconds ( 30 ));
}
Exception handling
Centralized exception handling in middleware:
app . UseExceptionHandler ( errorApp =>
{
errorApp . Run ( async context =>
{
var error = context . Features . Get < IExceptionHandlerFeature >();
var exception = error ? . Error ;
// Log exception
_logger . LogError ( exception , "Unhandled exception" );
// Return error response
context . Response . StatusCode = 500 ;
await context . Response . WriteAsJsonAsync ( new ErrorDetails
{
Message = "An error occurred processing your request" ,
CorrelationId = context . TraceIdentifier
});
});
});
Caching strategy
Response caching
API endpoints use response caching:
[ HttpGet ]
[ ResponseCache ( Duration = 300 , VaryByQueryKeys = new [] { "id" })]
public async Task < IActionResult > GetCompany ( Guid id )
{
var company = await _companyManager . GetByIdAsync ( id );
return Ok ( company );
}
Distributed caching
Session state and frequently accessed data use distributed cache:
// Store in cache
await _cache . SetStringAsync ( key , JsonConvert . SerializeObject ( value ),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan . FromMinutes ( 30 )
});
// Retrieve from cache
var cached = await _cache . GetStringAsync ( key );
if ( cached != null )
{
return JsonConvert . DeserializeObject < T >( cached );
}
Security considerations
Authentication flow
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │────▶│ API │────▶│Identity │
└──────────┘ └──────────┘ │ Server │
│ │ └──────────┘
│ │ │
│ ◀─────────────────┘
│ │
◀────────────────┘
OAuth2 flow :
Client requests token from IdentityServer
IdentityServer validates credentials
IdentityServer returns access token
Client includes token in API requests
API validates token and processes request
Data encryption
Sensitive data is encrypted at rest:
// Encrypt credit card number
var encrypted = _aesHelper . Encrypt ( cardNumber , encryptionKey );
// Decrypt credit card number
var decrypted = _aesHelper . Decrypt ( encrypted , encryptionKey );
Next steps
Data flow Payment processing flow
Integration patterns Common integration scenarios
System overview High-level architecture
Security Authentication and authorization