Skip to main content
This document describes how data flows through the Chargeworx platform during key operations, with a focus on payment processing, account updates, and reporting.

Payment transaction flow

Authorization request flow

┌─────────┐
│ Client  │
└────┬────┘
     │ 1. POST /api/pay/{projectId}/transactions

┌─────────────────────────────────────────────────┐
│ API Layer (TransactionPaymentController)        │
│ - Validate request                              │
│ - Extract project context                       │
│ - Delegate to domain                            │
└────┬────────────────────────────────────────────┘
     │ 2. CreateTransactionAsync()

┌─────────────────────────────────────────────────┐
│ Domain Layer (TransactionManager)               │
│ - Validate merchant reference                   │
│ - Get processor configuration                   │
│ - Create transaction record                     │
└────┬────────────────────────────────────────────┘
     │ 3. SaveChangesAsync()

┌─────────────────────────────────────────────────┐
│ Data Layer (TransactionStore)                   │
│ - Insert Transaction                            │
│ - Insert TransactionCreditCard                  │
│ - Return transaction ID                         │
└────┬────────────────────────────────────────────┘
     │ 4. Transaction created

┌─────────────────────────────────────────────────┐
│ Domain Layer (CybersourceService)               │
│ - Build SOAP request                            │
│ - Add authentication                            │
│ - Call processor                                │
└────┬────────────────────────────────────────────┘
     │ 5. SOAP request

┌─────────────────────────────────────────────────┐
│ External (CyberSource)                          │
│ - Validate card                                 │
│ - Check fraud rules                             │
│ - Authorize with issuer                         │
│ - Return response                               │
└────┬────────────────────────────────────────────┘
     │ 6. SOAP response

┌─────────────────────────────────────────────────┐
│ Domain Layer (CybersourceService)               │
│ - Parse response                                │
│ - Extract result codes                          │
│ - Return standardized response                  │
└────┬────────────────────────────────────────────┘
     │ 7. Update transaction

┌─────────────────────────────────────────────────┐
│ Data Layer (TransactionStore)                   │
│ - Update Transaction status                     │
│ - Insert TransactionCreditCardEvent             │
│ - Insert RawCybResponse                         │
└────┬────────────────────────────────────────────┘
     │ 8. Transaction updated

┌─────────────────────────────────────────────────┐
│ Domain Layer (ReportTransactionStore)           │
│ - Insert ReportTransaction (async)              │
│ - Insert TransactionTagValues                   │
└────┬────────────────────────────────────────────┘
     │ 9. Return response

┌─────────┐
│ Client  │
└─────────┘

Key data transformations

Request transformation:
// API request
{
    "merchantReference": "ORDER-12345",
    "amount": 100.00,
    "currency": "USD",
    "cardNumber": "4111111111111111",
    "expirationMonth": "12",
    "expirationYear": "2025"
}

// Domain model
var transaction = new Transaction
{
    Id = Guid.NewGuid(),
    CompanyProjectId = projectId,
    MerchantReference = "ORDER-12345",
    Amount = 100.00m,
    Currency = "USD",
    TransactionType = TransactionType.Authorization,
    Status = TransactionStatus.Pending
};

// Processor request (CyberSource SOAP)
<ccAuthService run="true">
    <merchantReferenceCode>ORDER-12345</merchantReferenceCode>
    <purchaseTotals>
        <currency>USD</currency>
        <grandTotalAmount>100.00</grandTotalAmount>
    </purchaseTotals>
    <card>
        <accountNumber>4111111111111111</accountNumber>
        <expirationMonth>12</expirationMonth>
        <expirationYear>2025</expirationYear>
    </card>
</ccAuthService>
Response transformation:
// Processor response (CyberSource SOAP)
<ccAuthReply>
    <reasonCode>100</reasonCode>
    <amount>100.00</amount>
    <authorizationCode>123456</authorizationCode>
    <requestID>6789012345</requestID>
</ccAuthReply>

// Domain model update
transaction.Status = TransactionStatus.Approved;
transaction.ProcessorTransactionId = "6789012345";
transaction.AuthorizationCode = "123456";

// API response
{
    "transactionId": "550e8400-e29b-41d4-a716-446655440000",
    "status": "Approved",
    "authorizationCode": "123456",
    "processorTransactionId": "6789012345",
    "resultCode": "100"
}

Capture flow

Capture settles a previously authorized transaction:
┌─────────┐
│ Client  │
└────┬────┘
     │ 1. POST /api/adm/transactions/{id}/capture

┌─────────────────────────────────────────────────┐
│ API Layer (TransactionAdminController)          │
└────┬────────────────────────────────────────────┘
     │ 2. CaptureTransactionAsync()

