Payment transaction flow
Authorization request flow
Copy
Ask AI
┌─────────┐
│ 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:Copy
Ask AI
// 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>
Copy
Ask AI
// 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:Copy
Ask AI
┌─────────┐
│ 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:Copy
Ask AI
┌─────────┐
│ 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
Copy
Ask AI
┌─────────────────┐
│ 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
Copy
Ask AI
┌─────────────────┐
│ 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
Copy
Ask AI
┌─────────────────┐
│ 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
Copy
Ask AI
┌─────────┐
│ 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
Copy
Ask AI
┌─────────────────┐
│ 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
Copy
Ask AI
┌─────────┐
│ 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:Copy
Ask AI
// 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:Copy
Ask AI
// 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:Copy
Ask AI
// 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);
});