┌─────────────────────────────────────────────────┐
│ Domain Layer (TransactionManager)               │
│ - Load original transaction                     │
│ - Validate can capture                          │
│ - Create capture transaction                    │
└────┬────────────────────────────────────────────┘
     │ 3. Call processor

┌─────────────────────────────────────────────────┐
│ External (CyberSource)                          │
│ - Reference original auth                       │
│ - Settle funds                                  │
└────┬────────────────────────────────────────────┘
     │ 4. Update transactions

┌─────────────────────────────────────────────────┐
│ Data Layer                                      │
│ - Update original transaction (Captured)        │
│ - Insert capture transaction                    │
│ - Link transactions                             │
└─────────────────────────────────────────────────┘

Refund flow

Refund returns funds to the customer:
┌─────────┐
│ Client  │
└────┬────┘
     │ 1. POST /api/adm/transactions/{id}/refund

┌─────────────────────────────────────────────────┐
│ API Layer (TransactionAdminController)          │
└────┬────────────────────────────────────────────┘
     │ 2. RefundTransactionAsync()

┌─────────────────────────────────────────────────┐
│ Domain Layer (TransactionManager)               │
│ - Load original transaction                     │
│ - Validate can refund                           │
│ - Create refund transaction                     │
└────┬────────────────────────────────────────────┘
     │ 3. Call processor

┌─────────────────────────────────────────────────┐
│ External (CyberSource)                          │
│ - Reference original transaction                │
│ - Process refund                                │
└────┬────────────────────────────────────────────┘
     │ 4. Update transactions

┌─────────────────────────────────────────────────┐
│ Data Layer                                      │
│ - Insert refund transaction                     │
│ - Update original transaction (Refunded)        │
│ - Update report database                        │
└─────────────────────────────────────────────────┘

Account updater flow

Batch creation and submission

┌─────────────────┐
│ Background      │
│ Process         │
└────┬────────────┘
     │ 1. Trigger account updater

┌─────────────────────────────────────────────────┐
│ Domain Layer (AccountUpdaterService)            │
│ - Query cards due for update                    │
│ - Create batch record                           │
└────┬────────────────────────────────────────────┘
     │ 2. SaveChangesAsync()

┌─────────────────────────────────────────────────┐
│ Data Layer (CreditCardUpdaterStore)             │
│ - Insert CreditCardBatch                        │
│ - Link payment infos to batch                   │
│ - Set status to Pending                         │
└────┬────────────────────────────────────────────┘
     │ 3. Generate CSV file

┌─────────────────────────────────────────────────┐
│ Domain Layer (AccountUpdaterService)            │
│ - Query payment infos in batch                  │
│ - Generate CSV with card data                   │
│ - Encrypt sensitive data                        │
└────┬────────────────────────────────────────────┘
     │ 4. Upload to S3

┌─────────────────────────────────────────────────┐
│ External (AWS S3)                               │
│ - Store CSV file                                │
│ - Return file URL                               │
└────┬────────────────────────────────────────────┘
     │ 5. Upload to CyberSource

┌─────────────────────────────────────────────────┐
│ External (CyberSource Secure File Transfer)     │
│ - Receive batch file                            │
│ - Queue for processing                          │
│ - Return batch ID                               │
└────┬────────────────────────────────────────────┘
     │ 6. Update batch status

┌─────────────────────────────────────────────────┐
│ Data Layer (CreditCardUpdaterStore)             │
│ - Update batch status to Submitted              │
│ - Store batch ID                                │
│ - Set submission date                           │
└─────────────────────────────────────────────────┘

Result processing

┌─────────────────┐
│ Background      │
│ Process         │
└────┬────────────┘
     │ 1. Check for completed batches

┌─────────────────────────────────────────────────┐
│ Domain Layer (AccountUpdaterService)            │
│ - Query submitted batches                       │
│ - Check CyberSource for results                 │
└────┬────────────────────────────────────────────┘
     │ 2. Download results

┌─────────────────────────────────────────────────┐
│ External (CyberSource Secure File Transfer)     │
│ - Return results CSV                            │
└────┬────────────────────────────────────────────┘
     │ 3. Parse results

┌─────────────────────────────────────────────────┐
│ Domain Layer (AccountUpdaterService)            │
│ - Parse CSV file                                │
│ - Match to payment infos                        │
│ - Identify updates                              │
└────┬────────────────────────────────────────────┘
     │ 4. Update payment infos

┌─────────────────────────────────────────────────┐
│ Data Layer (Multiple Stores)                    │
│ - Create new payment info (if updated)          │
│ - Update CreditCardUpdaterStatus                │
│ - Link old to new payment info                  │
│ - Insert AccountUpdaterHistory                  │
└────┬────────────────────────────────────────────┘
     │ 5. Update batch status

┌─────────────────────────────────────────────────┐
│ Data Layer (CreditCardUpdaterStore)             │
│ - Update batch status to Processed              │
│ - Set processing date                           │
└─────────────────────────────────────────────────┘

Chargeback processing flow

┌─────────────────┐
│ Background      │
│ Process         │
└────┬────────────┘
     │ 1. Trigger chargeback download

┌─────────────────────────────────────────────────┐
│ Domain Layer (CybersourceReportsService)        │
│ - Get report subscription                       │
│ - Request chargeback report                     │
└────┬────────────────────────────────────────────┘
     │ 2. Download report

┌─────────────────────────────────────────────────┐
│ External (CyberSource Reports API)              │
│ - Return chargeback CSV                         │
└────┬────────────────────────────────────────────┘
     │ 3. Parse report

┌─────────────────────────────────────────────────┐
│ Domain Layer (CybersourceReportsService)        │
│ - Parse CSV rows                                │
│ - Extract chargeback details                    │
└────┬────────────────────────────────────────────┘
     │ 4. Match to transactions

┌─────────────────────────────────────────────────┐
│ Data Layer (TransactionStore)                   │
│ - Query by merchant reference                   │
│ - Query by processor transaction ID             │
└────┬────────────────────────────────────────────┘
     │ 5. Create chargeback records

┌─────────────────────────────────────────────────┐
│ Data Layer (ChargebackDetailStore)              │
│ - Insert ChargebackDetail                       │
│ - Link to original transaction                  │
│ - Set IsTransactionCreated = false              │
└────┬────────────────────────────────────────────┘
     │ 6. Create reversal transactions

┌─────────────────────────────────────────────────┐
│ Domain Layer (TransactionManager)               │
│ - Create reversal transaction                   │
│ - Set negative amount                           │
│ - Link to chargeback                            │
└────┬────────────────────────────────────────────┘
     │ 7. Update chargeback

┌─────────────────────────────────────────────────┐
│ Data Layer (ChargebackDetailStore)              │
│ - Update IsTransactionCreated = true            │
│ - Link to reversal transaction                  │
└────┬────────────────────────────────────────────┘
     │ 8. Send notifications

┌─────────────────────────────────────────────────┐
│ Domain Layer (EmailSender)                      │
│ - Get company admin emails                      │
│ - Send chargeback notification                  │
└─────────────────────────────────────────────────┘

Data import flow

Transaction import

┌─────────┐
│ Client  │
└────┬────┘
     │ 1. POST /api/adm/import (CSV file)

┌─────────────────────────────────────────────────┐
│ API Layer (ImportBatchController)               │
│ - Validate file format                          │
│ - Create import batch record                    │
└────┬────────────────────────────────────────────┘
     │ 2. Upload to S3

┌─────────────────────────────────────────────────┐
│ External (AWS S3)                               │
│ - Store CSV file                                │
│ - Return file URL                               │
└────┬────────────────────────────────────────────┘
     │ 3. Create background process

┌─────────────────────────────────────────────────┐
│ Data Layer (BackgroundProcessStore)             │
│ - Insert BackgroundProcess                      │
│ - Set type to ImportBatch                       │
│ - Store import batch ID in payload              │
└────┬────────────────────────────────────────────┘
     │ 4. Process import (async)

┌─────────────────────────────────────────────────┐
│ Background Process Worker                       │
│ - Get next pending process                      │
│ - Execute import                                │
└────┬────────────────────────────────────────────┘
     │ 5. Download and parse file

┌─────────────────────────────────────────────────┐
│ Domain Layer (ImportBatchManager)               │
│ - Download CSV from S3                          │
│ - Parse rows                                    │
│ - Validate data                                 │
└────┬────────────────────────────────────────────┘
     │ 6. Bulk insert transactions

┌─────────────────────────────────────────────────┐
│ Data Layer (ImportBatchStore)                   │
│ - Call ImportTransactionCreateEntries SP        │
│ - Bulk insert transactions                      │
│ - Insert transaction events                     │
│ - Return success/error counts                   │
└────┬────────────────────────────────────────────┘
     │ 7. Update import batch

┌─────────────────────────────────────────────────┐
│ Data Layer (ImportBatchStore)                   │
│ - Update status to Completed                    │
│ - Store success/error counts                    │
│ - Store error messages                          │
└────┬────────────────────────────────────────────┘
     │ 8. Notify completion

┌─────────────────────────────────────────────────┐
│ Domain Layer (SignalR)                          │
│ - Send notification to user                     │
│ - Include import results                        │
└─────────────────────────────────────────────────┘

Reporting data flow

Report transaction synchronization

┌─────────────────┐
│ Background      │
│ Process         │
└────┬────────────┘
     │ 1. Trigger sync

┌─────────────────────────────────────────────────┐
│ Domain Layer (ReportTransactionManager)         │
│ - Query unsynchronized transactions             │
│ - Batch transactions (1000 at a time)           │
└────┬────────────────────────────────────────────┘
     │ 2. Read from main database

┌─────────────────────────────────────────────────┐
│ Data Layer (TransactionStore)                   │
│ - Query transactions with IsImported = false    │
│ - Include related data (events, tags)           │
└────┬────────────────────────────────────────────┘
     │ 3. Transform data

┌─────────────────────────────────────────────────┐
│ Domain Layer (ReportTransactionManager)         │
│ - Flatten transaction data                      │
│ - Extract tag values                            │
│ - Calculate derived fields                      │
└────┬────────────────────────────────────────────┘
     │ 4. Write to report database

┌─────────────────────────────────────────────────┐
│ Data Layer (ReportTransactionStore)             │
│ - Call ReportTransactionCreateEntries SP        │
│ - Bulk insert ReportTransaction                 │
│ - Bulk insert TransactionTagValues              │
└────┬────────────────────────────────────────────┘
     │ 5. Mark as synchronized

┌─────────────────────────────────────────────────┐
│ Data Layer (TransactionStore)                   │
│ - Update IsImported = true                      │
│ - Set SyncDate                                  │
└─────────────────────────────────────────────────┘

Data encryption flow

Credit card encryption

┌─────────┐
│ Client  │
└────┬────┘
     │ 1. Submit card number (plain text)

┌─────────────────────────────────────────────────┐
│ API Layer                                       │
│ - Receive card number                           │
│ - Validate format                               │
└────┬────────────────────────────────────────────┘
     │ 2. Encrypt card number

┌─────────────────────────────────────────────────┐
│ Domain Layer (AesEncryptionHelper)              │
│ - Get encryption key from key database          │
│ - Encrypt card number with AES-256             │
│ - Generate hash for lookup                      │
└────┬────────────────────────────────────────────┘
     │ 3. Store encrypted data

┌─────────────────────────────────────────────────┐
│ Data Layer (CreditCardStore)                    │
│ - Insert CreditCard (encrypted)                 │
│ - Insert CreditCardNumber (hash)                │
│ - Store in credit card database                 │
└────┬────────────────────────────────────────────┘
     │ 4. Use for transaction

┌─────────────────────────────────────────────────┐
│ Domain Layer (CybersourceService)               │
│ - Decrypt card number                           │
│ - Use for processor call                        │
│ - Never log plain text                          │
└─────────────────────────────────────────────────┘

Database synchronization

Cross-database transactions

When operations span multiple databases, the platform uses coordinated transactions:
// Example: Creating a company with API key
using var mainTx = await _mainContext.Database.BeginTransactionAsync();
using var keyTx = await _keyContext.Database.BeginTransactionAsync();

try
{
    // 1. Create company in main database
    var company = new Company { Name = "Acme Corp" };
    await _mainContext.Companies.AddAsync(company);
    await _mainContext.SaveChangesAsync();
    
    // 2. Create API key in key database
    var key = new Key 
    { 
        CompanyId = company.Id,
        Value = GenerateKey() 
    };
    await _keyContext.Keys.AddAsync(key);
    await _keyContext.SaveChangesAsync();
    
    // 3. Commit both transactions
    await mainTx.CommitAsync();
    await keyTx.CommitAsync();
}
catch
{
    // Rollback both on error
    await mainTx.RollbackAsync();
    await keyTx.RollbackAsync();
    throw;
}

Performance optimizations

Batch processing

Large operations use batching to avoid memory issues:
// Process 1000 transactions at a time
var batchSize = 1000;
var offset = 0;

while (true)
{
    var batch = await _transactionStore
        .GetUnsynchronizedAsync(batchSize, offset);
    
    if (!batch.Any())
        break;
    
    await ProcessBatchAsync(batch);
    offset += batchSize;
}

Caching strategy

Frequently accessed data is cached:
// Cache processor configuration
var cacheKey = $"processor:{projectId}";
var processor = await _cache.GetOrCreateAsync(cacheKey, async entry =>
{
    entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
    return await _processorStore.GetActiveAsync(projectId);
});

Next steps